[MDC]TabBar의 인디케이터 높이 변경

Material Design Components for iOSMDCTabBar 을 사용하고 있었습니다만, 탭 아래의 인디케이터가 디폴트로 2pt라고 하는 매우 가는 선 밖에 draw 할 수 없어 곤란하고 있었습니다.
하지만, 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)
  }
}

위의 예라면 아래와 같이 됩니다.


언더라인의 높이를 바꾸고 싶을 뿐인데 수고가 걸려 버립니다만, 자유도는 높기 때문에 여러가지 재미있을 수도 있을 것 같습니다.

좋은 웹페이지 즐겨찾기