CosmicMind / Material

A UI/UX framework for creating beautiful applications.
http://cosmicmind.com
MIT License
11.99k stars 1.26k forks source link

want to change TabBar line width #1109

Closed takehilo closed 6 years ago

takehilo commented 6 years ago

I want to change the width of TabBar line. I tried the following in the subclass of TabsController but it didn't work. How can I do this?

override func layoutSubviews() {
    super.layoutSubviews()

    var frame = tabBar.line.frame
    frame.size.width = frame.size.width - 100
    tabBar.line.frame = frame
}
OrkhanAlikhanov commented 6 years ago

Hey! I don't know why you want to change tabbar line width, but that does not work because the line width changed via animation: https://github.com/CosmicMind/Material/blob/6b989a48aeb1317411396b8ea970a15a45e29e9b/Sources/iOS/TabBar.swift#L602-L603

When you tap a tabItem or swipe the controller, above code is called. Motion works with CoreAnimation which animates view from old values to the new values on presentationLayer and sets the new values back to the actual layer/view when animation is done. So changing tabBar.line.width won't work because what's actually shown on the screen during animation is presentationlayer and after animation is done values of presentationLayer will be passed to the actual layer/view.

OrkhanAlikhanov commented 6 years ago

Actually there is hackish way to achieve what you want. Since the frame of the line is calculated from tabItem itself, we can tweak tabItem properties before animation is fired and set it back before layout cycle happens:

class MyTabsController: TabsController, TabBarDelegate {
  override func prepare() {
    super.prepare()
    tabBar.delegate = self
  }

  func tabBar(tabBar: TabBar, willSelect tabItem: TabItem) {
    let desiredWidth: CGFloat = 100
    let w = tabItem.frame.width
    let x = tabItem.center.x

    tabItem.frame.size.width = desiredWidth
    tabItem.frame.origin.x = x - desiredWidth / 2

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
      tabItem.frame.size.width = w
      tabItem.frame.origin.x = x - w / 2
    }
  }

@danieldahan What do yo think maybe we should add something like:

func tabBar(tabBar: TabBar, lineWidthFor tabItem: TabItem) -> CGFloat
daniel-jonathan commented 6 years ago

I think for the purposes of customization, we can offer an enum that is by default set to auto for line transition changes, and then add a custom enum that accepts a value for the desired width. @OrkhanAlikhanov what do you think?

OrkhanAlikhanov commented 6 years ago

Well, that sounds reasonable. Can tabItems have different widths? If so, then lines can also have different widths based on tabItem's width. So, we need another case accepting closure for dynamic calculation.

enum TabBarLineWidthStyle {
  case auto
  case fixed(CGFloat)
  case custom((TabBarItem) -> CGFloat)
}

Usage:

tabBar.tabBarLineWidthStyle = .auto
tabBar.tabBarLineWidthStyle = .fixed(100)
tabBar.tabBarLineWidthStyle = .custom { tabItem in
    return tabItem.bounds.width - 20
  }

What do you think?

daniel-jonathan commented 6 years ago

I think the 3 options would solve all cases nicely. Maybe we can rename the enum to TabBarItemsStyle. Not so sure I like the word Width in there. In either case, the naming could be changed on final review when we PR the branch into dev.

daniel-jonathan commented 6 years ago

Please find the solution for this in Material 2.16.2. Thank you!