2023-08-08 23:23:47 +03:00

142 lines
5.6 KiB
Swift

import Foundation
import UIKit
import Display
import ComponentFlow
import AppBundle
public final class OptionButtonComponent: Component {
public struct Colors: Equatable {
public var background: UIColor
public var foreground: UIColor
public init(
background: UIColor,
foreground: UIColor
) {
self.background = background
self.foreground = foreground
}
}
public let colors: Colors
public let icon: String
public let action: () -> Void
public init(
colors: Colors,
icon: String,
action: @escaping () -> Void
) {
self.colors = colors
self.icon = icon
self.action = action
}
public static func ==(lhs: OptionButtonComponent, rhs: OptionButtonComponent) -> Bool {
if lhs.colors != rhs.colors {
return false
}
if lhs.icon != rhs.icon {
return false
}
return true
}
public final class View: HighlightTrackingButton {
private var component: OptionButtonComponent?
private let backgroundView: UIImageView
private let iconView: UIImageView
private let arrowView: UIImageView
override init(frame: CGRect) {
self.backgroundView = UIImageView()
self.iconView = UIImageView()
self.arrowView = UIImageView()
super.init(frame: frame)
self.addSubview(self.backgroundView)
self.addSubview(self.iconView)
self.addSubview(self.arrowView)
self.highligthedChanged = { [weak self] highlighed in
guard let self else {
return
}
let transition = Transition(animation: .curve(duration: 0.25, curve: .easeInOut))
let scale: CGFloat = highlighed ? 0.8 : 1.0
transition.setSublayerTransform(view: self, transform: CATransform3DMakeScale(scale, scale, 1.0))
}
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
}
@objc private func pressed() {
guard let component = self.component else {
return
}
component.action()
}
func update(component: OptionButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
let previousComponent = self.component
self.component = component
let size = CGSize(width: 52.0, height: 28.0)
if previousComponent?.colors.background != component.colors.background {
self.backgroundView.image = generateStretchableFilledCircleImage(diameter: size.height, color: component.colors.background)
}
if previousComponent?.icon != component.icon {
if previousComponent != nil, let previousImage = self.iconView.image {
let tempView = UIImageView(image: previousImage)
tempView.tintColor = component.colors.foreground
tempView.frame = self.iconView.frame
self.insertSubview(tempView, belowSubview: self.iconView)
tempView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak tempView] _ in
tempView?.removeFromSuperview()
})
tempView.layer.animateScale(from: 1.0, to: 0.2, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
self.iconView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.iconView.layer.animateScale(from: 0.2, to: 1.0, duration: 0.3)
}
self.iconView.image = UIImage(bundleImageName: component.icon)?.withRenderingMode(.alwaysTemplate)
}
if previousComponent == nil {
self.arrowView.image = UIImage(bundleImageName: "Stories/SelectorArrowDown")?.withRenderingMode(.alwaysOriginal)
}
if previousComponent?.colors.foreground != component.colors.foreground {
self.iconView.tintColor = component.colors.foreground
self.arrowView.tintColor = component.colors.foreground
}
if let iconSize = self.iconView.image?.size, let arrowSize = self.arrowView.image?.size {
transition.setFrame(view: self.iconView, frame: CGRect(origin: CGPoint(x: 3.0, y: floor((size.height - iconSize.height) * 0.5)), size: iconSize))
transition.setFrame(view: self.arrowView, frame: CGRect(origin: CGPoint(x: size.width - 8.0 - arrowSize.width, y: floor((size.height - arrowSize.height) * 0.5)), size: arrowSize))
}
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: size))
return size
}
}
public func makeView() -> View {
return View(frame: CGRect())
}
public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
}
}