Material Design Components for iOS のMDCTabBarを使っていたのですが、タブ下のインジケータがデフォルトで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)
}
}
アンダーラインの高さを変えたいだけなのに手間がかかってしまいますが、自由度は高いので色々面白いこともできそうです。