LoginSignup
3
3

More than 5 years have passed since last update.

[MDC]TabBarのindicatorの高さを変更する

Posted at

Material Design Components for iOSMDCTabBarを使っていたのですが、タブ下のインジケータがデフォルトで2ptというとても細い線しか描画できず困っていました。
が、40.1.0より自由に変更できるようになっていました。

使い方
let tabBar = MDCTabBar()
tabBar.items = ....
tabBar.selectionIndicatorTemplate = MDCTabBarUnderlineIndicatorTemplate()

アンダーラインはMDCTabBarUnderlineIndicatorTemplateとしてデフォルトで用意してありますが、高さが2.0fと固定されているため、高さを変更する場合は独自で用意する必要があります。

TabIndicator.swift
import Foundation
import MaterialComponents

class TabIndicator: NSObject, MDCTabBarIndicatorTemplate {
    /// タブインジケーター高さ
    private let underlineHeight: CGFloat = 4.0
    func indicatorAttributes(for context: MDCTabBarIndicatorContext) -> MDCTabBarIndicatorAttributes {
        let bounds = context.bounds
        let attributes = MDCTabBarIndicatorAttributes()
        let underlineFrame = CGRect(x: bounds.minX,
                                    y: bounds.maxY - underlineHeight,
                                    width: bounds.width,
                                    height: underlineHeight)
        attributes.path = UIBezierPath(rect: underlineFrame)
        return attributes
    }
}

MDCTabBarIndicatorTemplateを継承し、indicatorAttributesを実装してその中で自由にインジケータを描画してあげればOKです。

ただし、注意点としてtabのitemsをsetした後に設定しないと反映されません。
(上の例だと、boundsがzeroになってしまいます)
そのため、私は下記のようにカスタムクラスを用意しています。

TabBar.swift
import MaterialComponents

class TabBar: MDCTabBar {
    override var items: [UITabBarItem] {
        get {
            return super.items
        }
        set {
            super.items = newValue
            /// itemsがセットされた後でないと反映されません
            selectionIndicatorTemplate = TabIndicator()
        }
    }
}

また、underlineだけではなく、色々なインジケータを表現できます。
下記はサンプルから拝借しました。

SampleIndicatorTemplate.swift
class IndicatorTemplate: NSObject, MDCTabBarIndicatorTemplate {
    func indicatorAttributes(for context: MDCTabBarIndicatorContext) -> MDCTabBarIndicatorAttributes {
      let attributes = MDCTabBarIndicatorAttributes()
      // Outset frame, round corners, and stroke.
      let indicatorFrame = context.contentFrame.insetBy(dx: -8, dy: -4)
      let path = UIBezierPath(roundedRect: indicatorFrame, cornerRadius: 4)
      attributes.path = path.stroked(withWidth: 2)
      return attributes
    }
}

extension UIBezierPath {
  /// Returns a copy of the path, stroked with the given line width.
  func stroked(withWidth width: CGFloat) -> UIBezierPath {
    let strokedPath = cgPath.copy(
      strokingWithWidth: width,
      lineCap: .butt,
      lineJoin: .miter,
      miterLimit: 0)
    return UIBezierPath(cgPath: strokedPath)
  }
}

上記例だと下記のようになります。
sample.png

アンダーラインの高さを変えたいだけなのに手間がかかってしまいますが、自由度は高いので色々面白いこともできそうです。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3