// // FeatureIntroView.swift // QuickLocation // import UIKit import SDCycleScrollView 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 = UIView() private let headerBgImg = 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 static let allItems: [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 ) ] private var displayedItems: [FeatureIntroItem] = [] 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 addSubview(headerBackgroundView) headerBackgroundView.backgroundColor = .clear headerBgImg.contentMode = .scaleAspectFill headerBgImg.clipsToBounds = true headerBackgroundView.addSubview(headerBgImg) headerBackgroundView.addSubview(cycleScrollView) 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 reloadFeatureItems() headerBackgroundView.layoutChain .edges(excludingEdge: .bottom) .heightToWidth(190 / 375) headerBgImg.layoutChain.edges() cycleScrollView.layoutChain.edges() sheetView.layoutChain .topToBottomOfView(headerBackgroundView, offset: -30) .edges(excludingEdge: .top) NSLayoutConstraint.activate([ 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) ]) } func reloadFeatureItems() { displayedItems = Self.allItems rebuildGrid() } private func rebuildGrid() { gridStack.arrangedSubviews.forEach { gridStack.removeArrangedSubview($0) $0.removeFromSuperview() } let columns = 2 var index = 0 while index < displayedItems.count { let rowStack = UIStackView() rowStack.axis = .horizontal rowStack.alignment = .fill rowStack.distribution = .fillEqually rowStack.spacing = 13 for _ in 0..