import Foundation import UIKit import Display import ComponentFlow final class ZoomComponent: Component { let availableValues: [Float] let value: Float let tag: AnyObject? init( availableValues: [Float], value: Float, tag: AnyObject? ) { self.availableValues = availableValues self.value = value self.tag = tag } static func ==(lhs: ZoomComponent, rhs: ZoomComponent) -> Bool { if lhs.availableValues != rhs.availableValues { return false } if lhs.value != rhs.value { return false } return true } final class View: UIView, UIGestureRecognizerDelegate, ComponentTaggedView { final class ItemView: HighlightTrackingButton { init() { super.init(frame: .zero) self.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.3) if #available(iOS 13.0, *) { self.layer.cornerCurve = .circular } self.layer.cornerRadius = 18.5 } required init(coder: NSCoder) { preconditionFailure() } func update(value: String, selected: Bool) { self.setAttributedTitle(NSAttributedString(string: value, font: Font.with(size: 13.0, design: .round, weight: .semibold), textColor: selected ? UIColor(rgb: 0xf8d74a) : .white, paragraphAlignment: .center), for: .normal) } } private let backgroundView: BlurredBackgroundView private var itemViews: [ItemView] = [] private var component: ZoomComponent? public func matches(tag: Any) -> Bool { if let component = self.component, let componentTag = component.tag { let tag = tag as AnyObject if componentTag === tag { return true } } return false } init() { self.backgroundView = BlurredBackgroundView(color: UIColor(rgb: 0x222222, alpha: 0.3)) self.backgroundView.clipsToBounds = true self.backgroundView.layer.cornerRadius = 43.0 / 2.0 super.init(frame: CGRect()) self.layer.allowsGroupOpacity = true self.addSubview(self.backgroundView) let pressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.handlePress(_:))) pressGestureRecognizer.minimumPressDuration = 0.01 pressGestureRecognizer.delegate = self self.addGestureRecognizer(pressGestureRecognizer) let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:))) panGestureRecognizer.delegate = self self.addGestureRecognizer(panGestureRecognizer) } required init?(coder aDecoder: NSCoder) { preconditionFailure() } @objc func handlePress(_ gestureRecognizer: UILongPressGestureRecognizer) { } @objc func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) { } override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return true } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } func animateIn() { self.backgroundView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) } func animateOut() { self.backgroundView.alpha = 0.0 self.backgroundView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) } func update(component: ZoomComponent, availableSize: CGSize, transition: ComponentTransition) -> CGSize { self.component = component let sideInset: CGFloat = 3.0 let spacing: CGFloat = 3.0 let buttonSize = CGSize(width: 37.0, height: 37.0) let size: CGSize = CGSize(width: buttonSize.width * CGFloat(component.availableValues.count) + spacing * CGFloat(component.availableValues.count - 1) + sideInset * 2.0, height: 43.0) var i = 0 var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: 3.0), size: buttonSize) for value in component.availableValues { let itemView: ItemView if self.itemViews.count == i { itemView = ItemView() self.addSubview(itemView) self.itemViews.append(itemView) } else { itemView = self.itemViews[i] } let text: String if value > 0.5 { if value == 1.0 { text = "1×" } else { text = "\(Int(value))" } } else { text = String(format: "%0.1f", value) } itemView.update(value: text, selected: value == 1.0) itemView.bounds = CGRect(origin: .zero, size: itemFrame.size) itemView.center = CGPoint(x: itemFrame.midX, y: itemFrame.midY) if value == 1.0 { itemView.transform = CGAffineTransformIdentity } else { itemView.transform = CGAffineTransform(scaleX: 0.7, y: 0.7) } i += 1 itemFrame = itemFrame.offsetBy(dx: buttonSize.width + spacing, dy: 0.0) } transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: size)) self.backgroundView.update(size: size, transition: transition.containedViewLayoutTransition) return size } } func makeView() -> View { return View() } func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { return view.update(component: self, availableSize: availableSize, transition: transition) } }