// // LNHomeTopTabView.swift // Lanu // // Created by OneeChan on 2025/11/14. // import Foundation import UIKit import SnapKit protocol LNHomeTopTabViewDelegate: AnyObject { func homeTopTabView(view: LNHomeTopTabView, didSelectAt index: Int, type: LNGameTypeItemVO) } class LNHomeTopTabView: UIView { private var titles: [LNGameTypeItemVO] = [] private let indicator = UIImageView() private let scrollView = UIScrollView() private let stackView = UIStackView() private var tabItemViews: [UIButton] = [] weak var delegate: LNHomeTopTabViewDelegate? override init(frame: CGRect) { super.init(frame: frame) setupViews() } func update(_ tabs: [LNGameTypeItemVO]) { let old = stackView.arrangedSubviews old.forEach { stackView.removeArrangedSubview($0) $0.removeFromSuperview() } tabItemViews.removeAll() titles = tabs for type in tabs { let tabItem = UIButton() tabItem.setTitle(type.name, for: .normal) tabItem.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } self.handleTabClick(type: type, tab: tabItem) }), for: .touchUpInside) stackView.addArrangedSubview(tabItem) tabItemViews.append(tabItem) } self.layoutIfNeeded() self.handleTabClick(type: self.titles.first!, tab: self.tabItemViews.first!) } func selectTab(at index: Int) { guard index < tabItemViews.count else { return } for (itemIndex, tab) in tabItemViews.enumerated() { let isSelected = itemIndex == index tab.titleLabel?.font = isSelected ? .heading_h1_5 : .heading_h3 tab.setTitleColor(isSelected ? .text_5 : .text_4, for: .normal) } indicator.snp.remakeConstraints { make in make.center.equalTo(tabItemViews[index]) } UIView.animate(withDuration: 0.3) { [weak self] in guard let self else { return } self.layoutIfNeeded() let targetTab = tabItemViews[index] self.scrollView.scrollRectToVisible(targetTab.frame, animated: true) } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension LNHomeTopTabView { private func handleTabClick(type: LNGameTypeItemVO, tab: UIButton) { guard let index = tabItemViews.firstIndex(of: tab) else { return } selectTab(at: index) delegate?.homeTopTabView(view: self, didSelectAt: index, type: type) } } extension LNHomeTopTabView { private func setupViews() { scrollView.showsHorizontalScrollIndicator = false scrollView.showsVerticalScrollIndicator = false addSubview(scrollView) scrollView.snp.makeConstraints { make in make.edges.equalToSuperview() } let fakeView = UIView() scrollView.addSubview(fakeView) fakeView.snp.makeConstraints { make in make.leading.top.bottom.equalToSuperview() make.width.equalTo(0) make.height.equalToSuperview() } indicator.image = .icHomeTabSelected scrollView.addSubview(indicator) stackView.axis = .horizontal stackView.spacing = 14 stackView.distribution = .equalSpacing scrollView.addSubview(stackView) stackView.snp.makeConstraints { make in make.edges.equalToSuperview() } } } #if DEBUG import SwiftUI struct LNHomeTopTabViewPreview: UIViewRepresentable { func makeUIView(context: Context) -> some UIView { let container = UIView() container.backgroundColor = .lightGray let view = LNHomeTopTabView() container.addSubview(view) view.snp.makeConstraints { make in make.leading.trailing.top.equalToSuperview() } return container } func updateUIView(_ uiView: UIViewType, context: Context) { } } #Preview(body: { LNHomeTopTabViewPreview() }) #endif