| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- //
- // LNInterestSetupViewController.swift
- // Lanu
- //
- // Created by OneeChan on 2025/12/3.
- //
- import Foundation
- import UIKit
- import SnapKit
- extension UIView {
- func pushToInterestSetup(_ config: LNProfileUpdateConfig) {
- let vc = LNInterestSetupViewController(config: config)
- navigationController?.pushViewController(vc, animated: true)
- }
- }
- class LNInterestSetupViewController: LNViewController {
- private let updateConfig: LNProfileUpdateConfig
-
- private let fakeNavBar = UIView()
-
- private let horizentalSpace = 22.0
- private let lineSpace = 8.0
- private let itemSpacing = 13.0
- private let minWidth = 115.0
- private let itemHeight = 85.0
- static let headerHeight = 44.0
- private let collectionViewLayout = UICollectionViewFlowLayout()
- private let collectionView: UICollectionView
-
- private let nextButton = UIButton()
- private let bottomGradient = CAGradientLayer()
-
- private let maxCount = 5
- private var selectedIndexs: [IndexPath] = []
- private var games: [LNGameTypeItemVO] = LNGameMateManager.shared.curGameTypes
-
- init(config: LNProfileUpdateConfig) {
- updateConfig = config
-
- collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
-
- super.init(nibName: nil, bundle: nil)
-
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- showNavigationBar = false
-
- setupViews()
- }
-
- override func viewDidLayoutSubviews() {
- super.viewDidLayoutSubviews()
-
- if let superlayer = bottomGradient.superlayer {
- bottomGradient.frame = superlayer.bounds
- }
- }
- }
- extension LNInterestSetupViewController: UICollectionViewDataSource, UICollectionViewDelegate {
- func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
- games[section].children.count
- }
-
- func numberOfSections(in collectionView: UICollectionView) -> Int {
- games.count
- }
-
- func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
- let cell = collectionView.dequeueReusableCell(withReuseIdentifier: LNInterestItemCell.className, for: indexPath) as! LNInterestItemCell
-
- let item = games[indexPath.section].children[indexPath.row]
- cell.update(item)
- cell.inSelected = selectedIndexs.contains(indexPath)
- cell.isEnable = selectedIndexs.count < maxCount || selectedIndexs.contains(indexPath)
-
- return cell
- }
-
- func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
- guard kind == UICollectionView.elementKindSectionHeader else {
- return UICollectionReusableView()
- }
-
- let view = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: LNInterestGroupHeader.className, for: indexPath) as! LNInterestGroupHeader
-
- let section = games[indexPath.section]
- view.update(section.name)
-
- return view
- }
-
- func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
- let needReload: Bool
- if selectedIndexs.contains(indexPath) {
- needReload = selectedIndexs.count == maxCount
- selectedIndexs.removeAll { $0 == indexPath }
- } else if selectedIndexs.count < maxCount {
- selectedIndexs.append(indexPath)
- needReload = selectedIndexs.count == maxCount
- } else {
- needReload = false
- }
-
- if needReload {
- collectionView.reloadData()
- } else {
- collectionView.reloadItems(at: [indexPath])
- }
- checkNextButtonEnable()
- }
- }
- extension LNInterestSetupViewController {
- private func checkNextButtonEnable() {
- nextButton.isEnabled = selectedIndexs.count > 0
- }
-
- private func setupViews() {
- view.backgroundColor = .primary_1
-
- let bg = UIImageView()
- bg.image = .init(named: "ic_login_interest_bg")
- view.addSubview(bg)
- bg.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.top.equalToSuperview()
- }
-
- let navBar = buildFakeNavBar()
- view.addSubview(navBar)
- navBar.snp.makeConstraints { make in
- make.directionalHorizontalEdges.equalToSuperview()
- make.top.equalToSuperview()
- }
-
- let titleView = buildTitles()
- view.addSubview(titleView)
- titleView.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(22)
- make.top.equalTo(fakeNavBar.snp.bottom).offset(22)
- }
-
- let listView = buildListView()
- view.addSubview(listView)
- listView.snp.makeConstraints { make in
- make.directionalHorizontalEdges.equalToSuperview().inset(horizentalSpace)
- make.top.equalTo(titleView.snp.bottom).offset(6)
- }
-
- let bottom = buildBottomView()
- view.addSubview(bottom)
- bottom.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom)
- make.top.equalTo(listView.snp.bottom).offset(-16)
- }
- }
-
- private func buildFakeNavBar() -> UIView {
- fakeNavBar.snp.makeConstraints { make in
- make.height.equalTo(44 + UIView.statusBarHeight)
- }
-
- let container = UIView()
- fakeNavBar.addSubview(container)
- container.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.bottom.equalToSuperview()
- make.height.equalTo(44)
- }
-
- let backButton = UIButton()
- backButton.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- navigationController?.popViewController(animated: true)
- }), for: .touchUpInside)
- let buttonImage: UIImage? = .init(systemName: "chevron.backward")?.withRenderingMode(.alwaysTemplate)
- backButton.setImage(buttonImage, for: .normal)
- backButton.tintColor = .text_4
- container.addSubview(backButton)
- backButton.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.leading.equalToSuperview().offset(16)
- make.width.height.equalTo(24)
- }
-
- return fakeNavBar
- }
-
- private func buildTitles() -> UIView {
- let stackView = UIStackView()
- stackView.axis = .vertical
- stackView.spacing = 6
-
- let mainLabel = UILabel()
- mainLabel.text = .init(key: "A00111")
- mainLabel.font = .heading_h1
- mainLabel.textColor = .text_5
- stackView.addArrangedSubview(mainLabel)
-
- let descLabel = UILabel()
- descLabel.font = .body_m
- descLabel.textColor = .text_3
- descLabel.text = .init(key: "A00112")
- descLabel.numberOfLines = 0
- stackView.addArrangedSubview(descLabel)
-
- return stackView
- }
-
- private func buildListView() -> UIView {
- let count = floor(CGFloat((view.bounds.width - horizentalSpace * 2) + itemSpacing) / minWidth)
- let itemWidth = (view.bounds.width - horizentalSpace * 2 + itemSpacing) / count - itemSpacing
- collectionViewLayout.itemSize = .init(width: itemWidth, height: itemHeight)
- collectionViewLayout.minimumLineSpacing = lineSpace
- collectionViewLayout.minimumInteritemSpacing = itemSpacing
- collectionViewLayout.headerReferenceSize = .init(width: view.bounds.width - horizentalSpace * 2, height: Self.headerHeight)
- // collectionViewLayout.sectionHeadersPinToVisibleBounds = true
-
- collectionView.backgroundColor = .clear
- collectionView.dataSource = self
- collectionView.delegate = self
- collectionView.showsHorizontalScrollIndicator = false
- collectionView.showsVerticalScrollIndicator = false
- collectionView.register(LNInterestItemCell.self, forCellWithReuseIdentifier: LNInterestItemCell.className)
- collectionView.register(LNInterestGroupHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: LNInterestGroupHeader.className)
- collectionView.contentInset = .init(top: 0, left: 0, bottom: 20, right: 0)
-
- return collectionView
- }
-
- private func buildBottomView() -> UIView {
- let container = UIView()
-
- bottomGradient.colors = [
- UIColor.white.withAlphaComponent(0).cgColor,
- UIColor.primary_1.cgColor,
- UIColor.primary_1.cgColor
- ]
- bottomGradient.locations = [0, 0.3, 1]
- bottomGradient.startPoint = .init(x: 0, y: 0)
- bottomGradient.endPoint = .init(x: 0, y: 1)
- container.layer.addSublayer(bottomGradient)
-
- nextButton.setBackgroundImage(.primary_8, for: .normal)
- nextButton.setTitle(.init(key: "A00113"), for: .normal)
- nextButton.setTitleColor(.text_1, for: .normal)
- nextButton.layer.cornerRadius = 23.5
- nextButton.clipsToBounds = true
- nextButton.isEnabled = false
- nextButton.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- let interests = selectedIndexs.compactMap {
- self.games[$0.section].children[$0.row].code
- }
- updateConfig.interest = interests
- LNProfileManager.shared.modifyMyProfile(config: updateConfig) { [weak self] success in
- guard let self else { return }
- guard success else { return }
- navigationController?.popToRootViewController(animated: true)
- LNGameMateManager.shared.getGameTypeList { _ in }
- }
- }), for: .touchUpInside)
- container.addSubview(nextButton)
- nextButton.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(16)
- make.top.equalToSuperview().offset(16)
- make.bottom.equalToSuperview().offset(-4)
- make.height.equalTo(47)
- }
-
- return container
- }
- }
- private class LNInterestItemCell: UICollectionViewCell {
- private let ic = UIImageView()
- private let nameLabel = UILabel()
- private let bg = UIImageView()
-
- var inSelected: Bool = false {
- didSet {
- bg.image = .init(named: inSelected ? "ic_login_interest_item_selected" : "ic_login_interest_item")
- }
- }
- var isEnable: Bool = true {
- didSet {
- contentView.alpha = isEnable ? 1.0 : 0.5
- isUserInteractionEnabled = isEnable
- }
- }
-
- override init(frame: CGRect) {
- super.init(frame: frame)
-
- contentView.addSubview(bg)
- bg.snp.makeConstraints { make in
- make.center.equalToSuperview()
- make.width.equalTo(102)
- make.height.equalTo(85)
- }
-
- ic.layer.cornerRadius = 25
- ic.clipsToBounds = true
- bg.addSubview(ic)
- ic.snp.makeConstraints { make in
- make.centerX.equalToSuperview()
- make.top.equalToSuperview().offset(7)
- make.width.height.equalTo(50)
- }
-
- nameLabel.font = .body_s
- nameLabel.textColor = .text_5
- nameLabel.textAlignment = .center
- bg.addSubview(nameLabel)
- nameLabel.snp.makeConstraints { make in
- make.directionalHorizontalEdges.equalToSuperview().inset(6)
- make.bottom.equalToSuperview().offset(-6)
- }
- }
-
- func update(_ item: LNGameCategoryItemVO) {
- ic.sd_setImage(with: URL(string: item.icon))
- nameLabel.text = item.name
-
- let curState = isSelected
- isSelected = curState
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- }
- private class LNInterestGroupHeader: UICollectionReusableView {
- private let titleLabel = UILabel()
-
- override init(frame: CGRect) {
- super.init(frame: frame)
-
- snp.makeConstraints { make in
- make.height.equalTo(LNInterestSetupViewController.headerHeight)
- }
-
- titleLabel.font = .heading_h2
- titleLabel.textColor = .text_5
- addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.bottom.equalToSuperview().offset(-8)
- make.leading.equalToSuperview()
- }
- }
-
- func update(_ title: String) {
- titleLabel.text = title
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- }
|