import Foundation import UIKit import Display import ComponentFlow public final class ContextReferenceButtonComponent: Component { let content: AnyComponent let tag: AnyObject? let minSize: CGSize? let action: (UIView, ContextGesture?) -> Void public init( content: AnyComponent, tag: AnyObject? = nil, minSize: CGSize?, action: @escaping (UIView, ContextGesture?) -> Void ) { self.content = content self.tag = tag self.minSize = minSize self.action = action } public static func ==(lhs: ContextReferenceButtonComponent, rhs: ContextReferenceButtonComponent) -> Bool { if lhs.content != rhs.content { return false } if lhs.tag !== rhs.tag { return false } if lhs.minSize != rhs.minSize { return false } return true } public final class View: UIView, ComponentTaggedView { let buttonView: HighlightableButtonNode let sourceView: ContextControllerSourceNode let contextContentView: ContextReferenceContentNode private let componentView: ComponentView private var component: ContextReferenceButtonComponent? 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 } public init() { self.componentView = ComponentView() self.buttonView = HighlightableButtonNode() self.sourceView = ContextControllerSourceNode() self.contextContentView = ContextReferenceContentNode() super.init(frame: CGRect()) self.buttonView.allowsGroupOpacity = true self.addSubview(self.buttonView.view) self.buttonView.addSubnode(self.sourceView) self.sourceView.addSubnode(self.contextContentView) self.sourceView.activated = { [weak self] gesture, _ in if let self, let component = self.component { component.action(self, gesture) } } self.buttonView.addTarget(self, action: #selector(self.pressed), forControlEvents: .touchUpInside) } required init?(coder aDecoder: NSCoder) { preconditionFailure() } @objc private func pressed() { self.component?.action(self, nil) } public func update(component: ContextReferenceButtonComponent, availableSize: CGSize, transition: Transition) -> CGSize { self.component = component let componentSize = self.componentView.update( transition: transition, component: component.content, environment: {}, containerSize: availableSize ) var size = componentSize if let minSize = component.minSize { size.width = max(size.width, minSize.width) size.height = max(size.height, minSize.height) } if let componentView = self.componentView.view { componentView.isUserInteractionEnabled = false if componentView.superview == nil { self.contextContentView.view.addSubview(componentView) } transition.setFrame(view: componentView, frame: CGRect(origin: CGPoint(x: floor((size.width - componentSize.width) / 2.0), y: floor((size.height - componentSize.height) / 2.0)), size: componentSize)) } transition.setFrame(view: self.buttonView.view, frame: CGRect(origin: .zero, size: size)) transition.setFrame(view: self.sourceView.view, frame: CGRect(origin: .zero, size: size)) transition.setFrame(view: self.contextContentView.view, frame: CGRect(origin: .zero, size: size)) return size } } public func makeView() -> View { return View() } public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { return view.update(component: self, availableSize: availableSize, transition: transition) } }