210 lines
6.5 KiB
Swift
210 lines
6.5 KiB
Swift
//
|
|
// VPTabBar.swift
|
|
// Veloria
|
|
//
|
|
// Created by Veloria on 2025/5/19.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
class VPTabBar: UITabBar {
|
|
static let itemCount = 4
|
|
static let itmeMargin: CGFloat = 15
|
|
static let itemSpacing: CGFloat = 11
|
|
static let itemMinWidth = 54.0
|
|
static let itemMaxWidth = UIScreen.width - (itemMinWidth + itemSpacing) * CGFloat(itemCount - 1) - itmeMargin * 2
|
|
static let animateDuration: TimeInterval = 0.3
|
|
|
|
override func sizeThatFits(_ size: CGSize) -> CGSize {
|
|
var size = super.sizeThatFits(size)
|
|
size.height = UIScreen.customTabBarHeight
|
|
return size
|
|
}
|
|
|
|
private let tagOffset = 1000
|
|
|
|
override var items: [UITabBarItem]? {
|
|
didSet {
|
|
reload()
|
|
}
|
|
}
|
|
|
|
var containers = [VPTabBarItemContainer]()
|
|
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
// self.updateLayout()
|
|
}
|
|
|
|
override func setItems(_ items: [UITabBarItem]?, animated: Bool) {
|
|
super.setItems(items, animated: animated)
|
|
self.reload()
|
|
}
|
|
|
|
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
|
|
var b = super.point(inside: point, with: event)
|
|
if !b {
|
|
for container in containers {
|
|
let p = CGPoint.init(x: point.x - container.frame.origin.x, y: point.y - container.frame.origin.y)
|
|
if container.point(inside: p, with: event) {
|
|
b = true
|
|
}
|
|
}
|
|
}
|
|
return b
|
|
}
|
|
|
|
}
|
|
|
|
extension VPTabBar {
|
|
func updateLayout() {
|
|
subviews.forEach {
|
|
if let cls = NSClassFromString("UITabBarButton") {
|
|
if $0.isKind(of: cls) == true {
|
|
$0.isHidden = true
|
|
}
|
|
}
|
|
}
|
|
|
|
var index = 0
|
|
containers.forEach {
|
|
if $0.contentView == (selectedItem as? VPTabBarItem)?.contentView {
|
|
index = $0.tag - tagOffset
|
|
}
|
|
}
|
|
updateSelectedStatus(index: index, animated: false)
|
|
|
|
}
|
|
|
|
|
|
///更新选中状态
|
|
private func updateSelectedStatus(index: Int, animated: Bool) {
|
|
let margin: CGFloat = Self.itmeMargin
|
|
let itemSpacing: CGFloat = Self.itemSpacing
|
|
|
|
let itemHeight = Self.itemMinWidth
|
|
let itemMinWidth = Self.itemMinWidth
|
|
let itemMaxWidth = Self.itemMaxWidth
|
|
var x = margin
|
|
|
|
containers.forEach {
|
|
if $0.tag == index + tagOffset {
|
|
// $0.frame = CGRect(x: x, y: 10, width: itemMaxWidth, height: itemHeight)
|
|
$0.snp.remakeConstraints { make in
|
|
make.left.equalTo(x)
|
|
make.top.equalTo(10)
|
|
make.width.equalTo(itemMaxWidth)
|
|
make.height.equalTo(itemHeight)
|
|
}
|
|
x += (itemMaxWidth + itemSpacing)
|
|
} else {
|
|
// $0.frame = CGRect(x: x, y: 10, width: itemMinWidth, height: itemHeight)
|
|
$0.snp.remakeConstraints { make in
|
|
make.left.equalTo(x)
|
|
make.top.equalTo(10)
|
|
make.width.equalTo(itemMinWidth)
|
|
make.height.equalTo(itemHeight)
|
|
}
|
|
x += (itemMinWidth + itemSpacing)
|
|
}
|
|
}
|
|
|
|
if animated {
|
|
UIView.animate(withDuration: Self.animateDuration) {
|
|
self.layoutIfNeeded()
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
extension VPTabBar {
|
|
func removeAll() {
|
|
for container in containers {
|
|
container.removeFromSuperview()
|
|
}
|
|
containers.removeAll()
|
|
}
|
|
|
|
func reload() {
|
|
guard let items = self.items else { return }
|
|
|
|
|
|
items.enumerated().forEach { (index, item) in
|
|
let container = VPTabBarItemContainer(self, tag: index + tagOffset)
|
|
addSubview(container)
|
|
|
|
if let item = item as? VPTabBarItem {
|
|
container.contentView = item.contentView
|
|
}
|
|
containers.append(container)
|
|
}
|
|
updateLayout()
|
|
|
|
self.setNeedsLayout()
|
|
|
|
}
|
|
|
|
@objc func highlightAction(_ sender: AnyObject?) {
|
|
guard let container = sender as? VPTabBarItemContainer else {
|
|
return
|
|
}
|
|
let newIndex = max(0, container.tag - tagOffset)
|
|
guard newIndex < items?.count ?? 0, let item = self.items?[newIndex], item.isEnabled == true else {
|
|
return
|
|
}
|
|
|
|
if let item = item as? VPTabBarItem {
|
|
item.contentView.highlight(animated: true, completion: nil)
|
|
}
|
|
}
|
|
|
|
@objc func dehighlightAction(_ sender: AnyObject?) {
|
|
guard let container = sender as? VPTabBarItemContainer else {
|
|
return
|
|
}
|
|
let newIndex = max(0, container.tag - tagOffset)
|
|
guard newIndex < items?.count ?? 0, let item = self.items?[newIndex], item.isEnabled == true else {
|
|
return
|
|
}
|
|
|
|
if let item = item as? VPTabBarItem {
|
|
item.contentView.dehighlight(animated: true, completion: nil)
|
|
}
|
|
}
|
|
|
|
@objc func selectAction(_ sender: AnyObject?) {
|
|
guard let container = sender as? VPTabBarItemContainer else {
|
|
return
|
|
}
|
|
select(itemAtIndex: container.tag - tagOffset, 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 currentIndex != newIndex {
|
|
if currentIndex != -1 && currentIndex < items?.count ?? 0{
|
|
if let currentItem = items?[currentIndex] as? VPTabBarItem {
|
|
currentItem.contentView.deselect(animated: animated, completion: nil)
|
|
}
|
|
}
|
|
if let item = item as? VPTabBarItem {
|
|
item.contentView.select(animated: animated, completion: nil)
|
|
}
|
|
} else if currentIndex == newIndex {
|
|
if let item = item as? VPTabBarItem {
|
|
item.contentView.reselect(animated: animated, completion: nil)
|
|
}
|
|
}
|
|
updateSelectedStatus(index: newIndex, animated: animated)
|
|
|
|
delegate?.tabBar?(self, didSelect: item)
|
|
}
|
|
}
|