This commit is contained in:
DDIsFriend
2023-08-18 17:28:57 +08:00
commit f0e8a1709d
4282 changed files with 192396 additions and 0 deletions

View 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
/// UITabBarItemPositioningUITabBarItemPositioninginsets使ESTabBarItemPositioningitem Position fill
///
/// - automatic: UITabBarItemPositioning.automatic
/// - fill: UITabBarItemPositioning.fill
/// - centered: UITabBarItemPositioning.centered
/// - fillExcludeSeparator: filltabBar线
/// - fillIncludeSeparator: filltabBar线
public enum ESTabBarItemPositioning : Int {
case automatic
case fill
case centered
case fillExcludeSeparator
case fillIncludeSeparator
}
/// UITabBarDelegateUITabBarControllerDelegate
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)
}
/// ESTabBarUITabBarUIControltabBarItemtabBardelegate,items,selectedImge,itemPositioning,itemWidth,itemSpacingtabBar
open class ESTabBar: UITabBar {
internal weak var customDelegate: ESTabBarDelegate?
/// tabBaritems
public var itemEdgeInsets = UIEdgeInsets.zero
/// itemPositioningitemPositioning,tabBaritemCustomPositioningitemPositioning
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()
}
}
/// tabBaritemview
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)
}
}
}
}