384 lines
14 KiB
Swift
384 lines
14 KiB
Swift
//
|
|
// MemberInfoView.swift
|
|
// QuickLocation
|
|
//
|
|
|
|
import UIKit
|
|
import Kingfisher
|
|
|
|
final class MemberInfoView: UIView {
|
|
|
|
lazy var navView = BaseNavigationView(title: "成员信息")
|
|
|
|
lazy var memberCV: UICollectionView = {
|
|
let layout = UICollectionViewFlowLayout()
|
|
layout.scrollDirection = .horizontal
|
|
layout.itemSize = CGSize(width: 61, height: 90)
|
|
layout.minimumLineSpacing = 12
|
|
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
|
cv.backgroundColor = .white
|
|
cv.showsHorizontalScrollIndicator = false
|
|
cv.contentInset = UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12)
|
|
cv.register(GroupMemberListCell.self)
|
|
cv.layer.cornerRadius = 20
|
|
return cv
|
|
}()
|
|
|
|
let scrollView = UIScrollView()
|
|
let contentView = UIView()
|
|
let detailCard = UIView()
|
|
private let rowsStack = UIStackView()
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
backgroundColor = UIColor(hexStr: "#F7F7F7")
|
|
setupUI()
|
|
}
|
|
|
|
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
|
|
|
private func setupUI() {
|
|
addSubview(navView)
|
|
navView.layoutChain.top().edgesHorzontal().height(kNaviHeight)
|
|
|
|
addSubview(scrollView)
|
|
scrollView.layoutChain.topToBottomOfView(navView).edgesHorzontal().bottom()
|
|
scrollView.addSubview(contentView)
|
|
contentView.layoutChain.edges().widthToView(scrollView)
|
|
|
|
contentView.addSubview(memberCV)
|
|
memberCV.layoutChain.top(8).edgesHorzontal(15).height(100)
|
|
|
|
detailCard.backgroundColor = .white
|
|
detailCard.layer.cornerRadius = 16
|
|
contentView.addSubview(detailCard)
|
|
detailCard.layoutChain
|
|
.topToBottomOfView(memberCV, offset: 12)
|
|
.edgesHorzontal(15)
|
|
.bottom(24)
|
|
|
|
rowsStack.axis = .vertical
|
|
rowsStack.spacing = 0
|
|
detailCard.addSubview(rowsStack)
|
|
rowsStack.layoutChain.edges(UIEdgeInsets(top: 4, left: 0, bottom: 4, right: 0))
|
|
}
|
|
|
|
func renderRows(_ rows: [MemberInfoRow]) {
|
|
rowsStack.arrangedSubviews.forEach {
|
|
rowsStack.removeArrangedSubview($0)
|
|
$0.removeFromSuperview()
|
|
}
|
|
for (i, row) in rows.enumerated() {
|
|
let view = MemberInfoRowView(row: row)
|
|
rowsStack.addArrangedSubview(view)
|
|
view.layoutChain.height(row.kind == .tag ? 56 : 52)
|
|
if i < rows.count - 1 {
|
|
let line = UIView()
|
|
line.backgroundColor = UIColor(hexStr: "#F2F2F2")
|
|
view.addSubview(line)
|
|
line.layoutChain.left(15).right(15).bottom(0).height(0.5)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
enum MemberInfoRowKind {
|
|
case text
|
|
case avatar
|
|
case tag
|
|
case disclosure
|
|
}
|
|
|
|
struct MemberInfoRow {
|
|
let title: String
|
|
let value: String
|
|
let kind: MemberInfoRowKind
|
|
let action: (() -> Void)?
|
|
let avatarURL: String?
|
|
let showsMoreTags: Bool
|
|
|
|
init(title: String,
|
|
value: String,
|
|
kind: MemberInfoRowKind = .text,
|
|
avatarURL: String? = nil,
|
|
showsMoreTags: Bool = false,
|
|
action: (() -> Void)? = nil) {
|
|
self.title = title
|
|
self.value = value
|
|
self.kind = kind
|
|
self.avatarURL = avatarURL
|
|
self.showsMoreTags = showsMoreTags
|
|
self.action = action
|
|
}
|
|
}
|
|
|
|
final class MemberInfoRowView: UIControl {
|
|
private let titleLab = UILabel()
|
|
private let valueLab = UILabel()
|
|
private let arrow = UIImageView(image: UIImage(named: "Mine/arrow_right"))
|
|
private let avatar = UIImageView()
|
|
private let actionHandler: (() -> Void)?
|
|
|
|
init(row: MemberInfoRow) {
|
|
self.actionHandler = row.action
|
|
super.init(frame: .zero)
|
|
titleLab.text = row.title
|
|
titleLab.font = .systemFont(ofSize: 14, weight: .medium)
|
|
titleLab.textColor = UIColor(hexStr: "#293445")
|
|
addSubview(titleLab)
|
|
titleLab.layoutChain.left(15).centerY()
|
|
|
|
valueLab.text = row.value
|
|
valueLab.font = .systemFont(ofSize: 14, weight: .medium)
|
|
valueLab.textColor = row.value == "未设置" || row.value == "未绑定" || row.value == "保密"
|
|
? UIColor(hexStr: "#AAAAAA")
|
|
: UIColor(hexStr: "#293445")
|
|
valueLab.textAlignment = .right
|
|
|
|
arrow.contentMode = .scaleAspectFit
|
|
avatar.contentMode = .scaleAspectFill
|
|
avatar.clipsToBounds = true
|
|
avatar.layer.cornerRadius = 14
|
|
|
|
switch row.kind {
|
|
case .text:
|
|
addSubview(valueLab)
|
|
valueLab.layoutChain.right(15).centerY().leftToRightOfView(titleLab, offset: 12)
|
|
case .disclosure:
|
|
addSubview(arrow)
|
|
arrow.layoutChain.right(15).centerY().width(12).height(12)
|
|
addSubview(valueLab)
|
|
valueLab.layoutChain.rightToLeftOfView(arrow, offset: -8).centerY().leftToRightOfView(titleLab, offset: 12)
|
|
case .avatar:
|
|
addSubview(arrow)
|
|
arrow.layoutChain.right(15).centerY().width(12).height(12)
|
|
addSubview(avatar)
|
|
avatar.layoutChain.rightToLeftOfView(arrow, offset: -8).centerY().width(28).height(28)
|
|
if let pic = row.avatarURL, !pic.isEmpty, let img = UIImage(named: "UserIcon/\(pic)") {
|
|
avatar.image = img
|
|
} else if let urlStr = row.avatarURL, let u = URL(string: urlStr), urlStr.hasPrefix("http") {
|
|
avatar.kf.setImage(with: u, placeholder: UIImage(named: "Common/default_avatar"))
|
|
} else {
|
|
avatar.image = UIImage(named: "Common/default_avatar")
|
|
}
|
|
case .tag:
|
|
setupGroupTags(row: row)
|
|
}
|
|
|
|
if row.action != nil {
|
|
isAccessibilityElement = true
|
|
accessibilityTraits = .button
|
|
addTarget(self, action: #selector(tap), for: .touchUpInside)
|
|
}
|
|
}
|
|
|
|
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
|
|
|
private func setupGroupTags(row: MemberInfoRow) {
|
|
let stack = UIStackView()
|
|
stack.axis = .horizontal
|
|
stack.alignment = .center
|
|
stack.spacing = 8
|
|
// The whole row owns the action; its display-only tags must not consume touches.
|
|
stack.isUserInteractionEnabled = false
|
|
addSubview(stack)
|
|
|
|
if row.showsMoreTags {
|
|
let moreLabel = UILabel()
|
|
moreLabel.text = "..."
|
|
moreLabel.font = .systemFont(ofSize: 14, weight: .medium)
|
|
moreLabel.textColor = UIColor(hexStr: "#D5923F")
|
|
moreLabel.backgroundColor = UIColor(hexStr: "#FFF9DD")
|
|
moreLabel.textAlignment = .center
|
|
moreLabel.layer.cornerRadius = 7
|
|
moreLabel.clipsToBounds = true
|
|
stack.addArrangedSubview(moreLabel)
|
|
moreLabel.layoutChain.width(26).height(26)
|
|
}
|
|
|
|
let nameLabel = MemberInfoTagLabel()
|
|
nameLabel.text = row.value
|
|
nameLabel.font = .systemFont(ofSize: 14, weight: .medium)
|
|
nameLabel.textColor = UIColor(hexStr: "#D5923F")
|
|
nameLabel.backgroundColor = UIColor(hexStr: "#FFF9DD")
|
|
nameLabel.textAlignment = .center
|
|
nameLabel.numberOfLines = 1
|
|
nameLabel.lineBreakMode = .byTruncatingTail
|
|
nameLabel.layer.cornerRadius = 7
|
|
nameLabel.clipsToBounds = true
|
|
nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
stack.addArrangedSubview(nameLabel)
|
|
nameLabel.layoutChain.height(26)
|
|
nameLabel.widthAnchor.constraint(lessThanOrEqualToConstant: 160).isActive = true
|
|
|
|
if row.showsMoreTags {
|
|
arrow.contentMode = .scaleAspectFit
|
|
stack.addArrangedSubview(arrow)
|
|
arrow.layoutChain.width(12).height(12)
|
|
}
|
|
|
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
NSLayoutConstraint.activate([
|
|
stack.centerYAnchor.constraint(equalTo: centerYAnchor),
|
|
stack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15),
|
|
stack.leadingAnchor.constraint(greaterThanOrEqualTo: titleLab.trailingAnchor, constant: 12)
|
|
])
|
|
}
|
|
|
|
@objc private func tap() { actionHandler?() }
|
|
}
|
|
|
|
private final class MemberInfoTagLabel: UILabel {
|
|
private let insets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
|
|
|
|
override func drawText(in rect: CGRect) {
|
|
super.drawText(in: rect.inset(by: insets))
|
|
}
|
|
|
|
override var intrinsicContentSize: CGSize {
|
|
let size = super.intrinsicContentSize
|
|
return CGSize(
|
|
width: size.width + insets.left + insets.right,
|
|
height: size.height + insets.top + insets.bottom
|
|
)
|
|
}
|
|
}
|
|
|
|
final class CommonGroupsPopVC: DLCustomPopVC {
|
|
private let groups: [CommonGroupModel]
|
|
private let groupInfos: [String: GroupInfoModel]
|
|
private let rowHeight: CGFloat = 64
|
|
private let maximumVisibleRows = 5
|
|
|
|
private let titleLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.text = "共同圈子"
|
|
label.font = .systemFont(ofSize: 18, weight: .semibold)
|
|
label.textColor = UIColor(hexStr: "#293445")
|
|
label.textAlignment = .center
|
|
return label
|
|
}()
|
|
|
|
private lazy var closeButton: UIButton = {
|
|
let button = UIButton(type: .custom)
|
|
button.setImage(UIImage(named: "Common/close"), for: .normal)
|
|
button.addTarget(self, action: #selector(close), for: .touchUpInside)
|
|
return button
|
|
}()
|
|
|
|
private lazy var tableView: UITableView = {
|
|
let tableView = UITableView(frame: .zero, style: .plain)
|
|
tableView.backgroundColor = .white
|
|
tableView.separatorStyle = .none
|
|
tableView.rowHeight = rowHeight
|
|
tableView.showsVerticalScrollIndicator = false
|
|
tableView.dataSource = self
|
|
tableView.register(CommonGroupReadonlyCell.self)
|
|
return tableView
|
|
}()
|
|
|
|
init(groups: [CommonGroupModel], groupInfos: [GroupInfoModel]) {
|
|
self.groups = CommonGroupModel.deduplicated(groups)
|
|
self.groupInfos = Dictionary(groupInfos.map { ($0.group_key, $0) }, uniquingKeysWith: { first, _ in first })
|
|
super.init(nibName: nil, bundle: nil)
|
|
popStyle = .bottom
|
|
bottomCornerRadius = 20
|
|
dimmingClick = true
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
contentView.addSubview(titleLabel)
|
|
contentView.addSubview(closeButton)
|
|
contentView.addSubview(tableView)
|
|
|
|
titleLabel.layoutChain.top(20).centerX().height(28)
|
|
closeButton.layoutChain.right(16).centerY(titleLabel).width(32).height(32)
|
|
tableView.layoutChain
|
|
.topToBottomOfView(titleLabel, offset: 12)
|
|
.left(12)
|
|
.right(12)
|
|
.bottom(16 + kSafeBottomMargin)
|
|
|
|
let visibleRows = min(groups.count, maximumVisibleRows)
|
|
let height = 20 + 28 + 12 + CGFloat(visibleRows) * rowHeight + 16 + kSafeBottomMargin
|
|
// The custom transition lays out contentBgView before its descendants. Give
|
|
// the outer sheet an explicit height so the panel cannot collapse to zero.
|
|
contentBgView.layoutChain.height(height)
|
|
contentView.layoutChain.height(height)
|
|
tableView.isScrollEnabled = groups.count > maximumVisibleRows
|
|
view.layoutIfNeeded()
|
|
}
|
|
|
|
@objc private func close() {
|
|
dismiss(animated: true)
|
|
}
|
|
}
|
|
|
|
extension CommonGroupsPopVC: UITableViewDataSource {
|
|
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
groups.count
|
|
}
|
|
|
|
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
let cell: CommonGroupReadonlyCell = tableView.dequeueReusableCell(for: indexPath)
|
|
let group = groups[indexPath.row]
|
|
cell.configure(group: group, info: groupInfos[group.group_key])
|
|
return cell
|
|
}
|
|
}
|
|
|
|
private final class CommonGroupReadonlyCell: UITableViewCell {
|
|
private let cardView = UIView()
|
|
private let iconView = UIImageView()
|
|
private let nameLabel = UILabel()
|
|
|
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
selectionStyle = .none
|
|
backgroundColor = .clear
|
|
|
|
cardView.backgroundColor = UIColor(hexStr: "#FAFAFA")
|
|
cardView.layer.cornerRadius = 10
|
|
contentView.addSubview(cardView)
|
|
cardView.addSubview(iconView)
|
|
cardView.addSubview(nameLabel)
|
|
|
|
cardView.layoutChain.edges(UIEdgeInsets(top: 4, left: 0, bottom: 4, right: 0))
|
|
iconView.layoutChain.left(12).centerY().width(40).height(40)
|
|
nameLabel.layoutChain
|
|
.leftToRightOfView(iconView, offset: 12)
|
|
.right(12, relation: .greaterThanOrEqual)
|
|
.centerY()
|
|
.height(26)
|
|
|
|
iconView.contentMode = .scaleAspectFill
|
|
iconView.layer.cornerRadius = 8
|
|
iconView.clipsToBounds = true
|
|
nameLabel.textColor = UIColor(hexStr: "#293445")
|
|
nameLabel.font = .systemFont(ofSize: 14, weight: .medium)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func prepareForReuse() {
|
|
super.prepareForReuse()
|
|
iconView.setGroupIcon(url: "", fallback: nil)
|
|
}
|
|
|
|
func configure(group: CommonGroupModel, info: GroupInfoModel?) {
|
|
nameLabel.text = group.group_name.isEmpty ? "未知圈子" : group.group_name
|
|
if let info {
|
|
iconView.setGroupIcon(info)
|
|
} else {
|
|
iconView.setGroupIcon(url: "", fallback: UIImage(named: "GroupIcon/1"))
|
|
}
|
|
}
|
|
}
|