// // Button.swift // MVMCoreUI // // Created by Robinson, Blake on 12/18/19. // Copyright © 2019 Verizon Wireless. All rights reserved. // public typealias ButtonAction = (Button) -> () @objcMembers open class Button: UIButton, MFButtonProtocol, MoleculeViewProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- open var model: MoleculeModelProtocol? open var actionModel: ActionModelProtocol? private var initialSetupPerformed = false private var buttonAction: ButtonAction? //-------------------------------------------------- // MARK: - Delegate //-------------------------------------------------- open weak var buttonDelegate: ButtonDelegateProtocol? //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- public override init(frame: CGRect) { super.init(frame: .zero) initialSetup() } public convenience init() { self.init(frame: .zero) initialSetup() } public required init?(coder: NSCoder) { super.init(coder: coder) fatalError("Button does not support xib.") } //-------------------------------------------------- // MARK: - Setup //-------------------------------------------------- /// Required to be called any init. Ensures setupView() only gets called once public func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true setupView() } } //-------------------------------------------------- // MARK: - Methods //-------------------------------------------------- /// Adds a block to be performed for the given event. open func addActionBlock(event: Event, _ buttonBlock: @escaping ButtonAction) { self.buttonAction = buttonBlock addTarget(self, action: #selector(callActionBlock(_:)), for: event) } @objc func callActionBlock(_ sender: Button) { buttonAction?(self) } open func set(with actionModel: ActionModelProtocol, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { self.actionModel = actionModel buttonDelegate = delegateObject?.buttonDelegate addActionBlock(event: .touchUpInside) { [weak self] sender in guard let self = self else { return } if let data = try? actionModel.encode(using: JSONEncoder()), let actionMap = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.init()) as? [AnyHashable: Any], delegateObject?.buttonDelegate?.button?(self, shouldPerformActionWithMap: actionMap, additionalData: additionalData) ?? true { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) } } } // MARK:- MoleculeViewProtocol open func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { self.model = model if let backgroundColor = model.backgroundColor { self.backgroundColor = backgroundColor.uiColor } guard let model = model as? ButtonModelProtocol else { return } isEnabled = model.enabled set(with: model.action, delegateObject: delegateObject, additionalData: additionalData) } open class func nameForReuse(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> String? { return model.moleculeName } open class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { return nil } open class func requiredModules(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, error: AutoreleasingUnsafeMutablePointer?) -> [String]? { return nil } open func reset() { backgroundColor = .clear } } // MARK: - MVMCoreViewProtocol extension Button: MVMCoreViewProtocol { open func updateView(_ size: CGFloat) { } /// Will be called only once. open func setupView() { translatesAutoresizingMaskIntoConstraints = false insetsLayoutMarginsFromSafeArea = false titleLabel?.numberOfLines = 0 titleLabel?.lineBreakMode = .byWordWrapping } } // MARK: AppleGuidelinesProtocol extension Button: AppleGuidelinesProtocol { override open func point(inside point: CGPoint, with event: UIEvent?) -> Bool { return Self.acceptablyOutsideBounds(point: point, bounds: bounds) } }