199 lines
6.6 KiB
Swift
199 lines
6.6 KiB
Swift
//
|
|
// TitleView.swift
|
|
// Crush
|
|
//
|
|
// Created by Leon on 2025/7/18.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
class TitleView: UIView {
|
|
// MARK: - Properties
|
|
private var stackV: UIStackView
|
|
private(set)var titleLabel: LineSpaceLabel
|
|
private var subTitleLabel: LineSpaceLabel
|
|
private var stackSpacing: CGFloat = 16
|
|
|
|
var title: String? {
|
|
didSet {
|
|
titleLabel.text = title
|
|
}
|
|
}
|
|
|
|
var subtitle: String? {
|
|
didSet {
|
|
if let subtitle = subtitle, !subtitle.isEmpty {
|
|
subTitleLabel.isHidden = false
|
|
subTitleLabel.text = subtitle
|
|
} else {
|
|
subTitleLabel.isHidden = true
|
|
subTitleLabel.text = ""
|
|
}
|
|
}
|
|
}
|
|
|
|
var attributeSubTitle: NSAttributedString? {
|
|
didSet {
|
|
if let attributeSubTitle = attributeSubTitle, attributeSubTitle.length > 0 {
|
|
subTitleLabel.isHidden = false
|
|
subTitleLabel.text = ""
|
|
subTitleLabel.attributedText = attributeSubTitle
|
|
} else {
|
|
subTitleLabel.isHidden = true
|
|
subTitleLabel.attributedText = nil
|
|
}
|
|
}
|
|
}
|
|
|
|
var titleHidden: Bool = false {
|
|
didSet {
|
|
titleLabel.isHidden = titleHidden
|
|
}
|
|
}
|
|
|
|
/// Default: 16
|
|
var optionInnerTopPadding: CGFloat = 16 {
|
|
didSet {
|
|
makeStackConstraint()
|
|
}
|
|
}
|
|
|
|
/// Default: 16
|
|
var optionInnerBottomPadding: CGFloat = 16 {
|
|
didSet {
|
|
makeStackConstraint()
|
|
}
|
|
}
|
|
|
|
var optionInnerLRPadding: CGFloat = 24 {
|
|
didSet {
|
|
makeStackConstraint()
|
|
}
|
|
}
|
|
|
|
var optionInnerTrailingAppend: CGFloat = 0 {
|
|
didSet {
|
|
makeStackConstraint()
|
|
}
|
|
}
|
|
|
|
var alwaysDarkMode: Bool = false {
|
|
didSet {
|
|
if alwaysDarkMode {
|
|
titleLabel.textColor = .c.ctpn//EPSystemToken.darkColor(.txtPrimaryNormal)
|
|
subTitleLabel.textColor = .c.ctsn//EPSystemToken.darkColor(.txtSecondaryNormal)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Initialization
|
|
override init(frame: CGRect) {
|
|
stackV = UIStackView()
|
|
titleLabel = LineSpaceLabel()
|
|
subTitleLabel = LineSpaceLabel()
|
|
|
|
super.init(frame: frame)
|
|
|
|
// Configure stack view
|
|
stackV.spacing = stackSpacing
|
|
stackV.axis = .vertical
|
|
addSubview(stackV)
|
|
stackV.snp.makeConstraints { make in
|
|
make.top.equalToSuperview().offset(optionInnerTopPadding)
|
|
make.bottom.equalToSuperview().offset(-optionInnerBottomPadding)
|
|
make.leading.equalToSuperview().offset(optionInnerLRPadding)
|
|
make.trailing.equalToSuperview().offset(-optionInnerLRPadding).priority(999)
|
|
}
|
|
|
|
// Configure title label
|
|
let titleT = CLSystemToken.typography(token: .thm)
|
|
titleLabel.config(titleT)
|
|
titleLabel.numberOfLines = 0
|
|
titleLabel.lineBreakMode = .byWordWrapping
|
|
stackV.addArrangedSubview(titleLabel)
|
|
|
|
// Configure subtitle label
|
|
let subTitleT = CLSystemToken.typography(token: .tbm)
|
|
subTitleLabel.config(subTitleT)
|
|
subTitleLabel.numberOfLines = 0
|
|
stackV.addArrangedSubview(subTitleLabel)
|
|
|
|
// Theme configuration
|
|
self.titleLabel.textColor = .c.ctpn //EPSystemToken.color(.txtPrimaryNormal)
|
|
self.subTitleLabel.textColor = .c.ctsn //EPSystemToken.color(.txtSecondaryNormal)
|
|
|
|
// Default state
|
|
subTitleLabel.isHidden = true
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
// MARK: - Helper Methods
|
|
private func makeStackConstraint() {
|
|
stackV.snp.remakeConstraints { make in
|
|
make.top.equalToSuperview().offset(optionInnerTopPadding)
|
|
make.bottom.lessThanOrEqualToSuperview().offset(-optionInnerBottomPadding)
|
|
make.leading.equalToSuperview().offset(optionInnerLRPadding)
|
|
make.trailing.equalToSuperview().offset(-self.optionInnerLRPadding - self.optionInnerTrailingAppend)
|
|
}
|
|
}
|
|
|
|
// MARK: - Public Methods
|
|
// func precalcHeight() -> CGFloat {
|
|
// preCalculateHeight()
|
|
// }
|
|
|
|
func preCalculateHeight() -> CGFloat {
|
|
let topBottomPadding = optionInnerTopPadding + optionInnerBottomPadding
|
|
|
|
// Calcualte way 1: height: 36
|
|
// let titleT = CLSystemToken.typography(token: .thm)
|
|
// let lineSpacing = titleT.lineHeight * 0.5
|
|
// let text = titleLabel.text ?? ""
|
|
// let labelWidth = UIScreen.main.bounds.width - optionInnerLRPadding * 2 - optionInnerTrailingAppend
|
|
// var titleHeight = heightForText(text, font: titleT.font!, width: labelWidth, lineSpacing: lineSpacing)
|
|
|
|
// Calculate way 2: height: 33.6
|
|
let titleHeight = titleLabel.isHidden ? 0 : titleLabel.sizeThatFits(CGSize(width: UIScreen.main.bounds.width - optionInnerLRPadding * 2 - optionInnerTrailingAppend, height: .greatestFiniteMagnitude)).height
|
|
// dlog("oldCalculateHeight: \(oldCalculateHeight), bouncingCalculate: \(titleHeight)")
|
|
|
|
let subTitleHeight = subTitleLabel.isHidden ? 0 : subTitleLabel.sizeThatFits(CGSize(width: UIScreen.main.bounds.width - optionInnerLRPadding * 2 - optionInnerTrailingAppend, height: .greatestFiniteMagnitude)).height
|
|
|
|
var total = topBottomPadding + titleHeight + subTitleHeight
|
|
if titleHeight > 0 && subTitleHeight > 0 {
|
|
total += stackSpacing
|
|
}
|
|
return ceil(total)
|
|
}
|
|
|
|
func setupTitleSubtitleAlignment(_ alignment: NSTextAlignment) {
|
|
titleLabel.textAlignment = alignment
|
|
subTitleLabel.textAlignment = alignment
|
|
}
|
|
|
|
// MARK: - Helper Methods
|
|
|
|
func heightForText(_ text: String, font: UIFont, width: CGFloat, lineSpacing: CGFloat = 0) -> CGFloat {
|
|
let paragraphStyle = NSMutableParagraphStyle()
|
|
paragraphStyle.lineBreakMode = .byWordWrapping
|
|
paragraphStyle.lineSpacing = lineSpacing
|
|
|
|
let attributes: [NSAttributedString.Key: Any] = [
|
|
.font: font,
|
|
.paragraphStyle: paragraphStyle
|
|
]
|
|
|
|
let size = CGSize(width: width, height: .greatestFiniteMagnitude)
|
|
let rect = (text as NSString).boundingRect(
|
|
with: size,
|
|
options: [.usesLineFragmentOrigin, .usesFontLeading],
|
|
attributes: attributes,
|
|
context: nil
|
|
)
|
|
return ceil(rect.height)
|
|
}
|
|
}
|