Visual_Novel_iOS/crush/Crush/Src/Components/UI/Buttons/IconButtons.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)
}
}