492 lines
18 KiB
Swift
492 lines
18 KiB
Swift
//
|
|
// IconButtons.swift
|
|
// Crush
|
|
//
|
|
// Created by Leon on 2025/7/18.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
enum IconButtonStyle: UInt {
|
|
case primary
|
|
case secondary
|
|
case tertiary
|
|
case ghost
|
|
case tertiaryRoundDark
|
|
case tertiaryRoundLight
|
|
}
|
|
|
|
enum IconSize: Int {
|
|
/// 24->21
|
|
case large = 21
|
|
/// 20
|
|
case medium = 20
|
|
/// 16
|
|
case small = 16
|
|
/// 12
|
|
case xs = 12
|
|
/// 10
|
|
case xs10 = 10
|
|
/// 8
|
|
case xxs = 8
|
|
}
|
|
|
|
enum IconButtonRadius: UInt {
|
|
case none
|
|
case rectangle
|
|
case round
|
|
}
|
|
|
|
// Base class: Typically use subclasses below
|
|
class EPIconButton: CLButton {
|
|
var style: IconButtonStyle
|
|
var iconSize: IconSize
|
|
var iconCode: IconCode
|
|
var radius: IconButtonRadius
|
|
|
|
init(style: IconButtonStyle, radius: IconButtonRadius, iconSize: IconSize, iconCode: IconCode) {
|
|
self.style = style
|
|
self.iconSize = iconSize
|
|
self.iconCode = iconCode
|
|
self.radius = radius
|
|
super.init(frame: .zero)
|
|
|
|
clipsToBounds = true
|
|
setupRadius()
|
|
setupTapExpand()
|
|
setupTheme()
|
|
}
|
|
|
|
convenience init(radius: IconButtonRadius, iconSize: IconSize, iconCode: IconCode) {
|
|
self.init(style: .primary, radius: radius, iconSize: iconSize, iconCode: iconCode)
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
override init(frame: CGRect) {
|
|
fatalError("init(frame:) has not been implemented")
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
func update(radius: IconButtonRadius, iconSize: IconSize, iconCode: IconCode) {
|
|
self.iconSize = iconSize
|
|
self.iconCode = iconCode
|
|
self.radius = radius
|
|
setupRadius()
|
|
setupTapExpand()
|
|
setupTheme()
|
|
}
|
|
|
|
var iconCodeValue: IconCode {
|
|
get { iconCode }
|
|
set {
|
|
iconCode = newValue
|
|
setupTheme()
|
|
}
|
|
}
|
|
|
|
func setupTheme() {
|
|
// Override in subclasses
|
|
}
|
|
|
|
func setupRadius() {
|
|
switch radius {
|
|
case .rectangle:
|
|
layer.cornerRadius = 8
|
|
case .round:
|
|
layer.cornerRadius = bgImageSize().height * 0.5
|
|
case .none:
|
|
layer.cornerRadius = 0
|
|
break
|
|
}
|
|
}
|
|
|
|
func setupTapExpand() {
|
|
let bgImageSize = bgImageSize()
|
|
let expand = 48 - bgImageSize.width
|
|
touchAreaInsets = UIEdgeInsets(top: expand, left: expand, bottom: expand, right: expand)
|
|
}
|
|
|
|
func bgImageSize() -> CGSize {
|
|
switch iconSize {
|
|
case .large: return CGSize(width: 48, height: 48)
|
|
case .medium: return CGSize(width: 36, height: 36)
|
|
case .small: return CGSize(width: 32, height: 32)
|
|
case .xs: return CGSize(width: 24, height: 24)
|
|
case .xs10: return CGSize(width: 20, height: 20)
|
|
case .xxs: return CGSize(width: 16, height: 16)
|
|
}
|
|
}
|
|
|
|
func makeIcon(color: UIColor) -> UIImage {
|
|
return MWIconFont.image(fromIconInt: iconCode.rawValue, size: CGSize(width: iconSize.rawValue, height: iconSize.rawValue), color: color, edgeInsets: .zero)!
|
|
}
|
|
|
|
func makeBGImage(colors: UIColor) -> UIImage {
|
|
let bgImageSize = bgImageSize()
|
|
return UIImage.withColor(color: colors, size: bgImageSize)
|
|
}
|
|
|
|
func makeBGCircleBGImage(color: UIColor) -> UIImage {
|
|
let bgImageSize = bgImageSize()
|
|
let bounds = CGRect(origin: .zero, size: bgImageSize)
|
|
return UIImage.circleImage(withColor: color, bounds: bounds)!
|
|
}
|
|
|
|
func makeBGGradientImage(token: EPGradient) -> UIImage {
|
|
var colors: [UIColor] = []
|
|
if let firstColor = token.firstColor {
|
|
colors.append(firstColor)
|
|
}
|
|
if let secondColor = token.secondColor {
|
|
colors.append(secondColor)
|
|
}
|
|
let bgImageSize = bgImageSize()
|
|
return UIImage.gradientHImageWithSize(size: bgImageSize, colors: colors)
|
|
}
|
|
|
|
func setupTapExpandEdge(insets: UIEdgeInsets) {
|
|
touchAreaInsets = insets
|
|
}
|
|
}
|
|
|
|
// MARK: - EPIconPrimaryButton
|
|
|
|
class EPIconPrimaryButton: EPIconButton {
|
|
convenience init(radius: IconButtonRadius, iconSize: IconSize, iconCode: IconCode) {
|
|
self.init(style: .primary, radius: radius, iconSize: iconSize, iconCode: iconCode)
|
|
}
|
|
|
|
override func setupTheme() {
|
|
let normalColor = UIColor.c.ctpn // EPSystemToken.color(.txtPrimarySpecialmapNormal)
|
|
let disabledColor = UIColor.c.ctpd // EPSystemToken.color(.txtPrimaryDisabled)
|
|
|
|
setImage(makeIcon(color: normalColor), for: .normal)
|
|
setImage(makeIcon(color: normalColor), for: .highlighted)
|
|
setImage(makeIcon(color: disabledColor), for: .disabled)
|
|
|
|
let normalGradient = CLSystemToken.gradient(token: .cpgn) // EPSystemToken.gradient(.primaryGradientNormal)
|
|
let pressGradient = CLSystemToken.gradient(token: .cpgp) // EPSystemToken.gradient(.primaryGradientPress)
|
|
let disabledGradient = CLSystemToken.gradient(token: .csed) // EPSystemToken.gradient(.surfaceElementDisabled)
|
|
|
|
setBackgroundImage(makeBGGradientImage(token: normalGradient), for: .normal)
|
|
setBackgroundImage(makeBGGradientImage(token: pressGradient), for: .highlighted)
|
|
setBackgroundImage(makeBGGradientImage(token: disabledGradient), for: .disabled)
|
|
}
|
|
}
|
|
|
|
// MARK: - EPIconSecondaryButton
|
|
|
|
class EPIconSecondaryButton: EPIconButton {
|
|
convenience init(iconSize: IconSize, iconCode: IconCode) {
|
|
self.init(style: .secondary, radius: .none, iconSize: iconSize, iconCode: iconCode)
|
|
}
|
|
|
|
override func setupTheme() {
|
|
layer.borderColor = UIColor.c.cpvn.cgColor // EPSystemToken.color(.primaryVariantNormal).cgColor
|
|
|
|
let normalColor: UIColor = .c.cpvn // EPSystemToken.color(.primaryVariantNormal)
|
|
let disabledColor: UIColor = .c.cpvd // EPSystemToken.color(.primaryVariantDisabled)
|
|
let pressColor: UIColor = .c.cpvp // EPSystemToken.color(.primaryVariantPress)
|
|
|
|
setImage(makeIcon(color: normalColor), for: .normal)
|
|
setImage(makeIcon(color: disabledColor), for: .disabled)
|
|
setImage(makeIcon(color: pressColor), for: .highlighted)
|
|
}
|
|
|
|
override func bgImageSize() -> CGSize {
|
|
switch iconSize {
|
|
case .large: return CGSize(width: 48, height: 48)
|
|
case .medium: return CGSize(width: 36, height: 36)
|
|
case .small: return CGSize(width: 32, height: 32)
|
|
case .xs: return CGSize(width: 24, height: 24)
|
|
default: return .zero
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - EPIconTertiaryButton
|
|
|
|
class EPIconTertiaryButton: EPIconButton {
|
|
var optionalIconColor: UIColor? {
|
|
didSet {
|
|
if let color = optionalIconColor {
|
|
setImage(makeIcon(color: color), for: .normal)
|
|
setImage(makeIcon(color: color), for: .highlighted)
|
|
setImage(makeIcon(color: color), for: .selected)
|
|
}
|
|
}
|
|
}
|
|
|
|
var optionalBackgroundColor: UIColor? {
|
|
didSet {
|
|
if let color = optionalBackgroundColor {
|
|
setBackgroundImage(makeBGImage(colors: color), for: .normal)
|
|
setBackgroundImage(makeBGImage(colors: color), for: .disabled)
|
|
setBackgroundImage(makeBGImage(colors: color), for: .highlighted)
|
|
}
|
|
}
|
|
}
|
|
|
|
convenience init(radius: IconButtonRadius, iconSize: IconSize, iconCode: IconCode) {
|
|
self.init(style: .primary, radius: radius, iconSize: iconSize, iconCode: iconCode)
|
|
}
|
|
|
|
override func setupTheme() {
|
|
let normalColor: UIColor = .c.ctpn // EPSystemToken.color(.txtPrimaryNormal)
|
|
let disabledColor: UIColor = .c.ctd // EPSystemToken.color(.txtDisabled)
|
|
|
|
setImage(makeIcon(color: normalColor), for: .normal)
|
|
setImage(makeIcon(color: disabledColor), for: .disabled)
|
|
setImage(makeIcon(color: normalColor), for: .highlighted)
|
|
setImage(makeIcon(color: normalColor), for: .selected)
|
|
|
|
let normalBgColor: UIColor = .c.csen // EPSystemToken.color(.surfaceElementNormal)
|
|
let disabledBgColor: UIColor = .c.csed // EPSystemToken.color(.surfaceElementDisabled)
|
|
let pressBgColor: UIColor = .c.csep // EPSystemToken.color(.surfaceElementPress)
|
|
|
|
setBackgroundImage(makeBGImage(colors: normalBgColor), for: .normal)
|
|
setBackgroundImage(makeBGImage(colors: disabledBgColor), for: .disabled)
|
|
setBackgroundImage(makeBGImage(colors: pressBgColor), for: .highlighted)
|
|
}
|
|
|
|
func clearBackgroundImage() {
|
|
setBackgroundImage(nil, for: .normal)
|
|
}
|
|
|
|
func resumeBackgroundImage() {
|
|
let normalBgColor: UIColor = .c.csen // EPSystemToken.color(.surfaceElementNormal)
|
|
setBackgroundImage(makeBGImage(colors: normalBgColor), for: .normal)
|
|
}
|
|
}
|
|
|
|
// MARK: - EPIconGhostButton
|
|
|
|
class EPIconGhostButton: EPIconButton {
|
|
var optionalIconColor: UIColor? {
|
|
didSet {
|
|
if let color = optionalIconColor {
|
|
setImage(makeIcon(color: color), for: .normal)
|
|
setImage(makeIcon(color: color), for: .highlighted)
|
|
setImage(makeIcon(color: color), for: .selected)
|
|
}
|
|
}
|
|
}
|
|
|
|
convenience init(radius: IconButtonRadius, iconSize: IconSize, iconCode: IconCode) {
|
|
self.init(style: .ghost, radius: radius, iconSize: iconSize, iconCode: iconCode)
|
|
}
|
|
|
|
override func setupTheme() {
|
|
let normalColor: UIColor = .c.ctpn // EPSystemToken.color(.txtPrimaryNormal)
|
|
let disabledColor: UIColor = .c.ctd // EPSystemToken.color(.txtDisabled)
|
|
|
|
setImage(makeIcon(color: normalColor), for: .normal)
|
|
setImage(makeIcon(color: disabledColor), for: .disabled)
|
|
setImage(makeIcon(color: normalColor), for: .highlighted)
|
|
|
|
let pressBgColor: UIColor = .c.csep // EPSystemToken.color(.surfaceElementPress)
|
|
|
|
setBackgroundImage(nil, for: .normal)
|
|
setBackgroundImage(nil, for: .disabled)
|
|
setBackgroundImage(makeBGCircleBGImage(color: pressBgColor), for: .highlighted)
|
|
}
|
|
}
|
|
|
|
// MARK: - EPIconGhostSecondaryButton
|
|
|
|
class EPIconGhostSecondaryButton: EPIconButton {
|
|
convenience init(radius: IconButtonRadius, iconSize: IconSize, iconCode: IconCode) {
|
|
self.init(style: .ghost, radius: .round, iconSize: iconSize, iconCode: iconCode)
|
|
}
|
|
|
|
override func setupTheme() {
|
|
let normalColor: UIColor = .c.ctsn // EPSystemToken.color(.txtSecondaryNormal)
|
|
let disabledColor: UIColor = .c.ctsd // EPSystemToken.color(.txtSecondaryDisabled)
|
|
|
|
setImage(makeIcon(color: normalColor), for: .normal)
|
|
setImage(makeIcon(color: disabledColor), for: .disabled)
|
|
setImage(makeIcon(color: normalColor), for: .highlighted)
|
|
|
|
let pressBgColor: UIColor = .c.csep // EPSystemToken.color(.surfaceElementPress)
|
|
|
|
setBackgroundImage(nil, for: .normal)
|
|
setBackgroundImage(nil, for: .disabled)
|
|
setBackgroundImage(makeBGCircleBGImage(color: pressBgColor), for: .highlighted)
|
|
}
|
|
}
|
|
|
|
// MARK: - EPIconContrastTertiaryButton
|
|
|
|
class EPIconContrastTertiaryButton: EPIconButton {
|
|
convenience init(radius: IconButtonRadius, iconSize: IconSize, iconCode: IconCode) {
|
|
self.init(style: .ghost, radius: .round, iconSize: iconSize, iconCode: iconCode)
|
|
}
|
|
|
|
override func setupTheme() {
|
|
let normalColor: UIColor = .c.ctpn // EPSystemToken.color(.txtPrimaryNormal)
|
|
let disabledColor: UIColor = .c.ctd // EPSystemToken.color(.txtDisabled)
|
|
|
|
setImage(makeIcon(color: normalColor), for: .normal)
|
|
setImage(makeIcon(color: disabledColor), for: .disabled)
|
|
setImage(makeIcon(color: normalColor), for: .highlighted)
|
|
setImage(makeIcon(color: normalColor), for: .selected)
|
|
|
|
let normalBgColor: UIColor = .c.csedn // EPSystemToken.color(.surfaceElementDarkNormal)
|
|
let disabledBgColor: UIColor = .c.csedd // EPSystemToken.color(.surfaceElementDarkDisabled)
|
|
let pressBgColor: UIColor = .c.csedp // EPSystemToken.color(.surfaceElementDarkPress)
|
|
|
|
setBackgroundImage(makeBGImage(colors: normalBgColor), for: .normal)
|
|
setBackgroundImage(makeBGImage(colors: disabledBgColor), for: .disabled)
|
|
setBackgroundImage(makeBGImage(colors: pressBgColor), for: .highlighted)
|
|
}
|
|
}
|
|
|
|
// MARK: - EPFloatingButton
|
|
|
|
class EPFloatingButton: EPIconButton {
|
|
convenience init(radius: IconButtonRadius, iconSize: IconSize, iconCode: IconCode) {
|
|
self.init(style: .ghost, radius: .round, iconSize: iconSize, iconCode: iconCode)
|
|
}
|
|
|
|
override func setupTheme() {
|
|
setupNormalMode()
|
|
}
|
|
|
|
func setupNormalMode() {
|
|
let normalColor: UIColor = .c.ctpn // EPSystemToken.color(.txtPrimaryNormal)
|
|
let disabledColor: UIColor = .c.ctpsd // EPSystemToken.color(.txtPrimarySpecialmapDisable)
|
|
|
|
setImage(makeIcon(color: normalColor), for: .normal)
|
|
setImage(makeIcon(color: disabledColor), for: .disabled)
|
|
setImage(makeIcon(color: normalColor), for: .highlighted)
|
|
|
|
let pressGradient = CLSystemToken.gradient(token: .cpgp) // EPSystemToken.gradient(.primaryGradientPress)
|
|
let gradient = CLSystemToken.gradient(token: .cpgn) // EPSystemToken.gradient(.primaryGradientNormal)
|
|
let disabledBgColor = UIColor.c.csfn // EPSystemToken.color(.surfaceFloatNormal)
|
|
|
|
setBackgroundImage(gradient.toImage(size: CGSize(width: 10, height: 10)), for: .normal)
|
|
setBackgroundImage(makeBGImage(colors: disabledBgColor), for: .disabled)
|
|
setBackgroundImage(pressGradient.toImage(size: CGSize(width: 10, height: 10)), for: .highlighted)
|
|
|
|
layer.removeAllAnimations()
|
|
}
|
|
|
|
func setupMatchingState() {
|
|
let normalColor: UIColor = .c.ctpn // EPSystemToken.color(.txtPrimaryNormal)
|
|
let disabledColor: UIColor = .c.ctd // EPSystemToken.color(.txtDisabled)
|
|
|
|
setImage(makeIcon(color: normalColor), for: .normal)
|
|
setImage(makeIcon(color: disabledColor), for: .disabled)
|
|
setImage(makeIcon(color: normalColor), for: .highlighted)
|
|
|
|
let pressGradient = CLSystemToken.gradient(token: .cpgp) // EPSystemToken.gradient(.positiveGradientPress)
|
|
let gradient = CLSystemToken.gradient(token: .cpgn) // EPSystemToken.gradient(.positiveGradientNormal)
|
|
let disabledBgColor: UIColor = .c.csfn // EPSystemToken.color(.surfaceFloatNormal)
|
|
|
|
setBackgroundImage(gradient.toImage(size: CGSize(width: 10, height: 10)), for: .normal)
|
|
setBackgroundImage(makeBGImage(colors: disabledBgColor), for: .disabled)
|
|
setBackgroundImage(pressGradient.toImage(size: CGSize(width: 10, height: 10)), for: .highlighted)
|
|
|
|
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
|
|
rotationAnimation.toValue = NSNumber(value: Double.pi * 2.0)
|
|
rotationAnimation.duration = 1.5
|
|
rotationAnimation.isRemovedOnCompletion = false
|
|
rotationAnimation.isCumulative = true
|
|
rotationAnimation.repeatCount = .infinity
|
|
layer.add(rotationAnimation, forKey: "rotationAnimation")
|
|
}
|
|
|
|
override func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
if radius == .round {
|
|
layer.cornerRadius = bounds.size.height * 0.5
|
|
}
|
|
}
|
|
|
|
deinit {
|
|
layer.removeAllAnimations()
|
|
}
|
|
}
|
|
|
|
// MARK: - EPIconTertiaryDarkButton
|
|
|
|
class EPIconTertiaryDarkButton: EPIconButton {
|
|
convenience init(radius: IconButtonRadius, iconSize: IconSize, iconCode: IconCode) {
|
|
self.init(style: .tertiaryRoundDark, radius: radius, iconSize: iconSize, iconCode: iconCode)
|
|
}
|
|
|
|
override func setupTheme() {
|
|
let normalColor: UIColor = .c.ctpsn // EPSystemToken.color(.txtPrimarySpecialmapNormal)
|
|
let disabledColor: UIColor = .c.ctpd // EPSystemToken.color(.txtPrimaryDisabled)
|
|
|
|
setImage(makeIcon(color: normalColor), for: .normal)
|
|
setImage(makeIcon(color: disabledColor), for: .disabled)
|
|
setImage(makeIcon(color: normalColor), for: .highlighted)
|
|
|
|
let normalBgColor: UIColor = .c.csedn // EPSystemToken.color(.surfaceElementDarkNormal)
|
|
let disabledBgColor: UIColor = .c.csedd // EPSystemToken.color(.surfaceElementDarkDisabled)
|
|
let pressBgColor: UIColor = .c.csedp // EPSystemToken.color(.surfaceElementDarkPress)
|
|
|
|
setBackgroundImage(makeBGImage(colors: normalBgColor), for: .normal)
|
|
setBackgroundImage(makeBGImage(colors: disabledBgColor), for: .disabled)
|
|
setBackgroundImage(makeBGImage(colors: pressBgColor), for: .highlighted)
|
|
}
|
|
|
|
func clearBackgroundImage() {
|
|
setBackgroundImage(nil, for: .normal)
|
|
}
|
|
|
|
func resumeBackgroundImage() {
|
|
let normalBgColor:UIColor = .c.csedn//EPSystemToken.color(.surfaceElementDarkNormal)
|
|
setBackgroundImage(makeBGImage(colors: normalBgColor), for: .normal)
|
|
}
|
|
}
|
|
|
|
// MARK: - EPIconTertiaryLightButton
|
|
|
|
class EPIconTertiaryLightButton: EPIconButton {
|
|
convenience init(radius: IconButtonRadius, iconSize: IconSize, iconCode: IconCode) {
|
|
self.init(style: .tertiaryRoundLight, radius: radius, iconSize: iconSize, iconCode: iconCode)
|
|
}
|
|
|
|
override func setupTheme() {
|
|
let normalColor: UIColor = .c.ctpsn//EPSystemToken.color(.txtPrimarySpecialmapNormal)
|
|
let disabledColor: UIColor = .c.ctpsd//EPSystemToken.color(.txtPrimarySpecialmapDisable)
|
|
|
|
setImage(makeIcon(color: normalColor), for: .normal)
|
|
setImage(makeIcon(color: disabledColor), for: .disabled)
|
|
setImage(makeIcon(color: normalColor), for: .highlighted)
|
|
|
|
let normalBgColor: UIColor = .c.cseln//EPSystemToken.color(.surfaceElementLightNormal)
|
|
let disabledBgColor: UIColor = .c.cseld//EPSystemToken.color(.surfaceElementLightDisabled)
|
|
let pressBgColor: UIColor = .c.cselp//EPSystemToken.color(.surfaceElementLightPress)
|
|
|
|
setBackgroundImage(makeBGImage(colors: normalBgColor), for: .normal)
|
|
setBackgroundImage(makeBGImage(colors: disabledBgColor), for: .disabled)
|
|
setBackgroundImage(makeBGImage(colors: pressBgColor), for: .highlighted)
|
|
}
|
|
}
|
|
|
|
class EPIconDestructiveButton : EPIconButton{
|
|
override func setupTheme() {
|
|
let normalColor: UIColor = .c.ctpn
|
|
let disabledColor: UIColor = .c.ctpd
|
|
|
|
setImage(makeIcon(color: normalColor), for: .normal)
|
|
setImage(makeIcon(color: disabledColor), for: .disabled)
|
|
setImage(makeIcon(color: normalColor), for: .highlighted)
|
|
|
|
let normalBgColor: UIColor = .c.cin
|
|
let disabledBgColor: UIColor = .c.cid
|
|
let pressBgColor: UIColor = .c.cip
|
|
|
|
setBackgroundImage(makeBGImage(colors: normalBgColor), for: .normal)
|
|
setBackgroundImage(makeBGImage(colors: disabledBgColor), for: .disabled)
|
|
setBackgroundImage(makeBGImage(colors: pressBgColor), for: .highlighted)
|
|
}
|
|
}
|