// // GroupSettingView.swift // QuickLocation // // Created by 八条 on 2026/6/9. // import UIKit import RxSwift import RxCocoa import TagListView final class GroupSettingView: UIView { private let pageInset: CGFloat = 15 var onRemoveMemberShortcutTapped: (() -> Void)? var onMembersTapped: (() -> Void)? func setupData(_ model: GroupInfoModel, members: [GroupMemberModel], groupNickname: String) { groupNameView.setValue(model.name) groupTypeView.setValue(model.groupTemplateName) groupDescView.setValue(model.description.isEmpty ? "暂无描述" : model.description) groupNicknameView.setValue(groupNickname) tagView.configure(tags: model.labels, editable: model.is_owner) switchBtn.isOn = model.review auditSwitchView.isHidden = !model.is_owner auditMemberView.isHidden = !model.is_owner dismissGroupView.isHidden = !model.is_owner leaveGroupView.isHidden = model.is_owner groupNameView.setShowsArrow(model.is_owner) groupTypeView.setShowsArrow(false) groupDescView.setShowsArrow(model.is_owner) groupNicknameView.setShowsArrow(true) auditMemberView.setShowsArrow(model.is_owner) let total = model.people_no > 0 ? model.people_no : members.count let online = members.filter(\.is_online).count memberCountLab.text = "共\(total)人/\(online)人在线" memberGridView.configure(members: members, showsRemoveShortcut: model.is_owner) setNeedsLayout() } func updateGroupNickname(_ nickname: String) { groupNicknameView.setValue(nickname) } override init(frame: CGRect) { super.init(frame: frame) backgroundColor = UIColor(hexStr: "#F7F8FA") memberGridView.onRemoveMemberShortcutTapped = { [weak self] in self?.onRemoveMemberShortcutTapped?() } memberGridView.onMembersTapped = { [weak self] in self?.onMembersTapped?() } setupUI() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { addSubview(navBgView) addSubview(navView) addSubview(scrollView) scrollView.addSubview(scrollContentView) scrollContentView.addSubview(contentStackView) contentStackView.addArrangedSubview(memberCardView) contentStackView.addArrangedSubview(infoCardView) contentStackView.addArrangedSubview(utilityCardView) contentStackView.addArrangedSubview(dangerCardView) setupMemberCard() setupInfoCard() setupUtilityCard() setupDangerCard() navBgView.translatesAutoresizingMaskIntoConstraints = false navView.translatesAutoresizingMaskIntoConstraints = false scrollView.translatesAutoresizingMaskIntoConstraints = false scrollContentView.translatesAutoresizingMaskIntoConstraints = false contentStackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ navBgView.topAnchor.constraint(equalTo: topAnchor), navBgView.leadingAnchor.constraint(equalTo: leadingAnchor), navBgView.trailingAnchor.constraint(equalTo: trailingAnchor), navBgView.heightAnchor.constraint(equalTo: widthAnchor, multiplier: 160.0 / 375.0), navView.topAnchor.constraint(equalTo: topAnchor), navView.leadingAnchor.constraint(equalTo: leadingAnchor), navView.trailingAnchor.constraint(equalTo: trailingAnchor), navView.heightAnchor.constraint(equalToConstant: kNaviHeight), scrollView.topAnchor.constraint(equalTo: navView.bottomAnchor), scrollView.leadingAnchor.constraint(equalTo: leadingAnchor), scrollView.trailingAnchor.constraint(equalTo: trailingAnchor), scrollView.bottomAnchor.constraint(equalTo: bottomAnchor), scrollContentView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor), scrollContentView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor), scrollContentView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor), scrollContentView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor), scrollContentView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor), contentStackView.topAnchor.constraint(equalTo: scrollContentView.topAnchor, constant: 14), contentStackView.leadingAnchor.constraint(equalTo: scrollContentView.leadingAnchor, constant: pageInset), contentStackView.trailingAnchor.constraint(equalTo: scrollContentView.trailingAnchor, constant: -pageInset), contentStackView.bottomAnchor.constraint(equalTo: scrollContentView.bottomAnchor, constant: -30) ]) } private func setupMemberCard() { memberCardView.addSubview(memberHeaderView) memberCardView.addSubview(memberGridView) memberCardView.addSubview(memberSeparatorView) memberCardView.addSubview(memberActionsView) memberHeaderView.addSubview(memberTitleLab) memberHeaderView.addSubview(memberCountLab) memberHeaderView.addSubview(memberHeaderArrowView) memberActionsView.addSubview(inviteCodeButton) memberActionsView.addSubview(actionDividerView) memberActionsView.addSubview(shareButton) let views = [memberHeaderView, memberGridView, memberSeparatorView, memberActionsView, memberTitleLab, memberCountLab, memberHeaderArrowView, inviteCodeButton, actionDividerView, shareButton] views.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } NSLayoutConstraint.activate([ memberHeaderView.topAnchor.constraint(equalTo: memberCardView.topAnchor), memberHeaderView.leadingAnchor.constraint(equalTo: memberCardView.leadingAnchor), memberHeaderView.trailingAnchor.constraint(equalTo: memberCardView.trailingAnchor), memberHeaderView.heightAnchor.constraint(equalToConstant: 44), memberTitleLab.leadingAnchor.constraint(equalTo: memberHeaderView.leadingAnchor, constant: 16), memberTitleLab.centerYAnchor.constraint(equalTo: memberHeaderView.centerYAnchor), memberCountLab.trailingAnchor.constraint(equalTo: memberHeaderArrowView.leadingAnchor, constant: -8), memberCountLab.centerYAnchor.constraint(equalTo: memberHeaderView.centerYAnchor), memberHeaderArrowView.trailingAnchor.constraint(equalTo: memberHeaderView.trailingAnchor, constant: -16), memberHeaderArrowView.centerYAnchor.constraint(equalTo: memberHeaderView.centerYAnchor), memberHeaderArrowView.widthAnchor.constraint(equalToConstant: 16), memberHeaderArrowView.heightAnchor.constraint(equalToConstant: 16), memberGridView.topAnchor.constraint(equalTo: memberHeaderView.bottomAnchor), memberGridView.leadingAnchor.constraint(equalTo: memberCardView.leadingAnchor, constant: 20), memberGridView.trailingAnchor.constraint(equalTo: memberCardView.trailingAnchor, constant: -20), memberSeparatorView.topAnchor.constraint(equalTo: memberGridView.bottomAnchor, constant: 6), memberSeparatorView.leadingAnchor.constraint(equalTo: memberCardView.leadingAnchor, constant: 16), memberSeparatorView.trailingAnchor.constraint(equalTo: memberCardView.trailingAnchor, constant: -16), memberSeparatorView.heightAnchor.constraint(equalToConstant: 1), memberActionsView.topAnchor.constraint(equalTo: memberSeparatorView.bottomAnchor, constant: 4), memberActionsView.leadingAnchor.constraint(equalTo: memberCardView.leadingAnchor), memberActionsView.trailingAnchor.constraint(equalTo: memberCardView.trailingAnchor), memberActionsView.bottomAnchor.constraint(equalTo: memberCardView.bottomAnchor, constant: -6), memberActionsView.heightAnchor.constraint(equalToConstant: 40), inviteCodeButton.leadingAnchor.constraint(equalTo: memberActionsView.leadingAnchor), inviteCodeButton.topAnchor.constraint(equalTo: memberActionsView.topAnchor), inviteCodeButton.bottomAnchor.constraint(equalTo: memberActionsView.bottomAnchor), inviteCodeButton.trailingAnchor.constraint(equalTo: actionDividerView.leadingAnchor), actionDividerView.centerXAnchor.constraint(equalTo: memberActionsView.centerXAnchor), actionDividerView.centerYAnchor.constraint(equalTo: memberActionsView.centerYAnchor), actionDividerView.widthAnchor.constraint(equalToConstant: 0.5), actionDividerView.heightAnchor.constraint(equalToConstant: 20), shareButton.leadingAnchor.constraint(equalTo: actionDividerView.trailingAnchor), shareButton.trailingAnchor.constraint(equalTo: memberActionsView.trailingAnchor), shareButton.topAnchor.constraint(equalTo: memberActionsView.topAnchor), shareButton.bottomAnchor.constraint(equalTo: memberActionsView.bottomAnchor) ]) } private func setupInfoCard() { infoCardView.addSubview(infoStackView) infoStackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ infoStackView.topAnchor.constraint(equalTo: infoCardView.topAnchor), infoStackView.leadingAnchor.constraint(equalTo: infoCardView.leadingAnchor), infoStackView.trailingAnchor.constraint(equalTo: infoCardView.trailingAnchor), infoStackView.bottomAnchor.constraint(equalTo: infoCardView.bottomAnchor) ]) infoStackView.addArrangedSubview(groupNameView) infoStackView.addArrangedSubview(groupTypeView) infoStackView.addArrangedSubview(groupDescView) infoStackView.addArrangedSubview(groupNicknameView) infoStackView.addArrangedSubview(auditSwitchView) infoStackView.addArrangedSubview(auditMemberView) infoStackView.addArrangedSubview(tagView) } private func setupUtilityCard() { utilityCardView.addSubview(clearHistoryView) clearHistoryView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate(clearHistoryView.edgesToSuperview()) } private func setupDangerCard() { dangerCardView.addSubview(dangerStackView) dangerStackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate(dangerStackView.edgesToSuperview()) dangerStackView.addArrangedSubview(dismissGroupView) dangerStackView.addArrangedSubview(leaveGroupView) } private func makeMemberActionButton(title: String, imageName: String) -> UIButton { let button = UIButton(type: .custom) let stackView = UIStackView() stackView.axis = .horizontal stackView.alignment = .center stackView.spacing = 7 stackView.isUserInteractionEnabled = false let imageView = UIImageView(image: UIImage(named: imageName)) imageView.contentMode = .scaleAspectFit imageView.widthAnchor.constraint(equalToConstant: 18).isActive = true imageView.heightAnchor.constraint(equalToConstant: 18).isActive = true let titleLab = UILabel() titleLab.text = title titleLab.textColor = UIColor(hexStr: "#293445") titleLab.font = .systemFont(ofSize: 16, weight: .medium) stackView.addArrangedSubview(imageView) stackView.addArrangedSubview(titleLab) button.addSubview(stackView) stackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ stackView.centerXAnchor.constraint(equalTo: button.centerXAnchor), stackView.centerYAnchor.constraint(equalTo: button.centerYAnchor) ]) return button } lazy var navBgView: UIImageView = { let view = UIImageView(image: UIImage(named: "Common/navBar_bg_2")) view.contentMode = .scaleAspectFill return view }() lazy var navView: BaseNavigationView = BaseNavigationView(title: "圈子详情") private lazy var scrollView: UIScrollView = { let view = UIScrollView() view.backgroundColor = .clear view.showsVerticalScrollIndicator = false return view }() private lazy var scrollContentView = UIView() private lazy var contentStackView: UIStackView = { let view = UIStackView() view.axis = .vertical view.spacing = 16 view.alignment = .fill view.distribution = .fill return view }() // MARK: - 圈子成员 private lazy var memberCardView = makeCardView() let memberHeaderView = UIView() private lazy var memberTitleLab: UILabel = { let label = UILabel() label.text = "圈子成员" label.textColor = UIColor(hexStr: "#293445") label.font = .systemFont(ofSize: 17, weight: .semibold) return label }() private lazy var memberCountLab: UILabel = { let label = UILabel() label.textColor = UIColor(hexStr: "#A5A8AE") label.font = .systemFont(ofSize: 13, weight: .regular) return label }() private lazy var memberHeaderArrowView: UIImageView = makeArrowView() private let memberGridView = GroupSettingMemberGridView() private let memberSeparatorView = HorizontalDashLineView() private let memberActionsView = UIView() lazy var inviteCodeButton: UIButton = makeMemberActionButton(title: "邀请码", imageName: "GroupSetting/invite_code") lazy var shareButton: UIButton = makeMemberActionButton(title: "分享", imageName: "GroupSetting/share") private lazy var actionDividerView: UIView = { let view = UIView() view.backgroundColor = UIColor(hexStr: "#E7E8EA") return view }() // MARK: - 圈子信息 private lazy var infoCardView = makeCardView() private lazy var infoStackView: UIStackView = { let view = UIStackView() view.axis = .vertical view.spacing = 0 view.alignment = .fill view.distribution = .fill return view }() let groupNameView = GroupSettingInfoRow(title: "圈子昵称", style: .text) let groupTypeView = GroupSettingInfoRow(title: "圈子类型", style: .text) let groupDescView = GroupSettingInfoRow(title: "圈子描述", style: .multilineText) let groupNicknameView = GroupSettingInfoRow(title: "我在本群昵称", style: .text) let auditSwitchView = GroupSettingInfoRow(title: "审核开关", style: .toggle) let auditMemberView = GroupSettingInfoRow(title: "审核成员", style: .text) let tagView = GroupSettingTagRow() var switchBtn: UISwitch { auditSwitchView.switchButton } // MARK: - 操作 private lazy var utilityCardView = makeCardView() let clearHistoryView = GroupSettingActionRow(title: "清空聊天记录") private lazy var dangerCardView = makeCardView() private lazy var dangerStackView: UIStackView = { let view = UIStackView() view.axis = .vertical view.spacing = 0 view.alignment = .fill view.distribution = .fill return view }() let dismissGroupView = GroupSettingActionRow(title: "解散圈子", titleColor: UIColor(hexStr: "#FF4D53"), hidesDivider: true) let leaveGroupView = GroupSettingActionRow(title: "退出圈子", titleColor: UIColor(hexStr: "#FF4D53"), hidesDivider: true) private func makeCardView() -> UIView { let view = UIView() view.backgroundColor = .white view.layer.cornerRadius = 12 view.clipsToBounds = true return view } private func makeArrowView() -> UIImageView { let view = UIImageView(image: UIImage(named: "GroupSetting/arrow_right")) view.contentMode = .scaleAspectFit return view } } private final class GroupSettingMemberGridView: UIView { private let itemWidth: CGFloat = 56 private let itemHeight: CGFloat = 62 var onRemoveMemberShortcutTapped: (() -> Void)? var onMembersTapped: (() -> Void)? private weak var removeMemberCell: GroupSettingRemoveMemberCell? private lazy var scrollView: UIScrollView = { let view = UIScrollView() view.showsHorizontalScrollIndicator = false view.showsVerticalScrollIndicator = false view.alwaysBounceHorizontal = true view.delaysContentTouches = false return view }() private lazy var itemsStackView: UIStackView = { let view = UIStackView() view.axis = .horizontal view.spacing = 12 view.alignment = .top view.distribution = .fill return view }() override init(frame: CGRect) { super.init(frame: frame) addSubview(scrollView) scrollView.addSubview(itemsStackView) let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap)) tap.cancelsTouchesInView = false scrollView.addGestureRecognizer(tap) scrollView.translatesAutoresizingMaskIntoConstraints = false itemsStackView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: topAnchor), scrollView.leadingAnchor.constraint(equalTo: leadingAnchor), scrollView.trailingAnchor.constraint(equalTo: trailingAnchor), scrollView.bottomAnchor.constraint(equalTo: bottomAnchor), itemsStackView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor), itemsStackView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor), itemsStackView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor), itemsStackView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor), itemsStackView.heightAnchor.constraint(equalTo: scrollView.frameLayoutGuide.heightAnchor) ]) heightAnchor.constraint(equalToConstant: itemHeight).isActive = true } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(members: [GroupMemberModel], showsRemoveShortcut: Bool) { itemsStackView.arrangedSubviews.forEach { itemsStackView.removeArrangedSubview($0) $0.removeFromSuperview() } removeMemberCell = nil members.forEach { member in let cell = GroupSettingMemberCell(member: member) cell.widthAnchor.constraint(equalToConstant: itemWidth).isActive = true itemsStackView.addArrangedSubview(cell) } if showsRemoveShortcut { let removeCell = GroupSettingRemoveMemberCell() removeMemberCell = removeCell removeCell.widthAnchor.constraint(equalToConstant: itemWidth).isActive = true itemsStackView.addArrangedSubview(removeCell) } } @objc private func handleTap(_ gesture: UITapGestureRecognizer) { if let removeMemberCell { let point = gesture.location(in: removeMemberCell) if removeMemberCell.bounds.contains(point) { onRemoveMemberShortcutTapped?() return } } onMembersTapped?() } } private final class GroupSettingMemberCell: UIView { init(member: GroupMemberModel) { super.init(frame: .zero) setupUI() avatarView.image = member.userIcon nicknameLab.text = member.nick_name.isEmpty ? "未知昵称" : member.nick_name } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { addSubview(avatarView) addSubview(nicknameLab) avatarView.translatesAutoresizingMaskIntoConstraints = false nicknameLab.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ avatarView.topAnchor.constraint(equalTo: topAnchor), avatarView.centerXAnchor.constraint(equalTo: centerXAnchor), avatarView.widthAnchor.constraint(equalToConstant: 40), avatarView.heightAnchor.constraint(equalToConstant: 40), nicknameLab.topAnchor.constraint(equalTo: avatarView.bottomAnchor, constant: 5), nicknameLab.leadingAnchor.constraint(equalTo: leadingAnchor), nicknameLab.trailingAnchor.constraint(equalTo: trailingAnchor) ]) } private lazy var avatarView: UIImageView = { let view = UIImageView() view.contentMode = .scaleAspectFill view.layer.cornerRadius = 11 view.clipsToBounds = true return view }() private lazy var nicknameLab: UILabel = { let label = UILabel() label.textColor = UIColor(hexStr: "#81858C") label.font = .systemFont(ofSize: 11, weight: .regular) label.textAlignment = .center label.lineBreakMode = .byTruncatingTail return label }() } private final class GroupSettingRemoveMemberCell: UIView { override init(frame: CGRect) { super.init(frame: frame) addSubview(iconContainer) addSubview(titleLab) iconContainer.addSubview(iconView) [iconContainer, titleLab, iconView].forEach { $0.translatesAutoresizingMaskIntoConstraints = false } NSLayoutConstraint.activate([ iconContainer.topAnchor.constraint(equalTo: topAnchor), iconContainer.centerXAnchor.constraint(equalTo: centerXAnchor), iconContainer.widthAnchor.constraint(equalToConstant: 40), iconContainer.heightAnchor.constraint(equalToConstant: 40), iconView.centerXAnchor.constraint(equalTo: iconContainer.centerXAnchor), iconView.centerYAnchor.constraint(equalTo: iconContainer.centerYAnchor), iconView.widthAnchor.constraint(equalToConstant: 16), iconView.heightAnchor.constraint(equalToConstant: 16), titleLab.topAnchor.constraint(equalTo: iconContainer.bottomAnchor, constant: 5), titleLab.leadingAnchor.constraint(equalTo: leadingAnchor), titleLab.trailingAnchor.constraint(equalTo: trailingAnchor) ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private lazy var iconContainer: UIView = { let view = UIView() view.backgroundColor = UIColor(hexStr: "#F6F4F5") view.layer.cornerRadius = 12 return view }() private lazy var iconView: UIImageView = { let view = UIImageView(image: UIImage(named: "GroupSetting/remove_member")) view.contentMode = .scaleAspectFit return view }() private lazy var titleLab: UILabel = { let label = UILabel() label.text = "移除" label.textColor = UIColor(hexStr: "#81858C") label.font = .systemFont(ofSize: 11, weight: .regular) label.textAlignment = .center return label }() } final class GroupSettingInfoRow: UIView { enum Style { case text case icon case multilineText case toggle } let switchButton = UISwitch() private let style: Style private let titleLab = UILabel() private let valueLab = UILabel() private let valueImageView = UIImageView() private let arrowImageView = UIImageView(image: UIImage(named: "GroupSetting/arrow_right")) private let dividerView = UIView() private var valueTrailingToArrow: NSLayoutConstraint? private var valueTrailingToRow: NSLayoutConstraint? private var imageTrailingToArrow: NSLayoutConstraint? private var imageTrailingToRow: NSLayoutConstraint? init(title: String, style: Style) { self.style = style super.init(frame: .zero) titleLab.text = title setupUI() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func setValue(_ value: String) { valueLab.text = value } func setImage(_ image: UIImage?) { valueImageView.image = image } func setShowsArrow(_ showsArrow: Bool) { guard style != .toggle else { return } arrowImageView.isHidden = !showsArrow valueTrailingToArrow?.isActive = showsArrow valueTrailingToRow?.isActive = !showsArrow imageTrailingToArrow?.isActive = showsArrow imageTrailingToRow?.isActive = !showsArrow } private func setupUI() { backgroundColor = .clear titleLab.textColor = UIColor(hexStr: "#293445") titleLab.font = .systemFont(ofSize: 16, weight: .regular) titleLab.setContentHuggingPriority(.required, for: .horizontal) titleLab.setContentCompressionResistancePriority(.required, for: .horizontal) valueLab.textColor = UIColor(hexStr: "#4B5565") valueLab.font = .systemFont(ofSize: 16, weight: .regular) valueLab.textAlignment = .right valueLab.lineBreakMode = .byTruncatingTail valueLab.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) valueImageView.contentMode = .scaleAspectFill valueImageView.layer.cornerRadius = 10 valueImageView.clipsToBounds = true arrowImageView.contentMode = .scaleAspectFit dividerView.backgroundColor = UIColor(hexStr: "#EDEEF0") switchButton.onTintColor = UIColor(hexStr: "#16B3FF") [titleLab, valueLab, valueImageView, arrowImageView, switchButton, dividerView].forEach { addSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false } NSLayoutConstraint.activate([ titleLab.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), arrowImageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), arrowImageView.centerYAnchor.constraint(equalTo: centerYAnchor), arrowImageView.widthAnchor.constraint(equalToConstant: 16), arrowImageView.heightAnchor.constraint(equalToConstant: 16), dividerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), dividerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), dividerView.bottomAnchor.constraint(equalTo: bottomAnchor), dividerView.heightAnchor.constraint(equalToConstant: 0.5) ]) switch style { case .text: setupTextLayout(multiline: false) case .icon: setupIconLayout() case .multilineText: setupTextLayout(multiline: true) case .toggle: setupToggleLayout() } } private func setupTextLayout(multiline: Bool) { valueImageView.isHidden = true switchButton.isHidden = true valueTrailingToArrow = valueLab.trailingAnchor.constraint(equalTo: arrowImageView.leadingAnchor, constant: -10) valueTrailingToRow = valueLab.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16) valueTrailingToRow?.isActive = true arrowImageView.isHidden = true if multiline { valueLab.numberOfLines = 2 valueLab.textAlignment = .right NSLayoutConstraint.activate([ titleLab.centerYAnchor.constraint(equalTo: centerYAnchor), valueLab.leadingAnchor.constraint(equalTo: titleLab.trailingAnchor, constant: 20), valueLab.centerYAnchor.constraint(equalTo: centerYAnchor), valueLab.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 12), valueLab.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -12), heightAnchor.constraint(greaterThanOrEqualToConstant: 72) ]) } else { NSLayoutConstraint.activate([ titleLab.centerYAnchor.constraint(equalTo: centerYAnchor), valueLab.leadingAnchor.constraint(greaterThanOrEqualTo: titleLab.trailingAnchor, constant: 20), valueLab.centerYAnchor.constraint(equalTo: centerYAnchor), heightAnchor.constraint(equalToConstant: 62) ]) } } private func setupIconLayout() { valueLab.isHidden = true switchButton.isHidden = true arrowImageView.isHidden = true imageTrailingToArrow = valueImageView.trailingAnchor.constraint(equalTo: arrowImageView.leadingAnchor, constant: -12) imageTrailingToRow = valueImageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16) imageTrailingToRow?.isActive = true NSLayoutConstraint.activate([ titleLab.centerYAnchor.constraint(equalTo: centerYAnchor), valueImageView.centerYAnchor.constraint(equalTo: centerYAnchor), valueImageView.widthAnchor.constraint(equalToConstant: 40), valueImageView.heightAnchor.constraint(equalToConstant: 40), heightAnchor.constraint(equalToConstant: 64) ]) } private func setupToggleLayout() { valueLab.isHidden = true valueImageView.isHidden = true arrowImageView.isHidden = true NSLayoutConstraint.activate([ titleLab.centerYAnchor.constraint(equalTo: centerYAnchor), switchButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), switchButton.centerYAnchor.constraint(equalTo: centerYAnchor), heightAnchor.constraint(equalToConstant: 62) ]) } } final class GroupSettingTagRow: UIView { private let titleLab = UILabel() private let arrowImageView = UIImageView(image: UIImage(named: "GroupSetting/arrow_right")) private let tagListView = TagListView() private let dividerView = UIView() private var tagTrailingToArrow: NSLayoutConstraint? private var tagTrailingToRow: NSLayoutConstraint? init() { super.init(frame: .zero) setupUI() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func configure(tags: [String], editable: Bool) { tagListView.removeAllTags() tagListView.addTags(tags) tagListView.tagViews.forEach { $0.layer.cornerRadius = 7 } arrowImageView.isHidden = !editable tagTrailingToArrow?.isActive = editable tagTrailingToRow?.isActive = !editable tagListView.invalidateIntrinsicContentSize() } private func setupUI() { titleLab.text = "圈子标签" titleLab.textColor = UIColor(hexStr: "#293445") titleLab.font = .systemFont(ofSize: 16, weight: .regular) titleLab.setContentHuggingPriority(.required, for: .horizontal) titleLab.setContentCompressionResistancePriority(.required, for: .horizontal) arrowImageView.contentMode = .scaleAspectFit arrowImageView.isHidden = true dividerView.backgroundColor = UIColor(hexStr: "#EDEEF0") tagListView.textFont = .systemFont(ofSize: 14, weight: .regular) tagListView.textColor = UIColor(hexStr: "#16B3FF") tagListView.tagBackgroundColor = UIColor(hexStr: "#E4F6FF") tagListView.paddingX = 12 tagListView.paddingY = 5 tagListView.alignment = .right tagListView.setContentHuggingPriority(.defaultLow, for: .horizontal) tagListView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) [titleLab, arrowImageView, tagListView, dividerView].forEach { addSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false } tagTrailingToArrow = tagListView.trailingAnchor.constraint(equalTo: arrowImageView.leadingAnchor, constant: -10) tagTrailingToRow = tagListView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16) tagTrailingToRow?.isActive = true NSLayoutConstraint.activate([ titleLab.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), titleLab.topAnchor.constraint(equalTo: topAnchor, constant: 18), arrowImageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), arrowImageView.centerYAnchor.constraint(equalTo: titleLab.centerYAnchor), arrowImageView.widthAnchor.constraint(equalToConstant: 16), arrowImageView.heightAnchor.constraint(equalToConstant: 16), tagListView.leadingAnchor.constraint(equalTo: titleLab.trailingAnchor, constant: 20), tagListView.topAnchor.constraint(equalTo: topAnchor, constant: 12), tagListView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -12), tagListView.heightAnchor.constraint(greaterThanOrEqualToConstant: 28), heightAnchor.constraint(greaterThanOrEqualToConstant: 62), dividerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), dividerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), dividerView.bottomAnchor.constraint(equalTo: bottomAnchor), dividerView.heightAnchor.constraint(equalToConstant: 0.5) ]) } } final class GroupSettingActionRow: UIView { init(title: String, titleColor: UIColor = UIColor(hexStr: "#293445"), hidesDivider: Bool = false) { super.init(frame: .zero) titleLab.text = title titleLab.textColor = titleColor dividerView.isHidden = hidesDivider setupUI() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupUI() { titleLab.font = .systemFont(ofSize: 16, weight: .regular) arrowImageView.contentMode = .scaleAspectFit dividerView.backgroundColor = UIColor(hexStr: "#EDEEF0") [titleLab, arrowImageView, dividerView].forEach { addSubview($0) $0.translatesAutoresizingMaskIntoConstraints = false } NSLayoutConstraint.activate([ titleLab.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), titleLab.centerYAnchor.constraint(equalTo: centerYAnchor), arrowImageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), arrowImageView.centerYAnchor.constraint(equalTo: centerYAnchor), arrowImageView.widthAnchor.constraint(equalToConstant: 16), arrowImageView.heightAnchor.constraint(equalToConstant: 16), dividerView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16), dividerView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16), dividerView.bottomAnchor.constraint(equalTo: bottomAnchor), dividerView.heightAnchor.constraint(equalToConstant: 0.5), heightAnchor.constraint(equalToConstant: 62) ]) } private let titleLab = UILabel() private let arrowImageView = UIImageView(image: UIImage(named: "GroupSetting/arrow_right")) private let dividerView = UIView() } private final class GroupSettingDashedLineView: UIView { override func draw(_ rect: CGRect) { guard let context = UIGraphicsGetCurrentContext() else { return } context.setStrokeColor(UIColor(hexStr: "#D8D8D8").cgColor) context.setLineWidth(1 / UIScreen.main.scale) context.setLineDash(phase: 0, lengths: [4, 4]) context.move(to: CGPoint(x: 0, y: rect.midY)) context.addLine(to: CGPoint(x: rect.maxX, y: rect.midY)) context.strokePath() } } private extension UIView { func edgesToSuperview() -> [NSLayoutConstraint] { guard let superview = superview else { return [] } return [ topAnchor.constraint(equalTo: superview.topAnchor), leadingAnchor.constraint(equalTo: superview.leadingAnchor), trailingAnchor.constraint(equalTo: superview.trailingAnchor), bottomAnchor.constraint(equalTo: superview.bottomAnchor) ] } }