228 lines
8.5 KiB
Swift
228 lines
8.5 KiB
Swift
//
|
|
// FeatureIntroView.swift
|
|
// QuickLocation
|
|
//
|
|
|
|
import UIKit
|
|
|
|
struct FeatureIntroItem {
|
|
let imageName: String
|
|
let accessibilityTitle: String
|
|
let aspectRatio: CGFloat
|
|
let action: FeatureIntroAction
|
|
}
|
|
|
|
enum FeatureIntroAction {
|
|
case signIn
|
|
case createBubble
|
|
case lockDistract
|
|
case pigeonMessage
|
|
case searchLocation
|
|
case sos
|
|
case placeholder
|
|
}
|
|
|
|
final class FeatureIntroView: UIView {
|
|
|
|
var onTapItem: ((FeatureIntroAction) -> Void)?
|
|
|
|
private let headerBackgroundView = UIImageView(image: UIImage(named: "Explore/header_bg"))
|
|
private let titleContainer = UIStackView()
|
|
private let titleLineView = UIView()
|
|
private let titleLabel = UILabel()
|
|
private let sheetView = UIView()
|
|
private let scrollView = UIScrollView()
|
|
private let contentView = UIView()
|
|
private let gridStack = UIStackView()
|
|
|
|
private let items: [FeatureIntroItem] = [
|
|
FeatureIntroItem(
|
|
imageName: "Explore/sign_in_card",
|
|
accessibilityTitle: "还在吗",
|
|
aspectRatio: 127 / 166,
|
|
action: .signIn
|
|
),
|
|
FeatureIntroItem(
|
|
imageName: "Explore/pigeon_message_card",
|
|
accessibilityTitle: "飞鸽传书",
|
|
aspectRatio: 127 / 166,
|
|
action: .pigeonMessage
|
|
),
|
|
FeatureIntroItem(
|
|
imageName: "Explore/lock_distract_card",
|
|
accessibilityTitle: "一键锁机",
|
|
aspectRatio: 130 / 166,
|
|
action: .lockDistract
|
|
),
|
|
FeatureIntroItem(
|
|
imageName: "Explore/bubble_card",
|
|
accessibilityTitle: "隐身气泡",
|
|
aspectRatio: 130 / 166,
|
|
action: .createBubble
|
|
),
|
|
FeatureIntroItem(
|
|
imageName: "Explore/search_location_card",
|
|
accessibilityTitle: "查位置",
|
|
aspectRatio: 130 / 166,
|
|
action: .searchLocation
|
|
),
|
|
FeatureIntroItem(
|
|
imageName: "Explore/sos_card",
|
|
accessibilityTitle: "SOS",
|
|
aspectRatio: 130 / 166,
|
|
action: .sos
|
|
)
|
|
]
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
setupUI()
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
private func setupUI() {
|
|
backgroundColor = .white
|
|
|
|
headerBackgroundView.contentMode = .scaleAspectFill
|
|
headerBackgroundView.clipsToBounds = true
|
|
addSubview(headerBackgroundView)
|
|
headerBackgroundView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
titleLineView.backgroundColor = UIColor(hexStr: "#253B55")
|
|
titleLineView.layer.cornerRadius = 1.5
|
|
titleLineView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
titleLabel.text = "一键锁机"
|
|
titleLabel.textColor = UIColor(hexStr: "#253B55")
|
|
titleLabel.font = FontManager.ziHunBianHei(28)
|
|
titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
|
|
titleContainer.axis = .horizontal
|
|
titleContainer.alignment = .center
|
|
titleContainer.spacing = 3
|
|
titleContainer.addArrangedSubview(titleLineView)
|
|
titleContainer.addArrangedSubview(titleLabel)
|
|
addSubview(titleContainer)
|
|
titleContainer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
sheetView.backgroundColor = .white
|
|
sheetView.layer.cornerRadius = 30
|
|
sheetView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
|
sheetView.clipsToBounds = true
|
|
addSubview(sheetView)
|
|
sheetView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
scrollView.alwaysBounceVertical = false
|
|
scrollView.showsVerticalScrollIndicator = false
|
|
scrollView.contentInset.bottom = 90
|
|
sheetView.addSubview(scrollView)
|
|
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
scrollView.addSubview(contentView)
|
|
contentView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
gridStack.axis = .vertical
|
|
gridStack.alignment = .fill
|
|
gridStack.distribution = .fill
|
|
gridStack.spacing = 13
|
|
contentView.addSubview(gridStack)
|
|
gridStack.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
for rowIndex in 0..<3 {
|
|
let rowStack = UIStackView()
|
|
rowStack.axis = .horizontal
|
|
rowStack.alignment = .fill
|
|
rowStack.distribution = .fillEqually
|
|
rowStack.spacing = 13
|
|
|
|
for columnIndex in 0..<2 {
|
|
let itemIndex = rowIndex * 2 + columnIndex
|
|
let item = items[itemIndex]
|
|
let card = FeatureIntroCardView(item: item)
|
|
card.tag = itemIndex
|
|
card.addTarget(self, action: #selector(cardTapped(_:)), for: .touchUpInside)
|
|
rowStack.addArrangedSubview(card)
|
|
card.heightAnchor.constraint(equalTo: card.widthAnchor, multiplier: item.aspectRatio).isActive = true
|
|
}
|
|
gridStack.addArrangedSubview(rowStack)
|
|
}
|
|
|
|
NSLayoutConstraint.activate([
|
|
headerBackgroundView.topAnchor.constraint(equalTo: topAnchor),
|
|
headerBackgroundView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
headerBackgroundView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
headerBackgroundView.heightAnchor.constraint(equalTo: widthAnchor, multiplier: 230 / 375),
|
|
|
|
titleLineView.widthAnchor.constraint(equalToConstant: 24),
|
|
titleLineView.heightAnchor.constraint(equalToConstant: 3),
|
|
titleContainer.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 75),
|
|
titleContainer.topAnchor.constraint(equalTo: topAnchor, constant: 91),
|
|
|
|
sheetView.topAnchor.constraint(equalTo: topAnchor, constant: 200),
|
|
sheetView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
sheetView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
sheetView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
|
|
|
scrollView.topAnchor.constraint(equalTo: sheetView.topAnchor),
|
|
scrollView.leadingAnchor.constraint(equalTo: sheetView.leadingAnchor),
|
|
scrollView.trailingAnchor.constraint(equalTo: sheetView.trailingAnchor),
|
|
scrollView.bottomAnchor.constraint(equalTo: sheetView.bottomAnchor),
|
|
|
|
contentView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
|
|
contentView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
|
|
contentView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
|
|
contentView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
|
|
contentView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor),
|
|
|
|
gridStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 19),
|
|
gridStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15),
|
|
gridStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),
|
|
gridStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20)
|
|
])
|
|
}
|
|
|
|
@objc private func cardTapped(_ sender: FeatureIntroCardView) {
|
|
guard items.indices.contains(sender.tag) else { return }
|
|
onTapItem?(items[sender.tag].action)
|
|
}
|
|
}
|
|
|
|
final class FeatureIntroCardView: UIControl {
|
|
|
|
private let imageView = UIImageView()
|
|
|
|
init(item: FeatureIntroItem) {
|
|
super.init(frame: .zero)
|
|
|
|
isAccessibilityElement = true
|
|
accessibilityLabel = item.accessibilityTitle
|
|
accessibilityTraits = .button
|
|
|
|
imageView.image = UIImage(named: item.imageName)
|
|
imageView.contentMode = .scaleAspectFit
|
|
imageView.isUserInteractionEnabled = false
|
|
addSubview(imageView)
|
|
imageView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
NSLayoutConstraint.activate([
|
|
imageView.topAnchor.constraint(equalTo: topAnchor),
|
|
imageView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
imageView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
imageView.bottomAnchor.constraint(equalTo: bottomAnchor)
|
|
])
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override var isHighlighted: Bool {
|
|
didSet {
|
|
imageView.alpha = isHighlighted ? 0.78 : 1
|
|
}
|
|
}
|
|
}
|