// // AppRestrictCell.swift // QuickLocation // import FamilyControls import UIKit import ManagedSettings @available(iOS 16.0, *) final class AppRestrictCell: UITableViewCell { static let reuseId = "AppRestrictCell" private enum Metric { static let appIcon: CGFloat = 40 static let linkIcon: CGFloat = 28 static let actionWidth: CGFloat = 48 static let actionHeight: CGFloat = 56 /// divider 右侧:8 + 48 + 8 + 48 + 12 static let actionAreaWidth: CGFloat = 124 } var onPair: (() -> Void)? var onDelete: (() -> Void)? private let systemIconHost = UIView() private let linkIconView = UIImageView() private let catalogIconView = UIImageView() private let placeholderView = UIView() private let placeholderLab = UILabel() private let divider = UIView() private let pairBtn = UIButton(type: .custom) private let deleteBtn = UIButton(type: .custom) private var systemTokenHost: ApplicationTokenIconHostingView? override init(style: CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) selectionStyle = .none backgroundColor = .clear contentView.backgroundColor = .clear setup() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setup() { contentView.addSubview(systemIconHost) contentView.addSubview(linkIconView) contentView.addSubview(catalogIconView) contentView.addSubview(placeholderView) contentView.addSubview(divider) contentView.addSubview(pairBtn) contentView.addSubview(deleteBtn) systemIconHost.clipsToBounds = true systemIconHost.layer.cornerRadius = 10 systemIconHost.layoutChain .left(16) .centerY() .width(Metric.appIcon) .height(Metric.appIcon) linkIconView.contentMode = .scaleAspectFit linkIconView.layoutChain .leftToRightOfView(systemIconHost, offset: 12) .centerY() .width(Metric.linkIcon) .height(Metric.linkIcon) catalogIconView.contentMode = .scaleAspectFill catalogIconView.layer.cornerRadius = 10 catalogIconView.clipsToBounds = true catalogIconView.layoutChain .leftToRightOfView(linkIconView, offset: 12) .centerY() .width(Metric.appIcon) .height(Metric.appIcon) placeholderView.backgroundColor = UIColor(hexStr: "#F3F4F6") placeholderView.layer.cornerRadius = Metric.appIcon / 2 placeholderView.layer.borderWidth = 1 placeholderView.layer.borderColor = UIColor(hexStr: "#E5E7EB").cgColor placeholderView.layoutChain .leftToRightOfView(linkIconView, offset: 12) .centerY() .width(Metric.appIcon) .height(Metric.appIcon) placeholderView.addSubview(placeholderLab) placeholderLab.text = "?" placeholderLab.font = .systemFont(ofSize: 18, weight: .bold) placeholderLab.textColor = UIColor(hexStr: "#9CA3AF") placeholderLab.textAlignment = .center placeholderLab.layoutChain.center() divider.backgroundColor = UIColor(hexStr: "#F0F0F0") divider.layoutChain .right(Metric.actionAreaWidth) .width(1) .top(12) .bottom(12) configureActionButton(pairBtn, imageName: "AppRestrict/pair_action_link", title: "配对", color: UIColor(hexStr: "#00ADFE")) configureActionButton(deleteBtn, imageName: "AppRestrict/pair_action_delete", title: "删除", color: UIColor(hexStr: "#FF2323")) pairBtn.addTarget(self, action: #selector(tapPair), for: .touchUpInside) deleteBtn.addTarget(self, action: #selector(tapDelete), for: .touchUpInside) pairBtn.layoutChain .leftToRightOfView(divider, offset: 8) .centerY() .width(Metric.actionWidth) .height(Metric.actionHeight) deleteBtn.layoutChain .leftToRightOfView(pairBtn, offset: 8) .centerY() .width(Metric.actionWidth) .height(Metric.actionHeight) } override func layoutSubviews() { super.layoutSubviews() pairBtn.setImageEdge(.top, spacing: 4) deleteBtn.setImageEdge(.top, spacing: 4) } private func configureActionButton(_ button: UIButton, imageName: String, title: String, color: UIColor) { button.setImage(UIImage(named: imageName)?.withRenderingMode(.alwaysOriginal), for: .normal) button.setTitle(title, for: .normal) button.setTitleColor(color, for: .normal) button.titleLabel?.font = .systemFont(ofSize: 12, weight: .medium) button.titleLabel?.textAlignment = .center button.titleLabel?.lineBreakMode = .byClipping } func configure(token: ApplicationToken, catalog: AppCatalogItem?) { systemTokenHost?.subviews.forEach { $0.removeFromSuperview() } let host = ApplicationTokenIconHostingView(token: token) systemIconHost.addSubview(host) host.layoutChain.edges() systemTokenHost = host let isPaired = catalog != nil linkIconView.image = UIImage(named: isPaired ? "AppRestrict/pair_link_active" : "AppRestrict/pair_link_inactive")? .withRenderingMode(.alwaysOriginal) catalogIconView.isHidden = !isPaired placeholderView.isHidden = isPaired if let catalog { AppCatalogStore.setIcon(for: catalogIconView, item: catalog, cornerRadius: 10) } else { catalogIconView.image = nil } } @objc private func tapPair() { onPair?() } @objc private func tapDelete() { onDelete?() } }