// // ChipButtons.swift // Crush // // Created by Leon on 2025/7/18. // import SnapKit import UIKit // MARK: - Base Class class EPChipButton: CLControl { // MARK: - Constants let chipButtonHeight: CGFloat = 32 // MARK: - Properties lazy var stack: UIStackView = { let stack = UIStackView() stack.alignment = .fill stack.distribution = .fill stack.alignment = .center stack.axis = .horizontal stack.spacing = 8 return stack }() lazy var imageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFill imageView.isUserInteractionEnabled = true return imageView }() lazy var textLabel: UILabel = { let label = UILabel() label.font = CLSystemToken.font(token: .tlm) label.isUserInteractionEnabled = true return label }() lazy var iconView: UIImageView = { let iconView = UIImageView() iconView.contentMode = .scaleAspectFit iconView.isUserInteractionEnabled = true return iconView }() var text: String? { didSet { textLabel.text = text setNeedsLayout() } } var iconCode: IconCode = .sort { didSet { iconView.isHidden = (iconCode.rawValue <= 0) iconView.image = defaultIcon } } var defaultIconColor: UIColor? { didSet { iconView.image = defaultIcon } } var defaultIcon: UIImage? { let color = defaultIconColor ?? UIColor.c.ctsn return MWIconFont.image(fromIconInt: iconCode.rawValue, size: iconSize, color: color, edgeInsets: .zero) } var selectedIcon: UIImage? { nil // Subclasses override } var defaultImage: UIImage? { nil // Subclasses override } var selectedImage: UIImage? { nil // Subclasses override } var paddingInsets: UIEdgeInsets { .zero // Subclasses override } var iconSize: CGSize { .zero // Subclasses override } var imageSize: CGSize { .zero // Subclasses override } // MARK: - Initialization override init(frame: CGRect) { super.init(frame: frame) clipsToBounds = true layer.cornerRadius = chipButtonHeight * 0.5 setupTheme() } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Setup func setupTheme() { textLabel.textColor = .c.ctpn // EPSystemToken.color(.txtPrimaryNormal) } // MARK: - Hit Testing override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { guard isUserInteractionEnabled, alpha != 0, !isHidden else { return nil } if self.point(inside: point, with: event) { return self } return super.hitTest(point, with: event) } } // MARK: - EPChipFilterButton /// 只有文字,左右16。 height 32 class EPChipFilterButton: EPChipButton { override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .c.csen // EPSystemToken.color(.surfaceElementNormal) layer.borderWidth = 0 addSubview(textLabel) textLabel.snp.makeConstraints { make in make.edges.equalTo(self).inset(paddingInsets).priority(.high) make.height.equalTo(chipButtonHeight) } } override func setupTheme() { super.setupTheme() layer.borderColor = UIColor.c.cpvn.cgColor // EPSystemToken.color(.primaryVariantNormal).cgColor } override var paddingInsets: UIEdgeInsets { UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16) } override var intrinsicContentSize: CGSize{ return CGSize(width: textLabel.size.width + 32, height: chipButtonHeight) } override var isHighlighted: Bool { didSet { backgroundColor = isSelected ? (isHighlighted ? UIColor.c.cpp : UIColor.c.cpn) : (isHighlighted ? UIColor.c.csep : UIColor.c.csen) } } override var isSelected: Bool { didSet { backgroundColor = isSelected ? UIColor.c.cpn : UIColor.c.csen layer.borderWidth = isSelected ? 1 : 0 } } override var isEnabled: Bool { didSet { textLabel.textColor = isEnabled ? UIColor.c.ctpn : UIColor.c.ctd backgroundColor = isEnabled ? UIColor.c.csen : UIColor.c.csed } } } class EPChipCenterIconButton: EPChipButton { override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .c.csen // EPSystemToken.color(.surfaceElementNormal) layer.borderWidth = 0 defaultIconColor = .c.ctpn addSubview(iconView) iconView.snp.makeConstraints { make in make.center.equalToSuperview() // make.height.equalTo(chipButtonHeight) } } override func setupTheme() { super.setupTheme() layer.borderColor = UIColor.c.cpvn.cgColor // EPSystemToken.color(.primaryVariantNormal).cgColor } override var iconSize: CGSize { return .init(width: 20, height: 20) } override var isHighlighted: Bool { didSet { backgroundColor = isSelected ? (isHighlighted ? UIColor.c.cpp : UIColor.c.cpn) : (isHighlighted ? UIColor.c.csep : UIColor.c.csen) } } override var isSelected: Bool { didSet { backgroundColor = isSelected ? UIColor.c.cpn : UIColor.c.csen layer.borderWidth = isSelected ? 1 : 0 } } override var isEnabled: Bool { didSet { textLabel.textColor = isEnabled ? UIColor.c.ctpn : UIColor.c.ctd backgroundColor = isEnabled ? UIColor.c.csen : UIColor.c.csed } } override func layoutSubviews() { super.layoutSubviews() layer.cornerRadius = bounds.size.height * 0.5 } } // MARK: - EPChipDropdownButton class EPChipDropdownButton: EPChipButton { var needTransform: Bool = true override init(frame: CGRect) { super.init(frame: frame) stack.spacing = 4 addSubview(stack) stack.snp.makeConstraints { make in make.edges.equalTo(self).inset(paddingInsets) make.height.equalTo(chipButtonHeight) } stack.addArrangedSubview(textLabel) stack.addArrangedSubview(iconView) iconView.snp.makeConstraints { make in make.size.equalTo(iconSize) } } override func setupTheme() { super.setupTheme() textLabel.textColor = isSelected ? UIColor.c.cpvn : UIColor.c.ctpn iconView.image = isSelected ? selectedIcon : defaultIcon } override var paddingInsets: UIEdgeInsets { UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 8) } override var iconSize: CGSize { CGSize(width: 12, height: 12) } override var defaultIcon: UIImage? { let color = UIColor.c.ctsn // EPSystemToken.color(.txtSecondaryNormal) return MWIconFont.image(fromIconInt: iconCode.rawValue, size: iconSize, color: color, edgeInsets: .zero) } override var selectedIcon: UIImage? { let color = UIColor.c.cpvn // EPSystemToken.color(.primaryVariantNormal) return MWIconFont.image(fromIconInt: iconCode.rawValue, size: iconSize, color: color, edgeInsets: .zero) } override var isHighlighted: Bool { didSet { backgroundColor = isHighlighted ? UIColor.c.csep : nil } } override var isSelected: Bool { didSet { textLabel.textColor = isSelected ? UIColor.c.cpvn : UIColor.c.ctpn UIView.animate(withDuration: 0.2) { if self.needTransform { self.iconView.transform = self.isSelected ? CGAffineTransform(rotationAngle: .pi) : .identity } self.iconView.image = self.isSelected ? self.selectedIcon : self.defaultIcon } } } } // MARK: - EPChipRemovableButton class EPChipRemovableButton: EPChipButton { override init(frame: CGRect) { super.init(frame: frame) stack.spacing = 8 addSubview(stack) stack.snp.makeConstraints { make in make.edges.equalTo(self).inset(paddingInsets).priority(998) make.height.equalTo(chipButtonHeight) } stack.addArrangedSubview(textLabel) stack.addArrangedSubview(iconView) iconView.snp.makeConstraints { make in make.size.equalTo(iconSize) } } override func setupTheme() { super.setupTheme() backgroundColor = .c.csen iconView.image = defaultIcon } override var paddingInsets: UIEdgeInsets { UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12) } override var iconSize: CGSize { CGSize(width: 16, height: 16) } override var defaultIcon: UIImage? { let color = UIColor.c.ctsn // EPSystemToken.color(.txtSecondaryNormal) return MWIconFont.image(fromIconInt: iconCode.rawValue, size: iconSize, color: color, edgeInsets: .zero) } override var selectedIcon: UIImage? { let color = UIColor.c.cpvn return MWIconFont.image(fromIconInt: iconCode.rawValue, size: iconSize, color: color, edgeInsets: .zero) } override var isHighlighted: Bool { didSet { backgroundColor = isHighlighted ? UIColor.c.csep : UIColor.c.csen } } func widthPreCal(tagHeight: CGFloat) -> CGFloat { let paddingInsets = self.paddingInsets return textLabel.sizeThatFits(CGSize(width: .greatestFiniteMagnitude, height: tagHeight)).width + paddingInsets.left + paddingInsets.right + iconSize.width + stack.spacing } func setupHasImageMode() { stack.insertArrangedSubview(imageView, at: 0) imageView.isHidden = false imageView.snp.makeConstraints { make in make.size.equalTo(imageSize) } } } // MARK: - EPChipAssistButton /// 图标在左,文字在右(用处eg:Interests添加页面Add按钮) class EPChipAssistButton: EPChipButton { var iconTextSpacing: CGFloat = 8 { didSet { stack.spacing = iconTextSpacing } } var buttonHeight: CGFloat = 32 { didSet { layer.cornerRadius = buttonHeight * 0.5 stack.snp.updateConstraints { make in make.height.equalTo(buttonHeight) } } } override init(frame: CGRect) { super.init(frame: frame) stack.spacing = 8 addSubview(stack) stack.snp.makeConstraints { make in make.edges.equalTo(self).inset(paddingInsets) make.height.equalTo(chipButtonHeight) } stack.addArrangedSubview(iconView) stack.addArrangedSubview(textLabel) iconView.snp.makeConstraints { make in make.size.equalTo(iconSize) } } override func setupTheme() { super.setupTheme() backgroundColor = .c.csen // EPSystemToken.color(.surfaceElementNormal) iconView.image = defaultIcon } override var paddingInsets: UIEdgeInsets { UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12) } override var iconSize: CGSize { CGSize(width: 16, height: 16) } override var defaultIcon: UIImage? { let color = defaultIconColor ?? UIColor.c.ctsn // EPSystemToken.color(.txtSecondaryNormal) return MWIconFont.image(fromIconInt: iconCode.rawValue, size: iconSize, color: color, edgeInsets: .zero) } // override var isHighlighted: Bool { // didSet { // backgroundColor = isHighlighted // ? UIColor.c.csep // : UIColor.c.csen // } // } override var isSelected: Bool { didSet { backgroundColor = isSelected ? UIColor.c.cpn : UIColor.c.csen //layer.borderWidth = isSelected ? 1 : 0 } } func setDefaultImage(_ image: UIImage?) { iconView.image = image } func setIconSize(_ size: CGSize) { iconView.snp.updateConstraints { make in make.size.equalTo(size) } } func widthPreCal(tagHeight: CGFloat) -> CGFloat { let paddingInsets = self.paddingInsets return textLabel.sizeThatFits(CGSize(width: .greatestFiniteMagnitude, height: tagHeight)).width + paddingInsets.left + paddingInsets.right + iconSize.width + stack.spacing } } // MARK: - EPChipContrastButton:EPChipAssistButton /// Like EPIconContrastTertiaryButton的加文字版本, 特别处理 class EPChipContrastButton:EPChipAssistButton { override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .c.csedn buttonHeight = 48 defaultIconColor = .white textLabel.font = .t.tll } override var paddingInsets: UIEdgeInsets { UIEdgeInsets(top: 0, left: 32, bottom: 0, right: 32) } override var iconSize: CGSize { CGSize(width: 24, height: 24) } } // MARK: - EPChipImageIconButton /// 左边image+中间文字+右边iconFont class EPChipImageIconButton: EPChipFilterButton { override init(frame: CGRect) { super.init(frame: frame) stack.spacing = 8 addSubview(stack) stack.snp.makeConstraints { make in make.edges.equalTo(self).inset(paddingInsets) make.height.equalTo(chipButtonHeight) } stack.addArrangedSubview(imageView) stack.addArrangedSubview(textLabel) stack.addArrangedSubview(iconView) imageView.snp.makeConstraints { make in make.size.equalTo(imageSize) } iconView.snp.makeConstraints { make in make.size.equalTo(iconSize) } imageView.isHidden = true iconView.isHidden = true } override var paddingInsets: UIEdgeInsets { UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16) } override var iconSize: CGSize { CGSize(width: 16, height: 16) } override var imageSize: CGSize { CGSize(width: 16, height: 16) } override var defaultIcon: UIImage? { let color = UIColor.c.ctsn // EPSystemToken.color(.txtSecondaryNormal) return MWIconFont.image(fromIconInt: iconCode.rawValue, size: iconSize, color: color, edgeInsets: .zero) } override var selectedIcon: UIImage? { let color = UIColor.c.cswn // EPSystemToken.color(.surfaceWhiteNormal) return MWIconFont.image(fromIconInt: iconCode.rawValue, size: iconSize, color: color, edgeInsets: .zero) } override var isSelected: Bool { didSet { iconView.image = isSelected ? selectedIcon : defaultIcon imageView.image = isSelected ? selectedImage : defaultImage } } func reloadPadding(showImage: Bool, showIcon: Bool) { var leftPadding: CGFloat = 16 var rightPadding: CGFloat = 16 if showImage { leftPadding = 12 } if showIcon { rightPadding = 12 } stack.snp.remakeConstraints { make in make.edges.equalTo(self).inset(UIEdgeInsets(top: 0, left: leftPadding, bottom: 0, right: rightPadding)) make.height.equalTo(chipButtonHeight) } } }