Veloria/Veloria/Base/View/TabBar/VPTabBar.swift
2025-05-20 10:34:39 +08:00

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)
}
}