// // LockDistractView.swift // QuickLocation // import UIKit import RxSwift import RxCocoa final class LockDistractView: UIView { var disposeBag = DisposeBag() let lockIconNames = [ "LockDistract/lock_icon_1", "LockDistract/lock_icon_2", "LockDistract/lock_icon_3", "LockDistract/lock_icon_4", "LockDistract/lock_icon_5", "LockDistract/lock_icon_6", "LockDistract/lock_icon_7" ] private(set) var selectedLockIconIndex = 0 private(set) var selectedAppIndexes: Set = [] private(set) var isLocked = false private let copyMaxLength = 20 override init(frame: CGRect) { super.init(frame: frame) backgroundColor = UIColor(hexStr: "#FAFAFA") setupUI() setupCopyLimit() applyLockedState() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configureMember(name: String, avatar: UIImage?) { memberNameLab.text = name.isEmpty ? " " : name memberAvatar.image = avatar ?? UIImage(named: "Common/default_avatar") } func setTodayLockCount(_ count: Int) { todayCountLab.text = "\(count)" } func setGroupName(_ name: String) { // let title = name.isEmpty ? "切换圈子" : " \(name) " // switchGroupBtn.setTitle(title, for: .normal) let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines) var titleAttributes = AttributeContainer() titleAttributes.font = .systemFont(ofSize: 12, weight: .medium) var configuration = switchGroupBtn.configuration ?? UIButton.Configuration.plain() configuration.attributedTitle = AttributedString( trimmedName.isEmpty ? "当前圈子" : trimmedName, attributes: titleAttributes ) configuration.image = UIImage(named: "Home/group_name_icon") configuration.imagePadding = 3 configuration.contentInsets = NSDirectionalEdgeInsets( top: 0, leading: 6, bottom: 0, trailing: 6 ) configuration.baseForegroundColor = UIColor(hexStr: "#293445") switchGroupBtn.configuration = configuration switchGroupBtn.titleLabel?.lineBreakMode = .byTruncatingTail switchGroupBtn.sizeToFit() } func configureLocked(_ locked: Bool) { isLocked = locked applyLockedState() } private func applyLockedState() { avatarLockView.isHidden = !isLocked styleSectionTitle.isHidden = isLocked styleCard.isHidden = isLocked lockedEmptyView.isHidden = !isLocked appCollectionView.reloadData() tipLab.text = isLocked ? "已被锁定情况下,先解锁才能锁定哟~" : "锁定功能只有在圈主的情况下才能使用哟~" lockBtn.setTitle(isLocked ? "请求解锁" : "锁定", for: .normal) } private func setupUI() { addSubview(scrollView) scrollView.addSubview(contentView) addSubview(navView) navView.addRightButton(switchGroupBtn) addSubview(footerBar) footerBar.addSubview(tipRow) footerBar.addSubview(lockBtn) contentView.addSubview(navBgView) contentView.addSubview(statsRow) statsRow.addArrangedSubview(memberCard) statsRow.addArrangedSubview(todayCard) contentView.addSubview(appsSectionTitle) contentView.addSubview(appsCard) appsCard.addSubview(appsHeaderRow) appsCard.addSubview(appCollectionView) contentView.addSubview(bottomStack) bottomStack.addArrangedSubview(styleSectionTitle) bottomStack.addArrangedSubview(styleCard) bottomStack.addArrangedSubview(lockedEmptyView) navView.layoutChain .edges(excludingEdge: .bottom) .height(kNaviHeight) footerBar.layoutChain .edgesHorzontal() .bottom() lockBtn.layoutChain .bottom(kSafeBottomMargin) .edgesHorzontal(30) .height(56) tipRow.layoutChain .centerX() .bottomToTopOfView(lockBtn, offset: -8) .top(10) scrollView.layoutChain .top() .edgesHorzontal() .bottomToTopOfView(footerBar) contentView.layoutChain .edges() .widthToView(scrollView) navBgView.layoutChain .top() .edgesHorzontal() .heightToWidth(200.0 / 375.0) statsRow.layoutChain .topToBottomOfView(navBgView, offset: -48) .edgesHorzontal(16) .height(120) appsSectionTitle.layoutChain .topToBottomOfView(statsRow, offset: 22) .left(20) appsCard.layoutChain .topToBottomOfView(appsSectionTitle, offset: 12) .edgesHorzontal(16) appsHeaderRow.layoutChain .top(16) .edgesHorzontal(14) .height(28) appCollectionView.layoutChain .topToBottomOfView(appsHeaderRow, offset: 12) .edgesHorzontal(8) .height(78) .bottom(16) bottomStack.layoutChain .topToBottomOfView(appsCard, offset: 22) .edgesHorzontal(16) .bottom(24) styleSectionTitle.layoutChain.left(4) lockedEmptyView.layoutChain .centerX() .height(180) } private func setupCopyLimit() { copyTF.rx.text.orEmpty .subscribe(onNext: { [weak self] text in guard let self else { return } if text.count > self.copyMaxLength { self.copyTF.text = String(text.prefix(self.copyMaxLength)) } let count = self.copyTF.text?.count ?? 0 self.copyCountLab.text = "\(count)/\(self.copyMaxLength)" }) .disposed(by: disposeBag) } // MARK: - Views lazy var navBgView: UIImageView = { let iv = UIImageView() iv.image = UIImage(named: "LockDistract/header_hero") iv.contentMode = .scaleAspectFill iv.clipsToBounds = true return iv }() lazy var navView: BaseNavigationView = { BaseNavigationView(title: "") }() lazy var footerBar: UIView = { let view = UIView() view.backgroundColor = UIColor(hexStr: "#FAFAFA") return view }() lazy var switchGroupBtn: UIButton = { let btn = UIButton(type: .custom) btn.backgroundColor = .white.withAlphaComponent(0.92) btn.setImage(UIImage(named: "Home/group_name_icon"), for: .normal) btn.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium) btn.setTitleColor(UIColor(hexStr: "#293445"), for: .normal) btn.cornerRadius = 8 btn.extendEdgeInsets = UIEdgeInsets(top: 20, left: 12, bottom: 12, right: 4) return btn }() lazy var scrollView: UIScrollView = { let sv = UIScrollView() sv.contentInsetAdjustmentBehavior = .never sv.showsVerticalScrollIndicator = false sv.alwaysBounceVertical = true sv.keyboardDismissMode = .onDrag return sv }() lazy var contentView = UIView() lazy var statsRow: UIStackView = { let stack = UIStackView() stack.axis = .horizontal stack.spacing = 12 stack.distribution = .fillEqually return stack }() lazy var memberCard: UIView = { let view = UIView() view.backgroundColor = .white view.cornerRadius = 16 view.addSubview(memberPrevBtn) view.addSubview(memberAvatar) view.addSubview(avatarLockView) view.addSubview(memberNextBtn) view.addSubview(memberNameChip) memberNameChip.addSubview(memberNameLab) memberAvatar.layoutChain .top(18) .centerX() .width(56) .height(56) avatarLockView.layoutChain .topToView(memberAvatar) .leftToView(memberAvatar) .rightToView(memberAvatar) .bottomToView(memberAvatar) memberPrevBtn.layoutChain .centerY(memberAvatar) .rightToLeftOfView(memberAvatar, offset: -18) .width(16) .height(16) memberNextBtn.layoutChain .centerY(memberAvatar) .leftToRightOfView(memberAvatar, offset: 18) .width(16) .height(16) memberNameChip.layoutChain .topToBottomOfView(memberAvatar, offset: 10) .centerX() .left(8, relation: .greaterThanOrEqual) .right(8, relation: .greaterThanOrEqual) .bottom(14) memberNameLab.layoutChain .top(6) .bottom(6) .left(5) .right(5) return view }() lazy var memberAvatar: UIImageView = { let iv = UIImageView(image: UIImage(named: "Common/default_avatar")) iv.contentMode = .scaleAspectFill iv.clipsToBounds = true iv.cornerRadius = 12 return iv }() lazy var avatarLockView: UIImageView = { let iv = UIImageView(image: UIImage(named: "LockDistract/avatar_lock")) iv.contentMode = .scaleAspectFill iv.clipsToBounds = true iv.cornerRadius = 12 iv.isHidden = true return iv }() lazy var memberNameChip: UIView = { let view = UIView() view.backgroundColor = UIColor(hexStr: "#F2F2F2") view.cornerRadius = 8 return view }() lazy var memberNameLab: UILabel = { let lab = UILabel() lab.text = " " lab.font = .systemFont(ofSize: 10, weight: .bold) lab.textColor = UIColor(hexStr: "#293445") lab.textAlignment = .center lab.lineBreakMode = .byTruncatingTail return lab }() lazy var memberPrevBtn: UIButton = { let btn = UIButton(type: .custom) btn.setImage(UIImage(named: "LockDistract/member_prev"), for: .normal) btn.extendEdgeInsets = UIEdgeInsets(top: 50, left: 30, bottom: 50, right: 18) return btn }() lazy var memberNextBtn: UIButton = { let btn = UIButton(type: .custom) btn.setImage(UIImage(named: "LockDistract/member_next"), for: .normal) btn.extendEdgeInsets = UIEdgeInsets(top: 50, left: 18, bottom: 50, right: 30) return btn }() lazy var todayCard: UIView = { let view = UIView() view.backgroundColor = .white view.cornerRadius = 16 let title = UILabel() title.text = "今日锁定应用" title.font = .systemFont(ofSize: 16, weight: .bold) title.textColor = UIColor(hexStr: "#293445") view.addSubview(title) title.layoutChain.top(21).left(23) view.addSubview(todayCountLab) view.addSubview(todayUnitLab) todayCountLab.layoutChain .leftToView(title, offset: 4) .bottom(10) todayUnitLab.layoutChain .leftToRightOfView(todayCountLab, offset: 7) .bottomToView(todayCountLab, offset: -12) return view }() lazy var todayCountLab: UILabel = { let lab = UILabel() lab.text = "0" lab.font = .systemFont(ofSize: 50, weight: .heavy) lab.textColor = UIColor(hexStr: "#00ADFE") return lab }() lazy var todayUnitLab: UILabel = { let lab = UILabel() lab.text = "个" lab.font = .systemFont(ofSize: 16, weight: .bold) lab.textColor = UIColor(hexStr: "#293445") return lab }() lazy var appsSectionTitle: UIView = { makeSectionTitle("锁定应用") }() lazy var appsCard: UIView = { let view = UIView() view.backgroundColor = .white view.cornerRadius = 26 return view }() lazy var appsHeaderRow: UIView = { let row = UIView() let left = UILabel() left.text = "选择锁定TA的app" left.font = .systemFont(ofSize: 14, weight: .medium) left.textColor = UIColor(hexStr: "#293445") row.addSubview(left) left.layoutChain.left().centerY() row.addSubview(notFetchedView) notFetchedView.layoutChain.right().centerY().height(28) row.layoutChain.height(28) return row }() lazy var notFetchedView: UIButton = { let btn = UIButton(type: .custom) btn.extendEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 4) let warn = UIView() warn.isUserInteractionEnabled = false warn.backgroundColor = UIColor(hexStr: "#FA5151") warn.cornerRadius = 5 let warnText = UILabel() warnText.text = "!" warnText.textColor = .white warnText.font = .systemFont(ofSize: 8) warnText.isUserInteractionEnabled = false btn.addSubview(warn) warn.addSubview(warnText) warnText.layoutChain.centerX().centerY() let notFetchedText = UILabel() notFetchedText.text = "未获取" notFetchedText.textColor = UIColor(hexStr: "#293445") notFetchedText.font = .systemFont(ofSize: 12, weight: .medium) notFetchedText.isUserInteractionEnabled = false btn.addSubview(notFetchedText) let arrow = UIImageView(image: UIImage(named: "LockDistract/arrow_right")) arrow.isUserInteractionEnabled = false btn.addSubview(arrow) warn.layoutChain .left() .centerY() .width(10).height(10) notFetchedText.layoutChain .leftToRightOfView(warn, offset: 4) .centerY() arrow.layoutChain .leftToRightOfView(notFetchedText, offset: 2) .right() .width(10) .heightToWidth(1) .centerY() return btn }() lazy var appCollectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal layout.itemSize = CGSize(width: 56, height: 78) layout.minimumLineSpacing = 12 layout.sectionInset = UIEdgeInsets(top: 0, left: 6, bottom: 0, right: 6) let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) cv.backgroundColor = .clear cv.showsHorizontalScrollIndicator = false cv.register(LockDistractAppCell.self) cv.dataSource = self cv.delegate = self cv.tag = 1 return cv }() lazy var bottomStack: UIStackView = { let stack = UIStackView() stack.axis = .vertical stack.alignment = .fill stack.spacing = 12 return stack }() lazy var styleSectionTitle: UIView = { makeSectionTitle("锁定界面样式") }() lazy var styleCard: UIView = { let card = UIView() card.backgroundColor = .white card.cornerRadius = 26 let wallLab = UILabel() wallLab.text = "锁定图标" wallLab.font = .systemFont(ofSize: 13, weight: .medium) wallLab.textColor = UIColor(hexStr: "#8A94A6") card.addSubview(wallLab) wallLab.layoutChain.top(16).left(14) card.addSubview(lockIconCV) lockIconCV.layoutChain .topToBottomOfView(wallLab, offset: 10) .edgesHorzontal(14) .height(74) let copyLab = UILabel() copyLab.text = "锁定文案" copyLab.font = .systemFont(ofSize: 13, weight: .medium) copyLab.textColor = UIColor(hexStr: "#8A94A6") card.addSubview(copyLab) copyLab.layoutChain.topToBottomOfView(lockIconCV, offset: 16).left(14) let input = UIView() input.backgroundColor = UIColor(hexStr: "#F5F6F8") input.cornerRadius = 12 card.addSubview(input) input.layoutChain .topToBottomOfView(copyLab, offset: 10) .edgesHorzontal(14) .height(44) .bottom(16) input.addSubview(copyTF) input.addSubview(copyCountLab) copyCountLab.layoutChain.centerY().right(12).width(35) copyTF.layoutChain.edgesVertical().left(12).rightToLeftOfView(copyCountLab, offset: -8) return card }() lazy var lockIconCV: UICollectionView = { let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal layout.itemSize = CGSize(width: 56, height: 74) layout.minimumLineSpacing = 10 let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) cv.backgroundColor = .clear cv.showsHorizontalScrollIndicator = false cv.register(LockDistractWallpaperCell.self) cv.dataSource = self cv.delegate = self cv.tag = 2 return cv }() lazy var copyTF: UITextField = { let tf = UITextField() tf.font = .systemFont(ofSize: 14, weight: .medium) tf.textColor = UIColor(hexStr: "#293445") tf.placeholderColor(placeholder: "随便说点什么吧~", color: UIColor(hexStr: "#999999"), font: .systemFont(ofSize: 14, weight: .medium)) return tf }() lazy var copyCountLab: UILabel = { let lab = UILabel() lab.text = "0/20" lab.font = .systemFont(ofSize: 14, weight: .medium) lab.textColor = UIColor(hexStr: "#AAAAAA") return lab }() lazy var lockedEmptyView: UIImageView = { let iv = UIImageView(image: UIImage(named: "LockDistract/locked_empty")) iv.contentMode = .scaleAspectFit iv.isHidden = true return iv }() lazy var tipLab: UILabel = { let lab = UILabel() lab.text = "锁定功能只有在圈主的情况下才能使用哟~" lab.font = .systemFont(ofSize: 12, weight: .regular) lab.textColor = UIColor(hexStr: "#00ADFE") lab.numberOfLines = 0 return lab }() lazy var tipRow: UIView = { let row = UIView() let icon = UIImageView(image: UIImage(named: "LockDistract/info_circle")) icon.contentMode = .scaleAspectFit row.addSubview(icon) row.addSubview(tipLab) icon.layoutChain.left().centerY().width(14).height(14) tipLab.layoutChain.leftToRightOfView(icon, offset: 6).right().top().bottom() return row }() lazy var lockBtn: UIButton = { let btn = UIButton(type: .custom) btn.setTitle("锁定", for: .normal) btn.setTitleColor(.white, for: .normal) btn.titleLabel?.font = FontManager.boboBold(18) btn.setBackgroundImage(UIImage(named: "Common/button_bg_2"), for: .normal) btn.cornerRadius = 20 return btn }() private func makeSectionTitle(_ text: String) -> UIView { let row = UIView() let star = UIImageView(image: UIImage(named: "LockDistract/section_star")) star.contentMode = .scaleAspectFit let lab = UILabel() lab.text = text lab.font = .systemFont(ofSize: 16, weight: .bold) lab.textColor = UIColor(hexStr: "#1F2A44") row.addSubview(star) row.addSubview(lab) star.layoutChain.left().centerY().width(14).height(14) lab.layoutChain.leftToRightOfView(star, offset: 6).right().centerY() row.layoutChain.height(22) return row } } extension LockDistractView: UICollectionViewDataSource, UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { collectionView.tag == 1 ? 1 : lockIconNames.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { if collectionView.tag == 1 { let cell = collectionView.dequeueReusableCell(for: indexPath) as LockDistractAppCell cell.configure( image: UIImage(named: "LockDistract/app_unknown"), selected: selectedAppIndexes.contains(indexPath.item), showsCheck: false ) return cell } let cell = collectionView.dequeueReusableCell(for: indexPath) as LockDistractWallpaperCell cell.configure( imageName: lockIconNames[indexPath.item], isSelected: indexPath.item == selectedLockIconIndex ) return cell } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if collectionView.tag == 1 { guard !isLocked, indexPath.item > 0 else { return } if selectedAppIndexes.contains(indexPath.item) { selectedAppIndexes.remove(indexPath.item) } else { selectedAppIndexes.insert(indexPath.item) } collectionView.reloadData() return } selectedLockIconIndex = indexPath.item collectionView.reloadData() } } final class LockDistractAppCell: UICollectionViewCell { private let iconView: UIImageView = { let iv = UIImageView() iv.contentMode = .scaleAspectFit iv.clipsToBounds = true iv.cornerRadius = 12 return iv }() private let checkBtn: UIButton = { let btn = UIButton(type: .custom) btn.setImage(UIImage(named: "Common/checkbox"), for: .normal) btn.setImage(UIImage(named: "Common/checkbox_on"), for: .selected) btn.isUserInteractionEnabled = false return btn }() override init(frame: CGRect) { super.init(frame: frame) contentView.addSubview(iconView) contentView.addSubview(checkBtn) iconView.layoutChain.top().centerX().width(48).height(48) checkBtn.layoutChain.topToBottomOfView(iconView, offset: 6).centerX().width(16).height(16) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(image: UIImage?, selected: Bool, showsCheck: Bool) { iconView.image = image checkBtn.isSelected = selected checkBtn.isHidden = !showsCheck } } final class LockDistractWallpaperCell: UICollectionViewCell { private let frameView: UIView = { let view = UIView() view.backgroundColor = UIColor(hexStr: "#F6F6F6") view.cornerRadius = 14 return view }() private let imageView: UIImageView = { let iv = UIImageView() iv.contentMode = .scaleAspectFit iv.clipsToBounds = true return iv }() override init(frame: CGRect) { super.init(frame: frame) contentView.addSubview(frameView) frameView.addSubview(imageView) frameView.layoutChain.edges() imageView.layoutChain .centerY() .centerX() .width(48) .height(48) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(imageName: String, isSelected: Bool) { imageView.image = UIImage(named: imageName) frameView.backgroundColor = UIColor(hexStr: isSelected ? "#EAF8FF" : "#F6F6F6") frameView.borderWidth = isSelected ? 1.5 : 0 frameView.borderColor = UIColor(hexStr: isSelected ? "#00ADFE" : "") } }