317 lines
11 KiB
Swift
317 lines
11 KiB
Swift
//
|
||
// MemberPhoneReportView.swift
|
||
// QuickLocation
|
||
//
|
||
|
||
import UIKit
|
||
|
||
struct MemberPhoneReportPreview {
|
||
let screenTimeSeconds: Int?
|
||
let usageCount: Int
|
||
let unlockCount: Int
|
||
|
||
static func currentUser(from manager: UnlockCountManager = .shared) -> MemberPhoneReportPreview {
|
||
let seconds = manager.todayScreenTimeSeconds
|
||
return MemberPhoneReportPreview(
|
||
screenTimeSeconds: seconds > 0 ? seconds : nil,
|
||
usageCount: manager.todayAppUsageCount,
|
||
unlockCount: manager.todayCount
|
||
)
|
||
}
|
||
}
|
||
|
||
/// 成员面板「手机报告」:屏幕使用时长 + 使用次数 + 今日解锁
|
||
final class MemberPhoneReportView: UIView {
|
||
|
||
private enum Layout {
|
||
static let designCardWidth: CGFloat = 345
|
||
static let leftPanelWidthRatio: CGFloat = 164 / designCardWidth
|
||
static let leftPanelAspect: CGFloat = 147 / 164
|
||
static let panelGap: CGFloat = 13
|
||
static let cardInset: CGFloat = 12
|
||
static let usageCardAspect: CGFloat = 69 / 146
|
||
static let usageUnlockGap: CGFloat = 9
|
||
}
|
||
|
||
private let titleColor = UIColor(hexStr: "#293445")
|
||
private let usageTint = UIColor(hexStr: "#E8FBE8")
|
||
private let unlockTint = UIColor(hexStr: "#E8F4FF")
|
||
|
||
private let sectionIcon = UIImageView()
|
||
private let sectionTitleLab = UILabel()
|
||
private let cardView = UIView()
|
||
private let leftPanel = UIView()
|
||
private let rightPanel = UIView()
|
||
private let screenTimeIcon = UIImageView()
|
||
private let screenTimeTitleLab = UILabel()
|
||
private let screenTimeBadge = ScreenTimeBadgeView()
|
||
private let usageCard = UIView()
|
||
private let usageIcon = UIImageView()
|
||
private let usageTitleLab = UILabel()
|
||
private let usageValueLab = UILabel()
|
||
private let unlockCard = UIView()
|
||
private let unlockIcon = UIImageView()
|
||
private let unlockTitleLab = UILabel()
|
||
private let unlockValueLab = UILabel()
|
||
|
||
override init(frame: CGRect) {
|
||
super.init(frame: frame)
|
||
setupUI()
|
||
configure(with: nil)
|
||
}
|
||
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
func configure(with preview: MemberPhoneReportPreview?) {
|
||
if let preview {
|
||
screenTimeBadge.configure(seconds: preview.screenTimeSeconds)
|
||
usageValueLab.text = "\(preview.usageCount)"
|
||
unlockValueLab.text = "\(preview.unlockCount)"
|
||
} else {
|
||
screenTimeBadge.configure(seconds: nil)
|
||
usageValueLab.text = "**"
|
||
unlockValueLab.text = "**"
|
||
}
|
||
}
|
||
|
||
private func setupUI() {
|
||
backgroundColor = .clear
|
||
|
||
sectionIcon.image = UIImage(named: "Home/member_phone_report")
|
||
sectionIcon.contentMode = .scaleAspectFit
|
||
addSubview(sectionIcon)
|
||
sectionIcon.layoutChain
|
||
.top()
|
||
.left(15)
|
||
.width(18)
|
||
.heightToWidth(1)
|
||
|
||
sectionTitleLab.text = "手机报告"
|
||
sectionTitleLab.font = .systemFont(ofSize: 16, weight: .bold)
|
||
sectionTitleLab.textColor = titleColor
|
||
addSubview(sectionTitleLab)
|
||
sectionTitleLab.layoutChain
|
||
.leftToRightOfView(sectionIcon, offset: 6)
|
||
.centerY(sectionIcon)
|
||
|
||
cardView.backgroundColor = .white
|
||
cardView.cornerRadius = 16
|
||
addSubview(cardView)
|
||
cardView.layoutChain
|
||
.topToBottomOfView(sectionIcon, offset: 12)
|
||
.edgesHorzontal(15)
|
||
.bottom()
|
||
|
||
cardView.addSubview(leftPanel)
|
||
cardView.addSubview(rightPanel)
|
||
leftPanel.backgroundColor = UIColor(hexStr: "#FFFCEA")
|
||
leftPanel.cornerRadius = 14
|
||
rightPanel.backgroundColor = .clear
|
||
leftPanel.layoutChain
|
||
.top(Layout.cardInset)
|
||
.left(Layout.cardInset)
|
||
.widthToView(cardView, multiplier: Layout.leftPanelWidthRatio)
|
||
.heightToWidth(Layout.leftPanelAspect)
|
||
|
||
rightPanel.layoutChain
|
||
.top(Layout.cardInset)
|
||
.right(Layout.cardInset)
|
||
.leftToRightOfView(leftPanel, offset: Layout.panelGap)
|
||
.heightToView(leftPanel)
|
||
.bottom(Layout.cardInset)
|
||
|
||
screenTimeIcon.image = UIImage(named: "Home/member_report_clock")
|
||
screenTimeIcon.contentMode = .scaleAspectFit
|
||
leftPanel.addSubview(screenTimeIcon)
|
||
screenTimeIcon.layoutChain
|
||
.top(7)
|
||
.left(7)
|
||
.width(20)
|
||
.height(20)
|
||
|
||
screenTimeTitleLab.text = "屏幕使用时长"
|
||
screenTimeTitleLab.font = .systemFont(ofSize: 12, weight: .bold)
|
||
screenTimeTitleLab.textColor = UIColor(hexStr: "#293445")
|
||
leftPanel.addSubview(screenTimeTitleLab)
|
||
screenTimeTitleLab.layoutChain
|
||
.leftToRightOfView(screenTimeIcon, offset: 6)
|
||
.centerY(screenTimeIcon)
|
||
|
||
leftPanel.addSubview(screenTimeBadge)
|
||
screenTimeBadge.layoutChain
|
||
.topToBottomOfView(screenTimeTitleLab, offset: 13)
|
||
.centerX(leftPanel, offset: 7)
|
||
|
||
let screenTimeBadgeArrow = UIImageView(image: UIImage(named: "Home/member_screenTime_arrow"))
|
||
leftPanel.addSubview(screenTimeBadgeArrow)
|
||
screenTimeBadgeArrow.layoutChain
|
||
.topToBottomOfView(screenTimeBadge)
|
||
.centerX(screenTimeBadge)
|
||
|
||
let barchartIcon = UIImageView(image: UIImage(named: "Home/member_screenTime"))
|
||
leftPanel.addSubview(barchartIcon)
|
||
barchartIcon.layoutChain
|
||
.centerX()
|
||
.topToBottomOfView(screenTimeBadge, offset: 12)
|
||
|
||
usageCard.backgroundColor = UIColor(hexStr: "#F8FFEE")
|
||
usageCard.cornerRadius = 14
|
||
rightPanel.addSubview(usageCard)
|
||
usageCard.layoutChain
|
||
.top()
|
||
.edgesHorzontal()
|
||
.heightToWidth(Layout.usageCardAspect)
|
||
|
||
usageIcon.image = UIImage(named: "Home/member_usage_count")
|
||
usageIcon.contentMode = .scaleAspectFit
|
||
usageCard.addSubview(usageIcon)
|
||
usageIcon.layoutChain
|
||
.top(8)
|
||
.left(10)
|
||
.width(20)
|
||
.height(20)
|
||
|
||
usageTitleLab.text = "使用次数"
|
||
usageTitleLab.font = .systemFont(ofSize: 12, weight: .bold)
|
||
usageTitleLab.textColor = UIColor(hexStr: "#293445")
|
||
usageCard.addSubview(usageTitleLab)
|
||
usageTitleLab.layoutChain
|
||
.leftToRightOfView(usageIcon, offset: 6)
|
||
.centerY(usageIcon)
|
||
.right(relation: .lessThanOrEqual)
|
||
|
||
usageValueLab.font = .systemFont(ofSize: 24, weight: .bold)
|
||
usageValueLab.textColor = UIColor(hexStr: "#83DE00")
|
||
usageCard.addSubview(usageValueLab)
|
||
usageValueLab.layoutChain
|
||
.topToBottomOfView(usageTitleLab, offset: 4)
|
||
.centerX()
|
||
|
||
let usageValueUnitLab = UILabel()
|
||
usageValueUnitLab.text = "次"
|
||
usageValueUnitLab.textColor = UIColor(hexStr: "#293445")
|
||
usageValueUnitLab.font = .systemFont(ofSize: 12, weight: .bold)
|
||
usageCard.addSubview(usageValueUnitLab)
|
||
usageValueUnitLab.layoutChain
|
||
.leftToRightOfView(usageValueLab, offset: 3)
|
||
.bottomToView(usageValueLab, offset: -4)
|
||
|
||
unlockCard.backgroundColor = UIColor(hexStr: "#F2FEFF")
|
||
unlockCard.cornerRadius = 14
|
||
rightPanel.addSubview(unlockCard)
|
||
unlockCard.layoutChain
|
||
.topToBottomOfView(usageCard, offset: Layout.usageUnlockGap)
|
||
.edgesHorzontal()
|
||
.heightToWidth(Layout.usageCardAspect)
|
||
.bottom()
|
||
|
||
unlockIcon.image = UIImage(named: "Home/member_daily_unlock")
|
||
unlockIcon.contentMode = .scaleAspectFit
|
||
unlockCard.addSubview(unlockIcon)
|
||
unlockIcon.layoutChain
|
||
.top(8)
|
||
.left(10)
|
||
.width(20)
|
||
.height(20)
|
||
|
||
unlockTitleLab.text = "今日解锁"
|
||
unlockTitleLab.font = .systemFont(ofSize: 12, weight: .bold)
|
||
unlockTitleLab.textColor = UIColor(hexStr: "#293445")
|
||
unlockCard.addSubview(unlockTitleLab)
|
||
unlockTitleLab.layoutChain
|
||
.leftToRightOfView(unlockIcon, offset: 6)
|
||
.centerY(unlockIcon)
|
||
.right(relation: .lessThanOrEqual)
|
||
|
||
unlockValueLab.font = .systemFont(ofSize: 24, weight: .bold)
|
||
unlockValueLab.textColor = UIColor(hexStr: "#08B6C4")
|
||
unlockCard.addSubview(unlockValueLab)
|
||
unlockValueLab.layoutChain
|
||
.topToBottomOfView(unlockTitleLab, offset: 4)
|
||
.centerX()
|
||
|
||
let unlockValueUnitLab = UILabel()
|
||
unlockValueUnitLab.text = "次"
|
||
unlockValueUnitLab.textColor = UIColor(hexStr: "#293445")
|
||
unlockValueUnitLab.font = .systemFont(ofSize: 12, weight: .bold)
|
||
unlockCard.addSubview(unlockValueUnitLab)
|
||
unlockValueUnitLab.layoutChain
|
||
.leftToRightOfView(unlockValueLab, offset: 3)
|
||
.bottomToView(unlockValueLab, offset: -4)
|
||
}
|
||
}
|
||
|
||
// MARK: - 屏幕时长 Badge
|
||
|
||
private final class ScreenTimeBadgeView: UIView {
|
||
|
||
private let contentStack = UIStackView()
|
||
private let numberColor = UIColor(hexStr: "#FF6B4A")
|
||
private let unitColor = UIColor(hexStr: "#293445")
|
||
private let numberFont = UIFont.systemFont(ofSize: 24, weight: .heavy)
|
||
private let unitFont = UIFont.systemFont(ofSize: 14, weight: .heavy)
|
||
|
||
override init(frame: CGRect) {
|
||
super.init(frame: frame)
|
||
backgroundColor = .white
|
||
cornerRadius = 11
|
||
contentStack.axis = .horizontal
|
||
contentStack.alignment = .lastBaseline
|
||
contentStack.spacing = 2
|
||
addSubview(contentStack)
|
||
contentStack.layoutChain
|
||
.edges(UIEdgeInsets(top: 6, left: 10, bottom: 6, right: 10))
|
||
}
|
||
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
func configure(seconds: Int?) {
|
||
contentStack.arrangedSubviews.forEach { view in
|
||
contentStack.removeArrangedSubview(view)
|
||
view.removeFromSuperview()
|
||
}
|
||
|
||
guard let seconds, seconds > 0 else {
|
||
contentStack.addArrangedSubview(makeLabel(text: "**", font: numberFont, color: numberColor))
|
||
return
|
||
}
|
||
|
||
let hours = seconds / 3600
|
||
let minutes = (seconds % 3600) / 60
|
||
|
||
if hours > 0 {
|
||
appendPart(number: "\(hours)", unit: "h")
|
||
if minutes > 0 {
|
||
contentStack.addArrangedSubview(makeSpacer(width: 4))
|
||
appendPart(number: "\(minutes)", unit: "m")
|
||
}
|
||
} else {
|
||
appendPart(number: "\(minutes)", unit: "m")
|
||
}
|
||
}
|
||
|
||
private func appendPart(number: String, unit: String) {
|
||
contentStack.addArrangedSubview(makeLabel(text: number, font: numberFont, color: numberColor))
|
||
contentStack.addArrangedSubview(makeLabel(text: unit, font: unitFont, color: unitColor))
|
||
}
|
||
|
||
private func makeLabel(text: String, font: UIFont, color: UIColor) -> UILabel {
|
||
let label = UILabel()
|
||
label.text = text
|
||
label.font = font
|
||
label.textColor = color
|
||
return label
|
||
}
|
||
|
||
private func makeSpacer(width: CGFloat) -> UIView {
|
||
let spacer = UIView()
|
||
spacer.translatesAutoresizingMaskIntoConstraints = false
|
||
spacer.widthAnchor.constraint(equalToConstant: width).isActive = true
|
||
return spacer
|
||
}
|
||
}
|