| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- //
- // LNJoinUsInputInfoView.swift
- // Gami
- //
- // Created by OneeChan on 2026/1/19.
- //
- import Foundation
- import UIKit
- import SnapKit
- import Combine
- protocol LNJoinUsInputInfoViewDelegate: AnyObject {
- func joinUsInputInfoViewDidFinish(view: LNJoinUsInputInfoView)
- }
- class LNJoinUsInputInfoView: UIView {
- private let scrollView = UIScrollView()
-
- private let avatar = LNImageUploadView()
-
- private let nameInput = UITextField()
- private let nameCountLabel = UILabel()
-
- private let maleButton = UIButton()
- private let femaleButton = UIButton()
- private var gender: LNUserGender = .unknow {
- didSet {
- maleButton.backgroundColor = gender == .male ? .fill_5 : .primary_1
- femaleButton.backgroundColor = gender == .female ? .fill_5 : .primary_1
- }
- }
-
- private let birthdayLabel = UILabel()
- private var birthday: Double = 0 {
- didSet {
- birthdayLabel.text = birthday.formattedFullDate("-")
- }
- }
-
- private let languageLabel = UILabel()
- private var curLanguages: [LNLanguageConstsVO] = [] {
- didSet {
- languageLabel.text = curLanguages.map({ $0.name }).joined(separator: ",")
- }
- }
-
- private let bioInput = LNCommonTextView()
-
- private let applyButton = UIButton()
-
- weak var delegate: LNJoinUsInputInfoViewDelegate?
-
- override init(frame: CGRect) {
- super.init(frame: frame)
-
- setupViews()
- }
-
- func update(_ info: LNJoinUsBaseInfoVO) {
- avatar.loadImage(url:info.avatar)
- nameInput.text = info.nickname
- gender = info.gender
- bioInput.setText(info.intro)
-
- var languages: [LNLanguageConstsVO] = []
- for language in info.languageCodes {
- if let item = LNConfigManager.shared.commonConfig.commonLanguageConsts.first(where: { $0.code == language }) {
- languages.append(item)
- }
- }
- curLanguages = languages
-
- let dateFormatter = DateFormatter()
- dateFormatter.dateFormat = "yyyy-MM-dd"
- if let date = dateFormatter.date(from: info.birthday) {
- birthday = date.timeIntervalSince1970
- } else {
- birthday = Double(myUserInfo.birthday / 1_000)
- }
-
- checkSaveButton()
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- }
- extension LNJoinUsInputInfoView: LNImageUploadViewDelegate {
- func onImageUploadView(view: LNImageUploadView, didUploadImage url: String) {
- checkSaveButton()
- }
- }
- extension LNJoinUsInputInfoView: UITextFieldDelegate, UITextViewDelegate {
- func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
- let currentText = textField.text ?? ""
-
- guard let range = Range(range, in: currentText) else { return false }
- let newText = currentText.replacingCharacters(in: range, with: string)
- if newText.count < currentText.count {
- return true
- }
-
- return newText.count <= LNProfileManager.nameMaxInput
- }
-
- func textFieldShouldReturn(_ textField: UITextField) -> Bool {
- textField.resignFirstResponder()
- return true
- }
-
- func textViewDidChange(_ textView: UITextView) {
- checkSaveButton()
- }
- }
- extension LNJoinUsInputInfoView {
- private func checkSaveButton() {
- let allInput = avatar.imageUrl?.isEmpty == false
- && nameInput.text?.isEmpty == false
- && gender != .unknow
- && birthday != 0
- && !curLanguages.isEmpty
- && bioInput.textView.text.isEmpty == false
-
- if allInput != applyButton.isEnabled {
- applyButton.isEnabled = allInput
- applyButton.setBackgroundImage(allInput ? .primary_8 : nil, for: .normal)
- }
- }
-
- private func setupViews() {
- onTap { [weak self] in
- guard let self else { return }
- endEditing(true)
- }
-
- scrollView.showsVerticalScrollIndicator = false
- scrollView.showsHorizontalScrollIndicator = false
- scrollView.contentInset = .init(top: 0, left: 0, bottom: -commonBottomInset + 32 + 47, right: 0)
- scrollView.adjustKeyoard()
- addSubview(scrollView)
- scrollView.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(16)
- make.top.equalToSuperview().offset(20)
- make.bottom.equalToSuperview()
- }
-
- let stackView = UIStackView()
- stackView.axis = .vertical
- stackView.spacing = 24
- scrollView.addSubview(stackView)
- stackView.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- make.width.equalToSuperview()
- }
-
- stackView.addArrangedSubview(buildAvatarView())
- stackView.addArrangedSubview(buildNickNameView())
- stackView.addArrangedSubview(buildGenderView())
- stackView.addArrangedSubview(buildBirthdayView())
- stackView.addArrangedSubview(buildLanguageView())
- stackView.addArrangedSubview(buildBioView())
-
- let bottomMenu = UIView()
- addSubview(bottomMenu)
- bottomMenu.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.bottom.equalToSuperview()
- }
-
- let bottomGradient = CAGradientLayer()
- bottomGradient.colors = [
- UIColor.white.withAlphaComponent(0).cgColor,
- UIColor.white.cgColor,
- UIColor.white.cgColor
- ]
- bottomGradient.locations = [0, 0.5, 1]
- bottomGradient.startPoint = .init(x: 0, y: 0)
- bottomGradient.endPoint = .init(x: 0, y: 1)
- bottomMenu.layer.addSublayer(bottomGradient)
- bottomMenu.publisher(for: \.bounds).removeDuplicates().sink { [weak bottomGradient] newValue in
- guard let bottomGradient else { return }
- bottomGradient.frame = newValue
- }.store(in: &cancellables)
-
- applyButton.setTitle(.init(key: "B00045"), for: .normal)
- applyButton.setTitleColor(.text_1, for: .normal)
- applyButton.titleLabel?.font = .heading_h3
- applyButton.layer.cornerRadius = 23.5
- applyButton.backgroundColor = .fill_4
- applyButton.clipsToBounds = true
- applyButton.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- guard let avatarUrl = avatar.imageUrl,
- let name = nameInput.text else {
- return
- }
-
- let config = LNJoinUsBaseInfoVO()
- config.avatar = avatarUrl
- config.nickname = name
- config.gender = gender
- config.birthday = birthday.formattedFullDate("-", normal: true)
- config.languageCodes = curLanguages.map({ $0.code })
- config.intro = bioInput.textView.text
-
- LNGameMateManager.shared.setJoinGameMateBaseInfo(info: config) { [weak self] success in
- guard let self, success else { return }
- delegate?.joinUsInputInfoViewDidFinish(view: self)
- }
- }), for: .touchUpInside)
- bottomMenu.addSubview(applyButton)
- applyButton.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(16)
- make.bottom.equalToSuperview().offset(commonBottomInset)
- make.top.equalToSuperview()
- make.height.equalTo(47)
- }
- }
-
- private func buildAvatarView() -> UIView {
- let groupView = LNJoinUsInputFieldGroupView()
- groupView.titleLabel.text = .init(key: "B00036")
- groupView.descLabel.text = .init(key: "B00037")
- groupView.showExample {
- let panel = LNJoinUsAvatarExamplePanel()
- panel.popup()
- }
-
- avatar.uploadType = .avatar
- avatar.backgroundColor = .fill_2
- avatar.layer.cornerRadius = 11
- avatar.clipsToBounds = true
- avatar.contentMode = .scaleAspectFill
- avatar.showClearButton = false
- avatar.showDefault = true
- avatar.delegate = self
- avatar.onTap { [weak self] in
- guard let self else { return }
- LNBottomSheetMenu.showImageSelectMenu(view: self, options: .init(allowEdit: true))
- { [weak self] image, _ in
- guard let self else { return }
- guard let image = image?.compress(type: .avatar) else { return }
- avatar.uploadImage(image: image)
- }
- }
- groupView.container.addSubview(avatar)
- avatar.snp.makeConstraints { make in
- make.centerX.equalToSuperview()
- make.verticalEdges.equalToSuperview()
- make.width.height.equalTo(105)
- }
- return groupView
- }
-
- private func buildNickNameView() -> UIView {
- let groupView = LNJoinUsInputFieldGroupView()
- groupView.titleLabel.text = .init(key: "A00188")
- groupView.descLabel.text = .init(key: "B00038")
-
- let container = UIView()
- container.backgroundColor = .fill_2
- container.layer.cornerRadius = 19
- groupView.container.addSubview(container)
- container.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- make.height.equalTo(38)
- }
-
- nameCountLabel.font = .body_m
- nameCountLabel.textColor = .text_2
- container.addSubview(nameCountLabel)
- nameCountLabel.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-16)
- }
-
- nameInput.font = .body_m
- nameInput.textColor = .text_5
- nameInput.placeholder = .init(key: "B00043")
- nameInput.delegate = self
- nameInput.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- nameCountLabel.text = "\(nameInput.text?.count ?? 0)/\(LNProfileManager.nameMaxInput)"
- }), for: .editingChanged)
- container.addSubview(nameInput)
- nameInput.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(16)
- make.centerY.equalToSuperview()
- make.trailing.equalTo(nameCountLabel.snp.leading).offset(-16)
- }
-
- return groupView
- }
-
- private func buildGenderView() -> UIView {
- let groupView = LNJoinUsInputFieldGroupView()
- groupView.titleLabel.text = .init(key: "A00032")
- groupView.descLabel.text = .init(key: "B00039")
-
- let stackView = UIStackView()
- stackView.spacing = 23
- stackView.distribution = .fillEqually
- groupView.container.addSubview(stackView)
- stackView.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
-
- maleButton.setImage(.icGenderMale, for: .normal)
- maleButton.setTitle(.init(key: "A00014"), for: .normal)
- maleButton.setTitleColor(.text_5, for: .normal)
- maleButton.titleLabel?.font = .heading_h4
- maleButton.backgroundColor = .primary_1
- maleButton.layer.cornerRadius = 20
- maleButton.titleEdgeInsets = .init(top: 0, left: 8, bottom: 0, right: 0)
- maleButton.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- gender = .male
- checkSaveButton()
- }), for: .touchUpInside)
- stackView.addArrangedSubview(maleButton)
- maleButton.snp.makeConstraints { make in
- make.height.equalTo(40)
- }
-
- femaleButton.setImage(.icGenderFemale, for: .normal)
- femaleButton.setTitle(.init(key: "A00015"), for: .normal)
- femaleButton.setTitleColor(.text_5, for: .normal)
- femaleButton.titleLabel?.font = .heading_h4
- femaleButton.backgroundColor = .primary_1
- femaleButton.layer.cornerRadius = 20
- femaleButton.titleEdgeInsets = .init(top: 0, left: 8, bottom: 0, right: 0)
- femaleButton.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- gender = .female
- checkSaveButton()
- }), for: .touchUpInside)
- stackView.addArrangedSubview(femaleButton)
- femaleButton.snp.makeConstraints { make in
- make.height.equalTo(40)
- }
-
- return groupView
- }
-
- private func buildBirthdayView() -> UIView {
- let groupView = LNJoinUsInputFieldGroupView()
- groupView.titleLabel.text = .init(key: "A00205")
- groupView.descLabel.text = .init(key: "B00040")
-
- let container = UIView()
- container.backgroundColor = .fill_2
- container.layer.cornerRadius = 19
- container.onTap { [weak self] in
- guard let self else { return }
- let panel = LNDatePickerPanel()
- panel.setDefault(birthday)
- panel.handler = { [weak self] date in
- guard let self else { return }
- birthday = date
- checkSaveButton()
- }
- panel.popup()
- }
- groupView.container.addSubview(container)
- container.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- make.height.equalTo(38)
- }
-
- let arrow = UIImageView.arrowImageView(size: 15)
- arrow.tintColor = .text_3
- container.addSubview(arrow)
- arrow.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-16)
- }
-
- birthdayLabel.font = .heading_h4
- birthdayLabel.textColor = .text_5
- container.addSubview(birthdayLabel)
- birthdayLabel.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(16)
- make.centerY.equalToSuperview()
- }
-
- return groupView
- }
-
- private func buildLanguageView() -> UIView {
- let groupView = LNJoinUsInputFieldGroupView()
- groupView.titleLabel.text = .init(key: "A00256")
- groupView.descLabel.isHidden = true
-
- let container = UIView()
- container.backgroundColor = .fill_2
- container.layer.cornerRadius = 19
- container.onTap { [weak self] in
- guard let self else { return }
- let panel = LNLanguageSelectPanel()
- panel.update(curSelect: curLanguages)
- panel.handler = { [weak self] languages in
- guard let self else { return }
- curLanguages = languages
- checkSaveButton()
- }
- panel.popup()
- }
- groupView.container.addSubview(container)
- container.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- make.height.equalTo(38)
- }
-
- let arrow = UIImageView.arrowImageView(size: 15)
- arrow.tintColor = .text_3
- container.addSubview(arrow)
- arrow.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview().offset(-16)
- }
-
- languageLabel.font = .heading_h4
- languageLabel.textColor = .text_5
- container.addSubview(languageLabel)
- languageLabel.snp.makeConstraints { make in
- make.leading.equalToSuperview().offset(16)
- make.centerY.equalToSuperview()
- }
-
- return groupView
- }
-
- private func buildBioView() -> UIView {
- let groupView = LNJoinUsInputFieldGroupView()
- groupView.titleLabel.text = .init(key: "B00042")
- groupView.descLabel.text = .init(key: "B00041")
-
- bioInput.placeholderLabel.text = .init(key: "B00044")
- bioInput.maxInput = LNProfileManager.bioMaxInput
- bioInput.delegate = self
- groupView.container.addSubview(bioInput)
- bioInput.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- make.height.equalTo(130)
- }
-
- return groupView
- }
- }
- #if DEBUG
- import SwiftUI
- struct LNJoinUsInputInfoViewPreview: UIViewRepresentable {
- func makeUIView(context: Context) -> some UIView {
- let container = UIView()
- container.backgroundColor = .fill
-
- let view = LNJoinUsInputInfoView()
- container.addSubview(view)
- view.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
-
- return container
- }
-
- func updateUIView(_ uiView: UIViewType, context: Context) { }
- }
- #Preview(body: {
- LNJoinUsInputInfoViewPreview()
- })
- #endif
|