I am attempting to create a customized TabBar with a slider that has the identical width because the buttons inside it. The slider ought to change shade and place primarily based on the index of the button pressed inside the TabBar. My present implementation is proven under, however the issue is that when the system rotates or the app resizes, the slider will get caught within the fallacious place. I’ve tried a number of approaches to unravel this difficulty, however I am beginning to assume that your complete logic behind my implementation may be flawed.
import UIKit
class SlidingTabBar: UIView {
public var tapCallback: ((Int) -> ())?
non-public let horizontalStack = UIStackView()
non-public let sliderView = UIView()
non-public var sliderLeftConstraint: NSLayoutConstraint?
let objects : [CustomTabBarItem] = CustomTabBarItem.allCases
override init(body: CGRect) {
tremendous.init(body: body)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
tremendous.init(coder:aDecoder)
commonInit()
}
override var intrinsicContentSize: CGSize {
return CGSize(width: UIView.noIntrinsicMetric, top: 60)
}
non-public func commonInit() {
// Arrange the slider view
sliderView.backgroundColor = objects[0].shade.withAlphaComponent(0.2)
sliderView.layer.cornerRadius = 10
sliderView.translatesAutoresizingMaskIntoConstraints = false
addSubview(sliderView)
horizontalStack.distribution = .fillEqually
horizontalStack.alignment = .middle
horizontalStack.translatesAutoresizingMaskIntoConstraints = false
addSubview(horizontalStack)
let pad: CGFloat = 8.0
sliderLeftConstraint = sliderView.leftAnchor.constraint(equalTo: horizontalStack.leftAnchor)
NSLayoutConstraint.activate([
horizontalStack.topAnchor.constraint(equalTo: topAnchor, constant: pad),
horizontalStack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: pad),
horizontalStack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -pad),
horizontalStack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -pad),
sliderLeftConstraint!,
sliderView.widthAnchor.constraint(equalTo: horizontalStack.widthAnchor, multiplier: 1.0 / CGFloat(items.count)),
sliderView.heightAnchor.constraint(equalTo: horizontalStack.heightAnchor),
sliderView.centerYAnchor.constraint(equalTo: horizontalStack.centerYAnchor)
])
for (index, merchandise) in objects.enumerated() {
let itemView = createTabBarItemView(for: merchandise)
itemView.tag = index
horizontalStack.addArrangedSubview(itemView)
itemView.topAnchor.constraint(equalTo: horizontalStack.topAnchor).isActive = true
itemView.bottomAnchor.constraint(equalTo: horizontalStack.bottomAnchor).isActive = true
}
self.layer.cornerRadius = 10.0
self.backgroundColor = .white
}
non-public func createTabBarItemView(for merchandise: CustomTabBarItem) -> UIView {
let itemView = UIView()
let imageView = UIImageView(picture: merchandise.picture)
imageView.contentMode = .scaleAspectFit
imageView.tintColor = merchandise == objects[0] ? merchandise.shade : .grey
let label = UILabel()
label.textual content = merchandise.title
label.textColor = merchandise == objects[0] ? merchandise.shade : .grey
label.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
let itemStackView = UIStackView(arrangedSubviews: [imageView, label])
itemStackView.axis = .vertical
itemStackView.alignment = .middle
itemStackView.spacing = 2
itemStackView.translatesAutoresizingMaskIntoConstraints = false
itemView.addSubview(itemStackView)
NSLayoutConstraint.activate([
itemStackView.centerXAnchor.constraint(equalTo: itemView.centerXAnchor),
itemStackView.centerYAnchor.constraint(equalTo: itemView.centerYAnchor),
])
itemView.addGestureRecognizer(UITapGestureRecognizer(goal: self, motion: #selector(itemTapped(_:))))
return itemView
}
@objc non-public func itemTapped(_ recognizer: UITapGestureRecognizer) {
guard let itemStackView = recognizer.view,
let indexItem = horizontalStack.arrangedSubviews.firstIndex(of: itemStackView) else { return }
tapCallback?(indexItem)
UIView.animate(withDuration: 0.3) {
self.sliderView.backgroundColor = self.objects[indexItem].shade.withAlphaComponent(0.2)
self.sliderLeftConstraint?.fixed = CGFloat(indexItem) * self.sliderView.bounds.width
self.layoutIfNeeded()
self.horizontalStack.arrangedSubviews.enumerated().forEach { (index, view) in
if let imageView = (view.subviews.first as? UIStackView)?.arrangedSubviews.first as? UIImageView,
let label = (view.subviews.first as? UIStackView)?.arrangedSubviews.final as? UILabel {
imageView.tintColor = index == indexItem ? self.objects[index].shade : .grey
label.textColor = index == indexItem ? self.objects[index].shade : .grey
}
}
}
}
}
I additionally created an enum
, CustomTabBarItem:
enum CustomTabBarItem: CaseIterable {
case dwelling, search, messages, profile
var picture: UIImage? {
change self {
case .dwelling: return UIImage(systemName: "home")
case .search: return UIImage(systemName: "magnifyingglass")
case .messages: return UIImage(systemName: "message")
case .profile: return UIImage(systemName: "individual")
}
}
var title: String {
change self {
case .dwelling: return "Dwelling"
case .search: return "Search"
case .messages: return "Messages"
case .profile: return "Profile"
}
}
var shade: UIColor {
change self {
case .dwelling: return .orange
case .search: return .blue
case .messages: return .inexperienced
case .profile: return .pink
}
}
}
Is there a solution to replace the mother or father view when the system rotates or a greater method altogether to attain what I am attempting to perform?