370 lines
11 KiB
Swift
370 lines
11 KiB
Swift
//
|
|
// MineView.swift
|
|
// QuickLocation
|
|
//
|
|
// Created based on Lanhu design: 我的·版本6
|
|
//
|
|
|
|
import UIKit
|
|
import RxSwift
|
|
import RxCocoa
|
|
|
|
final class MineView: UIView {
|
|
|
|
var disposeBag = DisposeBag()
|
|
|
|
// MARK: - Design Constants (iOS points, from Lanhu 版本6 @2x)
|
|
private let headerHeight: CGFloat = 160
|
|
private let avatarSize: CGFloat = 64
|
|
private let editBtnSize: CGFloat = 30
|
|
private let copyIconSize: CGFloat = 14
|
|
|
|
private let vipCardHeight: CGFloat = 90
|
|
|
|
private let settingsRowHeight: CGFloat = 52
|
|
|
|
// MARK: - Colors
|
|
private let titleColor = UIColor(hexStr: "#1A1A1A")
|
|
private let subtitleColor = UIColor(hexStr: "#767676")
|
|
private let vipBtnTextColor = UIColor(hexStr: "#00343B")
|
|
|
|
private func setupRx() {
|
|
avatarImageView.rx.tapGesture.subscribe { _ in
|
|
AppRouter.push(Route.login)
|
|
}.disposed(by: disposeBag)
|
|
}
|
|
|
|
// MARK: - UI Components
|
|
private lazy var headerBgImageView: UIImageView = {
|
|
let iv = UIImageView()
|
|
iv.image = UIImage(named: "Mine/header_bg")
|
|
iv.contentMode = .scaleAspectFill
|
|
iv.clipsToBounds = true
|
|
return iv
|
|
}()
|
|
|
|
lazy var avatarImageView: UIImageView = {
|
|
let iv = UIImageView()
|
|
iv.isUserInteractionEnabled = true
|
|
iv.contentMode = .scaleAspectFill
|
|
iv.layer.cornerRadius = avatarSize / 2
|
|
iv.layer.borderWidth = 2
|
|
iv.layer.borderColor = UIColor.white.cgColor
|
|
iv.clipsToBounds = true
|
|
iv.layer.shadowColor = UIColor.black.withAlphaComponent(0.2).cgColor
|
|
iv.layer.shadowOffset = .zero
|
|
iv.layer.shadowRadius = 2
|
|
iv.layer.shadowOpacity = 1
|
|
iv.backgroundColor = UIColor(hexStr: "#E0E0E0")
|
|
iv.image = UIImage(named: "Mine/default_avatar")
|
|
return iv
|
|
}()
|
|
|
|
lazy var nameLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.font = .systemFont(ofSize: 16, weight: .heavy)
|
|
label.textColor = titleColor
|
|
label.text = "肖战大王叫我来巡山"
|
|
return label
|
|
}()
|
|
|
|
lazy var editButton: UIButton = {
|
|
let btn = UIButton(type: .custom)
|
|
btn.setImage(UIImage(named: "Mine/edit"), for: .normal)
|
|
btn.backgroundColor = .clear
|
|
return btn
|
|
}()
|
|
|
|
lazy var idLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.font = .systemFont(ofSize: 14, weight: .medium)
|
|
label.textColor = subtitleColor
|
|
label.text = "ID:1234567"
|
|
return label
|
|
}()
|
|
|
|
lazy var copyButton: UIButton = {
|
|
let btn = UIButton(type: .custom)
|
|
btn.setImage(UIImage(named: "Mine/copy"), for: .normal)
|
|
btn.backgroundColor = .clear
|
|
return btn
|
|
}()
|
|
|
|
// VIP Card
|
|
lazy var vipCardView: UIView = {
|
|
let view = UIView()
|
|
view.layer.cornerRadius = 16
|
|
view.clipsToBounds = true
|
|
return view
|
|
}()
|
|
|
|
lazy var vipCardBgImageView: UIImageView = {
|
|
let iv = UIImageView()
|
|
iv.image = UIImage(named: "Mine/vip_card")
|
|
iv.contentMode = .scaleToFill
|
|
return iv
|
|
}()
|
|
|
|
lazy var vipTitleLab: UILabel = {
|
|
let label = UILabel()
|
|
label.font = .systemFont(ofSize: 18, weight: .bold)
|
|
label.textColor = .white
|
|
label.text = "开通会员"
|
|
return label
|
|
}()
|
|
|
|
lazy var vipContentLab: UILabel = {
|
|
let label = UILabel()
|
|
label.font = .systemFont(ofSize: 12, weight: .medium)
|
|
label.textColor = .white
|
|
label.text = "会员尊享10+VIP特权"
|
|
return label
|
|
}()
|
|
|
|
lazy var vipActivateButton: UIButton = {
|
|
let btn = UIButton(type: .custom)
|
|
btn.backgroundColor = UIColor(hexStr: "#C0FF54")
|
|
btn.setTitle("立即开通", for: .normal)
|
|
btn.setTitleColor(UIColor(hexStr: "#00343B"), for: .normal)
|
|
btn.titleLabel?.font = .systemFont(ofSize: 12, weight: .bold)
|
|
btn.cornerRadius = 14
|
|
return btn
|
|
}()
|
|
|
|
// Profile transition background (100pt section below VIP card)
|
|
private lazy var profileBgImageView: UIImageView = {
|
|
let iv = UIImageView()
|
|
iv.image = UIImage(named: "Mine/profile_bg")
|
|
iv.contentMode = .scaleToFill
|
|
return iv
|
|
}()
|
|
|
|
private lazy var bottomFillView: UIView = {
|
|
let view = UIView()
|
|
view.backgroundColor = UIColor(hexStr: "#F5FBFB")
|
|
return view
|
|
}()
|
|
|
|
// Settings Card
|
|
private lazy var settingsCardView: UIView = {
|
|
let view = UIView()
|
|
view.backgroundColor = .white
|
|
view.layer.cornerRadius = 12
|
|
view.layer.shadowColor = UIColor(red: 0.898, green: 0.898, blue: 0.898, alpha: 0.3).cgColor
|
|
view.layer.shadowOffset = .zero
|
|
view.layer.shadowRadius = 4
|
|
view.layer.shadowOpacity = 1
|
|
return view
|
|
}()
|
|
|
|
lazy var settingsTableView: UITableView = {
|
|
let tv = UITableView(frame: .zero, style: .plain)
|
|
tv.backgroundColor = .clear
|
|
tv.isScrollEnabled = false
|
|
tv.separatorStyle = .none
|
|
tv.register(MineMenuCell.self)
|
|
tv.rowHeight = settingsRowHeight
|
|
tv.estimatedRowHeight = settingsRowHeight
|
|
return tv
|
|
}()
|
|
|
|
// MARK: - Callbacks
|
|
var onMenuTap: ((Int) -> Void)?
|
|
|
|
// MARK: - Init
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
setupUI()
|
|
setupLayout()
|
|
setupRx()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
// MARK: - Setup
|
|
private func setupUI() {
|
|
backgroundColor = .white
|
|
|
|
addSubview(headerBgImageView)
|
|
addSubview(avatarImageView)
|
|
addSubview(nameLabel)
|
|
addSubview(editButton)
|
|
addSubview(idLabel)
|
|
addSubview(copyButton)
|
|
|
|
addSubview(vipCardView)
|
|
vipCardView.addSubview(vipCardBgImageView)
|
|
vipCardView.addSubview(vipActivateButton)
|
|
vipCardView.addSubview(vipTitleLab)
|
|
vipCardView.addSubview(vipContentLab)
|
|
|
|
addSubview(profileBgImageView)
|
|
addSubview(bottomFillView)
|
|
addSubview(settingsCardView)
|
|
settingsCardView.addSubview(settingsTableView)
|
|
}
|
|
|
|
private func setupLayout() {
|
|
// Header
|
|
headerBgImageView.layoutChain
|
|
.top().left().right()
|
|
.height(headerHeight)
|
|
|
|
// Avatar
|
|
avatarImageView.layoutChain
|
|
.top(kStatusBarHeight + 44)
|
|
.left(32)
|
|
.size(CGSize(width: avatarSize, height: avatarSize))
|
|
|
|
// Name
|
|
nameLabel.layoutChain
|
|
.leftToRightOfView(avatarImageView, offset: 20)
|
|
.topToView(avatarImageView, offset: 8)
|
|
|
|
// Edit button
|
|
editButton.layoutChain
|
|
.right(16)
|
|
.centerY(nameLabel)
|
|
.size(CGSize(width: editBtnSize, height: editBtnSize))
|
|
|
|
// ID
|
|
idLabel.layoutChain
|
|
.leftToRightOfView(avatarImageView, offset: 20)
|
|
.topToBottomOfView(nameLabel, offset: 4)
|
|
|
|
// Copy button
|
|
copyButton.layoutChain
|
|
.leftToRightOfView(idLabel, offset: 4)
|
|
.centerY(idLabel)
|
|
.size(CGSize(width: copyIconSize, height: copyIconSize))
|
|
|
|
// VIP Card
|
|
vipCardView.layoutChain
|
|
.topToBottomOfView(avatarImageView, offset: 28)
|
|
.left(16).right(16)
|
|
.height(vipCardHeight)
|
|
|
|
vipCardBgImageView.layoutChain
|
|
.edges()
|
|
|
|
vipTitleLab.layoutChain
|
|
.top(22)
|
|
.left(77)
|
|
|
|
vipContentLab.layoutChain
|
|
.topToBottomOfView(vipTitleLab, offset: 6)
|
|
.leftToView(vipTitleLab)
|
|
|
|
vipActivateButton.layoutChain
|
|
.right(19).centerY()
|
|
.width(70).height(28)
|
|
|
|
// Profile transition background (100pt, overlaps VIP card bottom half)
|
|
profileBgImageView.layoutChain
|
|
.topToView(vipCardView, offset: 32)
|
|
.left().right()
|
|
.height(100)
|
|
|
|
// Fill below profile transition
|
|
bottomFillView.layoutChain
|
|
.topToBottomOfView(profileBgImageView)
|
|
.left().right().bottom()
|
|
|
|
// Settings Card (fixed height, not stretched)
|
|
settingsCardView.layoutChain
|
|
.topToBottomOfView(vipCardView, offset: 20)
|
|
.left(16).right(16)
|
|
.height(312)
|
|
|
|
settingsTableView.layoutChain
|
|
.edges()
|
|
}
|
|
}
|
|
|
|
// MARK: - MineMenuCell
|
|
final class MineMenuCell: UITableViewCell {
|
|
|
|
func configure(_ model: MineMenuItem) {
|
|
iconImageView.image = UIImage(named: model.icon)
|
|
titleLabel.text = model.title
|
|
}
|
|
|
|
private func setupSubviews() {
|
|
contentView.addSubview(iconImageView)
|
|
contentView.addSubview(titleLabel)
|
|
contentView.addSubview(arrowImageView)
|
|
contentView.addSubview(dividerLine)
|
|
|
|
iconImageView.layoutChain
|
|
.left(14).centerY()
|
|
.size(CGSize(width: 24, height: 24))
|
|
|
|
titleLabel.layoutChain
|
|
.leftToRightOfView(iconImageView, offset: 8)
|
|
.centerY()
|
|
|
|
arrowImageView.layoutChain
|
|
.right(14).centerY()
|
|
.size(CGSize(width: 18, height: 18))
|
|
|
|
dividerLine.layoutChain
|
|
.left(14).right().bottom()
|
|
.height(0.5)
|
|
}
|
|
|
|
var showDivider: Bool = true {
|
|
didSet { dividerLine.isHidden = !showDivider }
|
|
}
|
|
|
|
private let iconImageView: UIImageView = {
|
|
let iv = UIImageView()
|
|
iv.contentMode = .scaleAspectFit
|
|
return iv
|
|
}()
|
|
|
|
private let titleLabel: UILabel = {
|
|
let label = UILabel()
|
|
label.font = UIFont(name: "PingFangSC-Medium", size: 14)
|
|
?? UIFont.systemFont(ofSize: 14, weight: .medium)
|
|
label.textColor = UIColor(hexStr: "#1A1A1A")
|
|
return label
|
|
}()
|
|
|
|
private let arrowImageView: UIImageView = {
|
|
let iv = UIImageView()
|
|
iv.contentMode = .scaleAspectFit
|
|
iv.image = UIImage(named: "Mine/arrow_right")
|
|
return iv
|
|
}()
|
|
|
|
private let dividerLine: UIView = {
|
|
let view = UIView()
|
|
view.backgroundColor = UIColor(hexStr: "#EDEDED")
|
|
return view
|
|
}()
|
|
|
|
override init(style: CellStyle, reuseIdentifier: String?) {
|
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
backgroundColor = .clear
|
|
selectionStyle = .none
|
|
setupSubviews()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func awakeFromNib() {
|
|
super.awakeFromNib()
|
|
// Initialization code
|
|
}
|
|
|
|
override func setSelected(_ selected: Bool, animated: Bool) {
|
|
super.setSelected(selected, animated: animated)
|
|
|
|
// Configure the view for the selected state
|
|
}
|
|
}
|