initial
This commit is contained in:
438
Pods/ESTabBarController-swift/Sources/ESTabBar.swift
generated
Normal file
438
Pods/ESTabBarController-swift/Sources/ESTabBar.swift
generated
Normal file
@@ -0,0 +1,438 @@
|
||||
//
|
||||
// ESTabBar.swift
|
||||
//
|
||||
// Created by Vincent Li on 2017/2/8.
|
||||
// Copyright (c) 2013-2018 ESTabBarController (https://github.com/eggswift/ESTabBarController)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
|
||||
/// 对原生的UITabBarItemPositioning进行扩展,通过UITabBarItemPositioning设置时,系统会自动添加insets,这使得添加背景样式的需求变得不可能实现。ESTabBarItemPositioning完全支持原有的item Position 类型,除此之外还支持完全fill模式。
|
||||
///
|
||||
/// - automatic: UITabBarItemPositioning.automatic
|
||||
/// - fill: UITabBarItemPositioning.fill
|
||||
/// - centered: UITabBarItemPositioning.centered
|
||||
/// - fillExcludeSeparator: 完全fill模式,布局不覆盖tabBar顶部分割线
|
||||
/// - fillIncludeSeparator: 完全fill模式,布局覆盖tabBar顶部分割线
|
||||
public enum ESTabBarItemPositioning : Int {
|
||||
|
||||
case automatic
|
||||
|
||||
case fill
|
||||
|
||||
case centered
|
||||
|
||||
case fillExcludeSeparator
|
||||
|
||||
case fillIncludeSeparator
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// 对UITabBarDelegate进行扩展,以支持UITabBarControllerDelegate的相关方法桥接
|
||||
internal protocol ESTabBarDelegate: NSObjectProtocol {
|
||||
|
||||
/// 当前item是否支持选中
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - tabBar: tabBar
|
||||
/// - item: 当前item
|
||||
/// - Returns: Bool
|
||||
func tabBar(_ tabBar: UITabBar, shouldSelect item: UITabBarItem) -> Bool
|
||||
|
||||
/// 当前item是否需要被劫持
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - tabBar: tabBar
|
||||
/// - item: 当前item
|
||||
/// - Returns: Bool
|
||||
func tabBar(_ tabBar: UITabBar, shouldHijack item: UITabBarItem) -> Bool
|
||||
|
||||
/// 当前item的点击被劫持
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - tabBar: tabBar
|
||||
/// - item: 当前item
|
||||
/// - Returns: Void
|
||||
func tabBar(_ tabBar: UITabBar, didHijack item: UITabBarItem)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// ESTabBar是高度自定义的UITabBar子类,通过添加UIControl的方式实现自定义tabBarItem的效果。目前支持tabBar的大部分属性的设置,例如delegate,items,selectedImge,itemPositioning,itemWidth,itemSpacing等,以后会更加细致的优化tabBar原有属性的设置效果。
|
||||
open class ESTabBar: UITabBar {
|
||||
|
||||
internal weak var customDelegate: ESTabBarDelegate?
|
||||
|
||||
/// tabBar中items布局偏移量
|
||||
public var itemEdgeInsets = UIEdgeInsets.zero
|
||||
/// 是否设置为自定义布局方式,默认为空。如果为空,则通过itemPositioning属性来设置。如果不为空则忽略itemPositioning,所以当tabBar的itemCustomPositioning属性不为空时,如果想改变布局规则,请设置此属性而非itemPositioning。
|
||||
public var itemCustomPositioning: ESTabBarItemPositioning? {
|
||||
didSet {
|
||||
if let itemCustomPositioning = itemCustomPositioning {
|
||||
switch itemCustomPositioning {
|
||||
case .fill:
|
||||
itemPositioning = .fill
|
||||
case .automatic:
|
||||
itemPositioning = .automatic
|
||||
case .centered:
|
||||
itemPositioning = .centered
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
self.reload()
|
||||
}
|
||||
}
|
||||
/// tabBar自定义item的容器view
|
||||
internal var containers = [ESTabBarItemContainer]()
|
||||
/// 缓存当前tabBarController用来判断是否存在"More"Tab
|
||||
internal weak var tabBarController: UITabBarController?
|
||||
/// 自定义'More'按钮样式,继承自ESTabBarItemContentView
|
||||
open var moreContentView: ESTabBarItemContentView? = ESTabBarItemMoreContentView.init() {
|
||||
didSet { self.reload() }
|
||||
}
|
||||
|
||||
open override var items: [UITabBarItem]? {
|
||||
didSet {
|
||||
self.reload()
|
||||
}
|
||||
}
|
||||
|
||||
open var isEditing: Bool = false {
|
||||
didSet {
|
||||
if oldValue != isEditing {
|
||||
self.updateLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open override func setItems(_ items: [UITabBarItem]?, animated: Bool) {
|
||||
super.setItems(items, animated: animated)
|
||||
self.reload()
|
||||
}
|
||||
|
||||
open override func beginCustomizingItems(_ items: [UITabBarItem]) {
|
||||
ESTabBarController.printError("beginCustomizingItems(_:) is unsupported in ESTabBar.")
|
||||
super.beginCustomizingItems(items)
|
||||
}
|
||||
|
||||
open override func endCustomizing(animated: Bool) -> Bool {
|
||||
ESTabBarController.printError("endCustomizing(_:) is unsupported in ESTabBar.")
|
||||
return super.endCustomizing(animated: animated)
|
||||
}
|
||||
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
self.updateLayout()
|
||||
}
|
||||
|
||||
open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
||||
var b = super.point(inside: point, with: event)
|
||||
if !b {
|
||||
for container in containers {
|
||||
if container.point(inside: CGPoint.init(x: point.x - container.frame.origin.x, y: point.y - container.frame.origin.y), with: event) {
|
||||
b = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal extension ESTabBar /* Layout */ {
|
||||
|
||||
func updateLayout() {
|
||||
guard let tabBarItems = self.items else {
|
||||
ESTabBarController.printError("empty items")
|
||||
return
|
||||
}
|
||||
|
||||
let tabBarButtons = subviews.filter { subview -> Bool in
|
||||
if let cls = NSClassFromString("UITabBarButton") {
|
||||
return subview.isKind(of: cls)
|
||||
}
|
||||
return false
|
||||
} .sorted { (subview1, subview2) -> Bool in
|
||||
return subview1.frame.origin.x < subview2.frame.origin.x
|
||||
}
|
||||
|
||||
if isCustomizing {
|
||||
for (idx, _) in tabBarItems.enumerated() {
|
||||
tabBarButtons[idx].isHidden = false
|
||||
moreContentView?.isHidden = true
|
||||
}
|
||||
for (_, container) in containers.enumerated(){
|
||||
container.isHidden = true
|
||||
}
|
||||
} else {
|
||||
for (idx, item) in tabBarItems.enumerated() {
|
||||
if let _ = item as? ESTabBarItem {
|
||||
tabBarButtons[idx].isHidden = true
|
||||
} else {
|
||||
tabBarButtons[idx].isHidden = false
|
||||
}
|
||||
if isMoreItem(idx), let _ = moreContentView {
|
||||
tabBarButtons[idx].isHidden = true
|
||||
}
|
||||
}
|
||||
for (_, container) in containers.enumerated(){
|
||||
container.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
var layoutBaseSystem = true
|
||||
if let itemCustomPositioning = itemCustomPositioning {
|
||||
switch itemCustomPositioning {
|
||||
case .fill, .automatic, .centered:
|
||||
break
|
||||
case .fillIncludeSeparator, .fillExcludeSeparator:
|
||||
layoutBaseSystem = false
|
||||
}
|
||||
}
|
||||
|
||||
if layoutBaseSystem {
|
||||
// System itemPositioning
|
||||
for (idx, container) in containers.enumerated(){
|
||||
if !tabBarButtons[idx].frame.isEmpty {
|
||||
container.frame = tabBarButtons[idx].frame
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Custom itemPositioning
|
||||
var x: CGFloat = itemEdgeInsets.left
|
||||
var y: CGFloat = itemEdgeInsets.top
|
||||
switch itemCustomPositioning! {
|
||||
case .fillExcludeSeparator:
|
||||
if y <= 0.0 {
|
||||
y += 1.0
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
let width = bounds.size.width - itemEdgeInsets.left - itemEdgeInsets.right
|
||||
let height = bounds.size.height - y - itemEdgeInsets.bottom
|
||||
let eachWidth = itemWidth == 0.0 ? width / CGFloat(containers.count) : itemWidth
|
||||
let eachSpacing = itemSpacing == 0.0 ? 0.0 : itemSpacing
|
||||
|
||||
for container in containers {
|
||||
container.frame = CGRect.init(x: x, y: y, width: eachWidth, height: height)
|
||||
x += eachWidth
|
||||
x += eachSpacing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal extension ESTabBar /* Actions */ {
|
||||
|
||||
func isMoreItem(_ index: Int) -> Bool {
|
||||
return ESTabBarController.isShowingMore(tabBarController) && (index == (items?.count ?? 0) - 1)
|
||||
}
|
||||
|
||||
func removeAll() {
|
||||
for container in containers {
|
||||
container.removeFromSuperview()
|
||||
}
|
||||
containers.removeAll()
|
||||
}
|
||||
|
||||
func reload() {
|
||||
removeAll()
|
||||
guard let tabBarItems = self.items else {
|
||||
ESTabBarController.printError("empty items")
|
||||
return
|
||||
}
|
||||
for (idx, item) in tabBarItems.enumerated() {
|
||||
let container = ESTabBarItemContainer.init(self, tag: 1000 + idx)
|
||||
self.addSubview(container)
|
||||
self.containers.append(container)
|
||||
|
||||
if let item = item as? ESTabBarItem, let contentView = item.contentView {
|
||||
container.addSubview(contentView)
|
||||
}
|
||||
if self.isMoreItem(idx), let moreContentView = moreContentView {
|
||||
container.addSubview(moreContentView)
|
||||
}
|
||||
}
|
||||
|
||||
self.updateAccessibilityLabels()
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
|
||||
@objc func highlightAction(_ sender: AnyObject?) {
|
||||
guard let container = sender as? ESTabBarItemContainer else {
|
||||
return
|
||||
}
|
||||
let newIndex = max(0, container.tag - 1000)
|
||||
guard newIndex < items?.count ?? 0, let item = self.items?[newIndex], item.isEnabled == true else {
|
||||
return
|
||||
}
|
||||
|
||||
if (customDelegate?.tabBar(self, shouldSelect: item) ?? true) == false {
|
||||
return
|
||||
}
|
||||
|
||||
if let item = item as? ESTabBarItem {
|
||||
item.contentView?.highlight(animated: true, completion: nil)
|
||||
} else if self.isMoreItem(newIndex) {
|
||||
moreContentView?.highlight(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func dehighlightAction(_ sender: AnyObject?) {
|
||||
guard let container = sender as? ESTabBarItemContainer else {
|
||||
return
|
||||
}
|
||||
let newIndex = max(0, container.tag - 1000)
|
||||
guard newIndex < items?.count ?? 0, let item = self.items?[newIndex], item.isEnabled == true else {
|
||||
return
|
||||
}
|
||||
|
||||
if (customDelegate?.tabBar(self, shouldSelect: item) ?? true) == false {
|
||||
return
|
||||
}
|
||||
|
||||
if let item = item as? ESTabBarItem {
|
||||
item.contentView?.dehighlight(animated: true, completion: nil)
|
||||
} else if self.isMoreItem(newIndex) {
|
||||
moreContentView?.dehighlight(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func selectAction(_ sender: AnyObject?) {
|
||||
guard let container = sender as? ESTabBarItemContainer else {
|
||||
return
|
||||
}
|
||||
select(itemAtIndex: container.tag - 1000, animated: true)
|
||||
}
|
||||
|
||||
@objc func select(itemAtIndex idx: Int, animated: Bool) {
|
||||
let newIndex = max(0, idx)
|
||||
let currentIndex = (selectedItem != nil) ? (items?.firstIndex(of: selectedItem!) ?? -1) : -1
|
||||
guard newIndex < items?.count ?? 0, let item = self.items?[newIndex], item.isEnabled == true else {
|
||||
return
|
||||
}
|
||||
|
||||
if (customDelegate?.tabBar(self, shouldSelect: item) ?? true) == false {
|
||||
return
|
||||
}
|
||||
|
||||
if (customDelegate?.tabBar(self, shouldHijack: item) ?? false) == true {
|
||||
customDelegate?.tabBar(self, didHijack: item)
|
||||
if animated {
|
||||
if let item = item as? ESTabBarItem {
|
||||
item.contentView?.select(animated: animated, completion: {
|
||||
item.contentView?.deselect(animated: false, completion: nil)
|
||||
})
|
||||
} else if self.isMoreItem(newIndex) {
|
||||
moreContentView?.select(animated: animated, completion: {
|
||||
self.moreContentView?.deselect(animated: animated, completion: nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if currentIndex != newIndex {
|
||||
if currentIndex != -1 && currentIndex < items?.count ?? 0{
|
||||
if let currentItem = items?[currentIndex] as? ESTabBarItem {
|
||||
currentItem.contentView?.deselect(animated: animated, completion: nil)
|
||||
} else if self.isMoreItem(currentIndex) {
|
||||
moreContentView?.deselect(animated: animated, completion: nil)
|
||||
}
|
||||
}
|
||||
if let item = item as? ESTabBarItem {
|
||||
item.contentView?.select(animated: animated, completion: nil)
|
||||
} else if self.isMoreItem(newIndex) {
|
||||
moreContentView?.select(animated: animated, completion: nil)
|
||||
}
|
||||
} else if currentIndex == newIndex {
|
||||
if let item = item as? ESTabBarItem {
|
||||
item.contentView?.reselect(animated: animated, completion: nil)
|
||||
} else if self.isMoreItem(newIndex) {
|
||||
moreContentView?.reselect(animated: animated, completion: nil)
|
||||
}
|
||||
|
||||
if let tabBarController = tabBarController {
|
||||
var navVC: UINavigationController?
|
||||
if let n = tabBarController.selectedViewController as? UINavigationController {
|
||||
navVC = n
|
||||
} else if let n = tabBarController.selectedViewController?.navigationController {
|
||||
navVC = n
|
||||
}
|
||||
|
||||
if let navVC = navVC {
|
||||
if navVC.viewControllers.contains(tabBarController) {
|
||||
if navVC.viewControllers.count > 1 && navVC.viewControllers.last != tabBarController {
|
||||
navVC.popToViewController(tabBarController, animated: true);
|
||||
}
|
||||
} else {
|
||||
if navVC.viewControllers.count > 1 {
|
||||
navVC.popToRootViewController(animated: animated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
delegate?.tabBar?(self, didSelect: item)
|
||||
self.updateAccessibilityLabels()
|
||||
}
|
||||
|
||||
func updateAccessibilityLabels() {
|
||||
guard let tabBarItems = self.items, tabBarItems.count == self.containers.count else {
|
||||
return
|
||||
}
|
||||
|
||||
for (idx, item) in tabBarItems.enumerated() {
|
||||
let container = self.containers[idx]
|
||||
container.accessibilityIdentifier = item.accessibilityIdentifier
|
||||
container.accessibilityTraits = item.accessibilityTraits
|
||||
|
||||
if item == selectedItem {
|
||||
container.accessibilityTraits = container.accessibilityTraits.union(.selected)
|
||||
}
|
||||
|
||||
if let explicitLabel = item.accessibilityLabel {
|
||||
container.accessibilityLabel = explicitLabel
|
||||
container.accessibilityHint = item.accessibilityHint ?? container.accessibilityHint
|
||||
} else {
|
||||
var accessibilityTitle = ""
|
||||
if let item = item as? ESTabBarItem {
|
||||
accessibilityTitle = item.accessibilityLabel ?? item.title ?? ""
|
||||
}
|
||||
if self.isMoreItem(idx) {
|
||||
accessibilityTitle = NSLocalizedString("More_TabBarItem", bundle: Bundle(for:ESTabBarController.self), comment: "")
|
||||
}
|
||||
|
||||
let formatString = NSLocalizedString(item == selectedItem ? "TabBarItem_Selected_AccessibilityLabel" : "TabBarItem_AccessibilityLabel",
|
||||
bundle: Bundle(for: ESTabBarController.self),
|
||||
comment: "")
|
||||
container.accessibilityLabel = String(format: formatString, accessibilityTitle, idx + 1, tabBarItems.count)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user