Files
Emoney-Info---IOS/Emoney Info/MainTabView.swift
Wira Basalamah 8f0b001501 Initial commit
2026-04-24 04:55:24 +07:00

199 lines
6.3 KiB
Swift

import UIKit
// MARK: - MainTabBarController
final class MainTabBarController: UITabBarController {
private let customTabBar = CustomTabBar()
override func viewDidLoad() {
super.viewDidLoad()
tabBar.isHidden = true
view.backgroundColor = Theme.Color.background
setupCustomTabBar()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
layoutCustomTabBar()
}
private func setupCustomTabBar() {
customTabBar.translatesAutoresizingMaskIntoConstraints = false
customTabBar.onTabSelected = { [weak self] index in
self?.selectedIndex = index
}
view.addSubview(customTabBar)
NSLayoutConstraint.activate([
customTabBar.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24),
customTabBar.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -24),
customTabBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -12),
customTabBar.heightAnchor.constraint(equalToConstant: 64)
])
}
private func layoutCustomTabBar() {
view.bringSubviewToFront(customTabBar)
}
// Call this after setting viewControllers to sync selection
override var selectedIndex: Int {
didSet { customTabBar.setSelected(selectedIndex) }
}
override func setTabBarHidden(_ hidden: Bool, animated: Bool = true) {
let duration = animated ? 0.25 : 0
UIView.animate(withDuration: duration) {
self.customTabBar.alpha = hidden ? 0 : 1
}
customTabBar.isUserInteractionEnabled = !hidden
}
}
// MARK: - CustomTabBar
private final class CustomTabBar: UIView {
var onTabSelected: ((Int) -> Void)?
private struct TabItem {
let icon: String // SF Symbol name
let label: String
}
private var items: [TabItem] {[
TabItem(icon: "wave.3.right.circle.fill", label: L10n.tabEmoney),
TabItem(icon: "gearshape.fill", label: L10n.tabSettings)
]}
private var itemViews: [TabItemView] = []
private var selectedIndex: Int = 0
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
backgroundColor = .white
layer.cornerRadius = 32
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.10
layer.shadowOffset = CGSize(width: 0, height: 4)
layer.shadowRadius = 16
let stack = UIStackView()
stack.axis = .horizontal
stack.distribution = .fillEqually
stack.translatesAutoresizingMaskIntoConstraints = false
addSubview(stack)
NSLayoutConstraint.activate([
stack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
stack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
stack.topAnchor.constraint(equalTo: topAnchor, constant: 8),
stack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8)
])
for (index, item) in items.enumerated() {
let tabView = TabItemView(icon: item.icon, label: item.label)
tabView.isActive = (index == selectedIndex)
tabView.tag = index
let tap = UITapGestureRecognizer(target: self, action: #selector(tabTapped(_:)))
tabView.addGestureRecognizer(tap)
stack.addArrangedSubview(tabView)
itemViews.append(tabView)
}
}
@objc private func tabTapped(_ gesture: UITapGestureRecognizer) {
guard let index = gesture.view?.tag else { return }
setSelected(index)
onTabSelected?(index)
}
func setSelected(_ index: Int) {
guard index != selectedIndex else { return }
selectedIndex = index
UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5) {
self.itemViews.enumerated().forEach { i, view in
view.isActive = (i == index)
view.layoutIfNeeded()
}
}
}
}
// MARK: - TabItemView
private final class TabItemView: UIView {
var isActive: Bool = false {
didSet { applyState() }
}
private let iconView = UIImageView()
private let labelView = UILabel()
private let pill = UIView()
init(icon: String, label: String) {
super.init(frame: .zero)
iconView.image = UIImage(systemName: icon)
iconView.contentMode = .scaleAspectFit
labelView.text = label
labelView.font = Theme.Font.caption(weight: .semibold)
labelView.textAlignment = .center
setup()
}
required init?(coder: NSCoder) { fatalError() }
private func setup() {
pill.layer.cornerRadius = 20
pill.translatesAutoresizingMaskIntoConstraints = false
addSubview(pill)
let stack = UIStackView(arrangedSubviews: [iconView, labelView])
stack.axis = .vertical
stack.spacing = 2
stack.alignment = .center
stack.translatesAutoresizingMaskIntoConstraints = false
addSubview(stack)
NSLayoutConstraint.activate([
pill.centerXAnchor.constraint(equalTo: centerXAnchor),
pill.centerYAnchor.constraint(equalTo: centerYAnchor),
pill.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.85),
pill.heightAnchor.constraint(equalToConstant: 48),
stack.centerXAnchor.constraint(equalTo: centerXAnchor),
stack.centerYAnchor.constraint(equalTo: centerYAnchor),
iconView.widthAnchor.constraint(equalToConstant: 20),
iconView.heightAnchor.constraint(equalToConstant: 20)
])
applyState()
}
private func applyState() {
if isActive {
pill.backgroundColor = Theme.Color.primary
iconView.tintColor = .white
labelView.textColor = .white
pill.transform = .identity
} else {
pill.backgroundColor = .clear
iconView.tintColor = Theme.Color.textSecondary
labelView.textColor = Theme.Color.textSecondary
pill.transform = .identity
}
}
}