Commit 57b9f836 by Daniel Dahan

added initial ChipBar

parent d75cb0e8
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
961154CC1F32A7B100A78D74 /* ChipBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961154CB1F32A7B100A78D74 /* ChipBar.swift */; };
961409B11E43D15C00E7BA99 /* FABMenu.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96A183621E0C6CE200083C30 /* FABMenu.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 961409B11E43D15C00E7BA99 /* FABMenu.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96A183621E0C6CE200083C30 /* FABMenu.swift */; settings = {ATTRIBUTES = (Public, ); }; };
961409B21E43D15C00E7BA99 /* FABMenuController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96A183641E0C6DD400083C30 /* FABMenuController.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 961409B21E43D15C00E7BA99 /* FABMenuController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96A183641E0C6DD400083C30 /* FABMenuController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9617B07D1DFCA8CF00410F8F /* Application.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961E6BDE1DDA2A95004E6C93 /* Application.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 9617B07D1DFCA8CF00410F8F /* Application.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961E6BDE1DDA2A95004E6C93 /* Application.swift */; settings = {ATTRIBUTES = (Public, ); }; };
...@@ -206,6 +207,7 @@ ...@@ -206,6 +207,7 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
961154CB1F32A7B100A78D74 /* ChipBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChipBar.swift; sourceTree = "<group>"; };
961276621DCD8B1800A7D920 /* CharacterAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacterAttribute.swift; sourceTree = "<group>"; }; 961276621DCD8B1800A7D920 /* CharacterAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacterAttribute.swift; sourceTree = "<group>"; };
961E6BDE1DDA2A95004E6C93 /* Application.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; }; 961E6BDE1DDA2A95004E6C93 /* Application.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
961E6BE11DDA2AF3004E6C93 /* Screen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = "<group>"; }; 961E6BE11DDA2AF3004E6C93 /* Screen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = "<group>"; };
...@@ -354,6 +356,14 @@ ...@@ -354,6 +356,14 @@
name = Text; name = Text;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
961154CA1F32999000A78D74 /* Chip */ = {
isa = PBXGroup;
children = (
961154CB1F32A7B100A78D74 /* ChipBar.swift */,
);
name = Chip;
sourceTree = "<group>";
};
961E6BDD1DDA2A7E004E6C93 /* Application */ = { 961E6BDD1DDA2A7E004E6C93 /* Application */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
...@@ -617,6 +627,7 @@ ...@@ -617,6 +627,7 @@
962DDD081D6FBBD0001C307C /* BottomTabBar */, 962DDD081D6FBBD0001C307C /* BottomTabBar */,
96BCB8031CB40F4B00C806FE /* Button */, 96BCB8031CB40F4B00C806FE /* Button */,
96BCB8021CB40F3B00C806FE /* Card */, 96BCB8021CB40F3B00C806FE /* Card */,
961154CA1F32999000A78D74 /* Chip */,
96BCB8051CB40F9C00C806FE /* Collection */, 96BCB8051CB40F9C00C806FE /* Collection */,
96BCB8001CB40F0300C806FE /* Color */, 96BCB8001CB40F0300C806FE /* Color */,
96328B9A1E05C135009A4C90 /* Data */, 96328B9A1E05C135009A4C90 /* Data */,
...@@ -1058,6 +1069,7 @@ ...@@ -1058,6 +1069,7 @@
96A1808B1F2E2B6E006BD69D /* Motion+UIView.swift in Sources */, 96A1808B1F2E2B6E006BD69D /* Motion+UIView.swift in Sources */,
96A180971F2E2B6E006BD69D /* MotionCoordinateSpace.swift in Sources */, 96A180971F2E2B6E006BD69D /* MotionCoordinateSpace.swift in Sources */,
96E09DC81F2287E50000B121 /* TabsController.swift in Sources */, 96E09DC81F2287E50000B121 /* TabsController.swift in Sources */,
961154CC1F32A7B100A78D74 /* ChipBar.swift in Sources */,
965689611F002F4C001C656D /* CardCollectionViewController.swift in Sources */, 965689611F002F4C001C656D /* CardCollectionViewController.swift in Sources */,
96A180951F2E2B6E006BD69D /* MotionContext.swift in Sources */, 96A180951F2E2B6E006BD69D /* MotionContext.swift in Sources */,
965E80CC1DD4C50600D61E4B /* Bar.swift in Sources */, 965E80CC1DD4C50600D61E4B /* Bar.swift in Sources */,
......
/*
* Copyright (C) 2015 - 2017, Daniel Dahan and CosmicMind, Inc. <http://cosmicmind.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of CosmicMind nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import UIKit
open class ChipItem: FlatButton {}
@objc(ChipBarDelegate)
public protocol ChipBarDelegate {
/**
A delegation method that is executed when the chipItem will trigger the
animation to the next chip.
- Parameter chipBar: A ChipBar.
- Parameter chipItem: A ChipItem.
*/
@objc
optional func chipBar(chipBar: ChipBar, willSelect chipItem: ChipItem)
/**
A delegation method that is executed when the chipItem did complete the
animation to the next chip.
- Parameter chipBar: A ChipBar.
- Parameter chipItem: A ChipItem.
*/
@objc
optional func chipBar(chipBar: ChipBar, didSelect chipItem: ChipItem)
}
@objc(ChipBarStyle)
public enum ChipBarStyle: Int {
case auto
case nonScrollable
case scrollable
}
open class ChipBar: Bar {
/// A boolean indicating if the ChipBar line is in an animation state.
open fileprivate(set) var isAnimating = false
/// The total width of the chipItems.
fileprivate var chipItemsTotalWidth: CGFloat {
var w: CGFloat = 0
for v in chipItems {
w += v.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: contentView.height)).width + interimSpace
}
return w
}
/// An enum that determines the chip bar style.
open var chipBarStyle = ChipBarStyle.auto {
didSet {
layoutSubviews()
}
}
/// A reference to the scroll view when the chip bar style is scrollable.
open let scrollView = UIScrollView()
/// Enables and disables bouncing when swiping.
open var isScrollBounceEnabled: Bool {
get {
return scrollView.bounces
}
set(value) {
scrollView.bounces = value
}
}
/// A delegation reference.
open weak var delegate: ChipBarDelegate?
/// The currently selected chipItem.
open fileprivate(set) var selected: ChipItem?
/// Buttons.
open var chipItems = [ChipItem]() {
didSet {
for b in oldValue {
b.removeFromSuperview()
}
prepareChipItems()
layoutSubviews()
}
}
open override func layoutSubviews() {
super.layoutSubviews()
guard willLayout else {
return
}
var lc = 0
var rc = 0
grid.begin()
grid.views.removeAll()
for v in leftViews {
if let b = v as? ChipItem {
b.contentEdgeInsets = .zero
b.titleEdgeInsets = .zero
}
v.width = v.intrinsicContentSize.width
v.sizeToFit()
v.grid.columns = Int(ceil(v.width / gridFactor)) + 2
lc += v.grid.columns
grid.views.append(v)
}
grid.views.append(contentView)
for v in rightViews {
if let b = v as? ChipItem {
b.contentEdgeInsets = .zero
b.titleEdgeInsets = .zero
}
v.width = v.intrinsicContentSize.width
v.sizeToFit()
v.grid.columns = Int(ceil(v.width / gridFactor)) + 2
rc += v.grid.columns
grid.views.append(v)
}
contentView.grid.begin()
contentView.grid.offset.columns = 0
var l: CGFloat = 0
var r: CGFloat = 0
if .center == contentViewAlignment {
if leftViews.count < rightViews.count {
r = CGFloat(rightViews.count) * interimSpace
l = r
} else {
l = CGFloat(leftViews.count) * interimSpace
r = l
}
}
let p = width - l - r - contentEdgeInsets.left - contentEdgeInsets.right
let columns = Int(ceil(p / gridFactor))
if .center == contentViewAlignment {
if lc < rc {
contentView.grid.columns = columns - 2 * rc
contentView.grid.offset.columns = rc - lc
} else {
contentView.grid.columns = columns - 2 * lc
rightViews.first?.grid.offset.columns = lc - rc
}
} else {
contentView.grid.columns = columns - lc - rc
}
grid.axis.columns = columns
if .scrollable == chipBarStyle || (.auto == chipBarStyle && chipItemsTotalWidth > bounds.width) {
var w: CGFloat = 0
for v in chipItems {
let x = v.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: contentView.height)).width + interimSpace
scrollView.addSubview(v)
v.height = scrollView.height
v.width = x
v.x = w
w += x
}
scrollView.contentSize = CGSize(width: w, height: height)
} else {
scrollView.grid.views = chipItems
scrollView.grid.axis.columns = chipItems.count
scrollView.contentSize = CGSize(width: scrollView.width, height: height)
}
grid.commit()
contentView.grid.commit()
layoutDivider()
}
open override func prepare() {
super.prepare()
contentEdgeInsetsPreset = .none
interimSpacePreset = .interimSpace6
prepareContentView()
prepareScrollView()
prepareDivider()
}
}
fileprivate extension ChipBar {
/// Prepares the divider.
func prepareDivider() {
dividerColor = Color.grey.lighten3
}
/// Prepares the chipItems.
func prepareChipItems() {
for v in chipItems {
v.grid.columns = 0
v.cornerRadius = 0
v.contentEdgeInsets = .zero
}
}
/// Prepares the contentView.
func prepareContentView() {
contentView.zPosition = 6000
}
/// Prepares the scroll view.
func prepareScrollView() {
scrollView.isPagingEnabled = false
scrollView.showsVerticalScrollIndicator = false
scrollView.showsHorizontalScrollIndicator = false
centerViews = [scrollView]
}
}
extension ChipBar {
/**
Selects a given index from the chipItems array.
- Parameter at index: An Int.
- Paramater completion: An optional completion block.
*/
open func select(at index: Int, completion: ((ChipItem) -> Void)? = nil) {
guard -1 < index, index < chipItems.count else {
return
}
animate(to: chipItems[index], isTriggeredByUserInteraction: false, completion: completion)
}
/**
Animates to a given chipItem.
- Parameter to chipItem: A ChipItem.
- Parameter completion: An optional completion block.
*/
open func animate(to chipItem: ChipItem, completion: ((ChipItem) -> Void)? = nil) {
animate(to: chipItem, isTriggeredByUserInteraction: false, completion: completion)
}
/**
Animates to a given chipItem.
- Parameter to chipItem: A ChipItem.
- Parameter isTriggeredByUserInteraction: A boolean indicating whether the
state was changed by a user interaction, true if yes, false otherwise.
- Parameter completion: An optional completion block.
*/
fileprivate func animate(to chipItem: ChipItem, isTriggeredByUserInteraction: Bool, completion: ((ChipItem) -> Void)? = nil) {
if isTriggeredByUserInteraction {
delegate?.chipBar?(chipBar: self, willSelect: chipItem)
}
selected = chipItem
isAnimating = true
if !scrollView.bounds.contains(chipItem.frame) {
let contentOffsetX = (chipItem.x < scrollView.bounds.minX) ? chipItem.x : chipItem.frame.maxX - scrollView.bounds.width
let normalizedOffsetX = min(max(contentOffsetX, 0), scrollView.contentSize.width - scrollView.bounds.width)
scrollView.setContentOffset(CGPoint(x: normalizedOffsetX, y: 0), animated: true)
}
}
}
...@@ -30,6 +30,13 @@ ...@@ -30,6 +30,13 @@
import UIKit import UIKit
open class TabItem: FlatButton {
open override func prepare() {
super.prepare()
pulseAnimation = .none
}
}
@objc(TabBarLineAlignment) @objc(TabBarLineAlignment)
public enum TabBarLineAlignment: Int { public enum TabBarLineAlignment: Int {
case top case top
...@@ -39,22 +46,22 @@ public enum TabBarLineAlignment: Int { ...@@ -39,22 +46,22 @@ public enum TabBarLineAlignment: Int {
@objc(TabBarDelegate) @objc(TabBarDelegate)
public protocol TabBarDelegate { public protocol TabBarDelegate {
/** /**
A delegation method that is executed when the button will trigger the A delegation method that is executed when the tabItem will trigger the
animation to the next tab. animation to the next tab.
- Parameter tabBar: A TabBar. - Parameter tabBar: A TabBar.
- Parameter button: A UIButton. - Parameter tabItem: A TabItem.
*/ */
@objc @objc
optional func tabBar(tabBar: TabBar, willSelect button: UIButton) optional func tabBar(tabBar: TabBar, willSelect tabItem: TabItem)
/** /**
A delegation method that is executed when the button did complete the A delegation method that is executed when the tabItem did complete the
animation to the next tab. animation to the next tab.
- Parameter tabBar: A TabBar. - Parameter tabBar: A TabBar.
- Parameter button: A UIButton. - Parameter tabItem: A TabItem.
*/ */
@objc @objc
optional func tabBar(tabBar: TabBar, didSelect button: UIButton) optional func tabBar(tabBar: TabBar, didSelect tabItem: TabItem)
} }
@objc(TabBarStyle) @objc(TabBarStyle)
...@@ -68,27 +75,17 @@ open class TabBar: Bar { ...@@ -68,27 +75,17 @@ open class TabBar: Bar {
/// A boolean indicating if the TabBar line is in an animation state. /// A boolean indicating if the TabBar line is in an animation state.
open fileprivate(set) var isAnimating = false open fileprivate(set) var isAnimating = false
/// The total width of the buttons. /// The total width of the tabItems.
fileprivate var buttonsTotalWidth: CGFloat { fileprivate var tabItemsTotalWidth: CGFloat {
var w: CGFloat = 0 var w: CGFloat = 0
for v in buttons { for v in tabItems {
w += v.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: contentView.height)).width + interimSpace w += v.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: contentView.height)).width + interimSpace
} }
return w return w
} }
/// Enables and disables bouncing when swiping.
open var isBounceEnabled: Bool {
get {
return scrollView.bounces
}
set(value) {
scrollView.bounces = value
}
}
/// An enum that determines the tab bar style. /// An enum that determines the tab bar style.
open var tabBarStyle = TabBarStyle.auto { open var tabBarStyle = TabBarStyle.auto {
didSet { didSet {
...@@ -99,69 +96,30 @@ open class TabBar: Bar { ...@@ -99,69 +96,30 @@ open class TabBar: Bar {
/// A reference to the scroll view when the tab bar style is scrollable. /// A reference to the scroll view when the tab bar style is scrollable.
open let scrollView = UIScrollView() open let scrollView = UIScrollView()
/// Does the scroll view bounce. /// Enables and disables bouncing when swiping.
open var isScrollBounceEnabled = true { open var isScrollBounceEnabled: Bool {
didSet {
scrollView.bounces = true
}
}
/// A delegation reference.
open weak var delegate: TabBarDelegate?
/// The currently selected button.
open fileprivate(set) var selected: UIButton?
/// A preset wrapper around contentEdgeInsets.
open override var contentEdgeInsetsPreset: EdgeInsetsPreset {
get {
return contentView.grid.contentEdgeInsetsPreset
}
set(value) {
contentView.grid.contentEdgeInsetsPreset = value
}
}
/// A reference to EdgeInsets.
@IBInspectable
open override var contentEdgeInsets: EdgeInsets {
get { get {
return contentView.grid.contentEdgeInsets return scrollView.bounces
} }
set(value) { set(value) {
contentView.grid.contentEdgeInsets = value scrollView.bounces = value
} }
} }
/// A preset wrapper around interimSpace. /// A delegation reference.
open override var interimSpacePreset: InterimSpacePreset { open weak var delegate: TabBarDelegate?
get {
return contentView.grid.interimSpacePreset
}
set(value) {
contentView.grid.interimSpacePreset = value
}
}
/// A wrapper around contentView.grid.interimSpace. /// The currently selected tabItem.
@IBInspectable open fileprivate(set) var selected: TabItem?
open override var interimSpace: InterimSpace {
get {
return contentView.grid.interimSpace
}
set(value) {
contentView.grid.interimSpace = value
}
}
/// Buttons. /// TabItems.
open var buttons = [UIButton]() { open var tabItems = [TabItem]() {
didSet { didSet {
for b in oldValue { for b in oldValue {
b.removeFromSuperview() b.removeFromSuperview()
} }
prepareButtons() prepareTabItems()
layoutSubviews() layoutSubviews()
} }
} }
...@@ -170,11 +128,11 @@ open class TabBar: Bar { ...@@ -170,11 +128,11 @@ open class TabBar: Bar {
@IBInspectable @IBInspectable
open var isLineAnimated = true { open var isLineAnimated = true {
didSet { didSet {
for b in buttons { for b in tabItems {
if isLineAnimated { if isLineAnimated {
prepareLineAnimationHandler(button: b) prepareLineAnimationHandler(tabItem: b)
} else { } else {
removeLineAnimationHandler(button: b) removeLineAnimationHandler(tabItem: b)
} }
} }
} }
...@@ -223,7 +181,7 @@ open class TabBar: Bar { ...@@ -223,7 +181,7 @@ open class TabBar: Bar {
grid.views.removeAll() grid.views.removeAll()
for v in leftViews { for v in leftViews {
if let b = v as? UIButton { if let b = v as? TabItem {
b.contentEdgeInsets = .zero b.contentEdgeInsets = .zero
b.titleEdgeInsets = .zero b.titleEdgeInsets = .zero
} }
...@@ -240,7 +198,7 @@ open class TabBar: Bar { ...@@ -240,7 +198,7 @@ open class TabBar: Bar {
grid.views.append(contentView) grid.views.append(contentView)
for v in rightViews { for v in rightViews {
if let b = v as? UIButton { if let b = v as? TabItem {
b.contentEdgeInsets = .zero b.contentEdgeInsets = .zero
b.titleEdgeInsets = .zero b.titleEdgeInsets = .zero
} }
...@@ -287,9 +245,9 @@ open class TabBar: Bar { ...@@ -287,9 +245,9 @@ open class TabBar: Bar {
grid.axis.columns = columns grid.axis.columns = columns
if .scrollable == tabBarStyle || (.auto == tabBarStyle && buttonsTotalWidth > bounds.width) { if .scrollable == tabBarStyle || (.auto == tabBarStyle && tabItemsTotalWidth > bounds.width) {
var w: CGFloat = 0 var w: CGFloat = 0
for v in buttons { for v in tabItems {
let x = v.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: contentView.height)).width + interimSpace let x = v.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: contentView.height)).width + interimSpace
scrollView.addSubview(v) scrollView.addSubview(v)
v.height = scrollView.height v.height = scrollView.height
...@@ -300,8 +258,8 @@ open class TabBar: Bar { ...@@ -300,8 +258,8 @@ open class TabBar: Bar {
scrollView.contentSize = CGSize(width: w, height: height) scrollView.contentSize = CGSize(width: w, height: height)
} else { } else {
scrollView.grid.views = buttons scrollView.grid.views = tabItems
scrollView.grid.axis.columns = buttons.count scrollView.grid.axis.columns = tabItems.count
scrollView.contentSize = CGSize(width: scrollView.width, height: height) scrollView.contentSize = CGSize(width: scrollView.width, height: height)
} }
...@@ -338,26 +296,26 @@ fileprivate extension TabBar { ...@@ -338,26 +296,26 @@ fileprivate extension TabBar {
dividerAlignment = .top dividerAlignment = .top
} }
/// Prepares the buttons. /// Prepares the tabItems.
func prepareButtons() { func prepareTabItems() {
for v in buttons { for v in tabItems {
v.grid.columns = 0 v.grid.columns = 0
v.cornerRadius = 0 v.cornerRadius = 0
v.contentEdgeInsets = .zero v.contentEdgeInsets = .zero
if isLineAnimated { if isLineAnimated {
prepareLineAnimationHandler(button: v) prepareLineAnimationHandler(tabItem: v)
} }
} }
} }
/** /**
Prepares the line animation handlers. Prepares the line animation handlers.
- Parameter button: A UIButton. - Parameter tabItem: A TabItem.
*/ */
func prepareLineAnimationHandler(button: UIButton) { func prepareLineAnimationHandler(tabItem: TabItem) {
removeLineAnimationHandler(button: button) removeLineAnimationHandler(tabItem: tabItem)
button.addTarget(self, action: #selector(handleLineAnimation(button:)), for: .touchUpInside) tabItem.addTarget(self, action: #selector(handleLineAnimation(tabItem:)), for: .touchUpInside)
} }
/// Prepares the contentView. /// Prepares the contentView.
...@@ -378,12 +336,12 @@ fileprivate extension TabBar { ...@@ -378,12 +336,12 @@ fileprivate extension TabBar {
fileprivate extension TabBar { fileprivate extension TabBar {
/// Layout the line view. /// Layout the line view.
func layoutLine() { func layoutLine() {
guard 0 < buttons.count else { guard 0 < tabItems.count else {
return return
} }
if nil == selected { if nil == selected {
selected = buttons.first selected = tabItems.first
} }
line.animate(.duration(0), line.animate(.duration(0),
...@@ -395,62 +353,62 @@ fileprivate extension TabBar { ...@@ -395,62 +353,62 @@ fileprivate extension TabBar {
extension TabBar { extension TabBar {
/** /**
Removes the line animation handlers. Removes the line animation handlers.
- Parameter button: A UIButton. - Parameter tabItem: A TabItem.
*/ */
fileprivate func removeLineAnimationHandler(button: UIButton) { fileprivate func removeLineAnimationHandler(tabItem: TabItem) {
button.removeTarget(self, action: #selector(handleLineAnimation(button:)), for: .touchUpInside) tabItem.removeTarget(self, action: #selector(handleLineAnimation(tabItem:)), for: .touchUpInside)
} }
} }
extension TabBar { extension TabBar {
/// Handles the button touch event. /// Handles the tabItem touch event.
@objc @objc
fileprivate func handleLineAnimation(button: UIButton) { fileprivate func handleLineAnimation(tabItem: TabItem) {
animate(to: button, isTriggeredByUserInteraction: true) animate(to: tabItem, isTriggeredByUserInteraction: true)
} }
} }
extension TabBar { extension TabBar {
/** /**
Selects a given index from the buttons array. Selects a given index from the tabItems array.
- Parameter at index: An Int. - Parameter at index: An Int.
- Paramater completion: An optional completion block. - Paramater completion: An optional completion block.
*/ */
open func select(at index: Int, completion: ((UIButton) -> Void)? = nil) { open func select(at index: Int, completion: ((TabItem) -> Void)? = nil) {
guard -1 < index, index < buttons.count else { guard -1 < index, index < tabItems.count else {
return return
} }
animate(to: buttons[index], isTriggeredByUserInteraction: false, completion: completion) animate(to: tabItems[index], isTriggeredByUserInteraction: false, completion: completion)
} }
/** /**
Animates to a given button. Animates to a given tabItem.
- Parameter to button: A UIButton. - Parameter to tabItem: A TabItem.
- Parameter completion: An optional completion block. - Parameter completion: An optional completion block.
*/ */
open func animate(to button: UIButton, completion: ((UIButton) -> Void)? = nil) { open func animate(to tabItem: TabItem, completion: ((TabItem) -> Void)? = nil) {
animate(to: button, isTriggeredByUserInteraction: false, completion: completion) animate(to: tabItem, isTriggeredByUserInteraction: false, completion: completion)
} }
/** /**
Animates to a given button. Animates to a given tabItem.
- Parameter to button: A UIButton. - Parameter to tabItem: A TabItem.
- Parameter isTriggeredByUserInteraction: A boolean indicating whether the - Parameter isTriggeredByUserInteraction: A boolean indicating whether the
state was changed by a user interaction, true if yes, false otherwise. state was changed by a user interaction, true if yes, false otherwise.
- Parameter completion: An optional completion block. - Parameter completion: An optional completion block.
*/ */
fileprivate func animate(to button: UIButton, isTriggeredByUserInteraction: Bool, completion: ((UIButton) -> Void)? = nil) { fileprivate func animate(to tabItem: TabItem, isTriggeredByUserInteraction: Bool, completion: ((TabItem) -> Void)? = nil) {
if isTriggeredByUserInteraction { if isTriggeredByUserInteraction {
delegate?.tabBar?(tabBar: self, willSelect: button) delegate?.tabBar?(tabBar: self, willSelect: tabItem)
} }
selected = button selected = tabItem
isAnimating = true isAnimating = true
line.animate(.duration(0.25), line.animate(.duration(0.25),
.size(CGSize(width: button.width, height: lineHeight)), .size(CGSize(width: tabItem.width, height: lineHeight)),
.position(CGPoint(x: button.center.x, y: .bottom == lineAlignment ? height - lineHeight / 2 : lineHeight / 2)), .position(CGPoint(x: tabItem.center.x, y: .bottom == lineAlignment ? height - lineHeight / 2 : lineHeight / 2)),
.completion { [weak self, isTriggeredByUserInteraction = isTriggeredByUserInteraction, button = button, completion = completion] _ in .completion { [weak self, isTriggeredByUserInteraction = isTriggeredByUserInteraction, tabItem = tabItem, completion = completion] _ in
guard let s = self else { guard let s = self else {
return return
} }
...@@ -458,14 +416,14 @@ extension TabBar { ...@@ -458,14 +416,14 @@ extension TabBar {
s.isAnimating = false s.isAnimating = false
if isTriggeredByUserInteraction { if isTriggeredByUserInteraction {
s.delegate?.tabBar?(tabBar: s, didSelect: button) s.delegate?.tabBar?(tabBar: s, didSelect: tabItem)
} }
completion?(button) completion?(tabItem)
}) })
if !scrollView.bounds.contains(button.frame) { if !scrollView.bounds.contains(tabItem.frame) {
let contentOffsetX = (button.x < scrollView.bounds.minX) ? button.x : button.frame.maxX - scrollView.bounds.width let contentOffsetX = (tabItem.x < scrollView.bounds.minX) ? tabItem.x : tabItem.frame.maxX - scrollView.bounds.width
let normalizedOffsetX = min(max(contentOffsetX, 0), scrollView.contentSize.width - scrollView.bounds.width) let normalizedOffsetX = min(max(contentOffsetX, 0), scrollView.contentSize.width - scrollView.bounds.width)
scrollView.setContentOffset(CGPoint(x: normalizedOffsetX, y: 0), animated: true) scrollView.setContentOffset(CGPoint(x: normalizedOffsetX, y: 0), animated: true)
} }
......
...@@ -32,13 +32,6 @@ import UIKit ...@@ -32,13 +32,6 @@ import UIKit
fileprivate var TabItemKey: UInt8 = 0 fileprivate var TabItemKey: UInt8 = 0
open class TabItem: FlatButton {
open override func prepare() {
super.prepare()
pulseAnimation = .none
}
}
@objc(TabBarAlignment) @objc(TabBarAlignment)
public enum TabBarAlignment: Int { public enum TabBarAlignment: Int {
case top case top
...@@ -166,7 +159,7 @@ open class TabsController: UIViewController { ...@@ -166,7 +159,7 @@ open class TabsController: UIViewController {
view.contentScaleFactor = Screen.scale view.contentScaleFactor = Screen.scale
prepareContainer() prepareContainer()
prepareTabBar() prepareTabBar()
prepareTabBarButtons() prepareTabBarItems()
prepareViewControllers() prepareViewControllers()
} }
} }
...@@ -183,18 +176,18 @@ fileprivate extension TabsController { ...@@ -183,18 +176,18 @@ fileprivate extension TabsController {
view.addSubview(tabBar) view.addSubview(tabBar)
} }
/// Prepares the tabBar buttons. /// Prepares the `tabBar.tabItems`.
func prepareTabBarButtons() { func prepareTabBarItems() {
var buttons = [UIButton]() var tabItems = [TabItem]()
for v in viewControllers { for v in viewControllers {
let b = v.tabItem let b = v.tabItem
b.removeTarget(self, action: #selector(handleTabBarButton(button:)), for: .touchUpInside) b.removeTarget(self, action: #selector(handle(tabItem:)), for: .touchUpInside)
b.addTarget(self, action: #selector(handleTabBarButton(button:)), for: .touchUpInside) b.addTarget(self, action: #selector(handle(tabItem:)), for: .touchUpInside)
buttons.append(b) tabItems.append(b)
} }
tabBar.buttons = buttons tabBar.tabItems = tabItems
} }
/// Prepares all the view controllers. /// Prepares all the view controllers.
...@@ -307,11 +300,11 @@ fileprivate extension TabsController { ...@@ -307,11 +300,11 @@ fileprivate extension TabsController {
fileprivate extension TabsController { fileprivate extension TabsController {
/** /**
Handles the tabItem. Handles the tabItem.
- Parameter button: A UIButton. - Parameter tabItem: A TabItem.
*/ */
@objc @objc
func handleTabBarButton(button: UIButton) { func handle(tabItem: TabItem) {
guard let i = tabBar.buttons.index(of: button) else { guard let i = tabBar.tabItems.index(of: tabItem) else {
return return
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment