diff --git a/Bento/Decorators/CustomInputComponent.swift b/Bento/Decorators/CustomInputComponent.swift index 2795ddc..232741a 100644 --- a/Bento/Decorators/CustomInputComponent.swift +++ b/Bento/Decorators/CustomInputComponent.swift @@ -1,6 +1,7 @@ import UIKit extension Renderable { + /// Sets up `customInput` to prepare for presention when user taps `self`. public func customInput( _ input: CustomInput, contentStatus: FocusEligibility.ContentStatus = .empty, @@ -9,37 +10,65 @@ extension Renderable { return CustomInputComponent( source: self, customInput: input, - contentStatus: contentStatus, - highlightColor: highlightColor + focusEligibility: .eligible(contentStatus), + highlightColor: highlightColor, + focusesOnFirstDisplay: false + ).asAnyRenderable() + } + + /// Sets up `customInput` to prepare for presention when user taps `self`, + /// and also automatically presents it immediately when `input` is `.some`. + /// + /// - important: This method is useful when `customInput` needs to be displayed asynchronously after state change. + /// - note: Due to asynchronous presentation, `focusEligibility` is not supported. + public func autodisplayingCustomInput( + _ input: CustomInput?, + highlightColor: UIColor? = UIColor(red: 239/255.0, green: 239/255.0, blue: 244/255.0, alpha: 1) + ) -> AnyRenderable { + return CustomInputComponent( + source: self, + customInput: input, + focusEligibility: .ineligible, + highlightColor: highlightColor, + focusesOnFirstDisplay: input != nil ).asAnyRenderable() } } struct CustomInputComponent: Renderable, Focusable { - let customInput: CustomInput + let customInput: CustomInput? let focusEligibility: FocusEligibility let highlightColor: UIColor? + let focusesOnFirstDisplay: Bool + let base: AnyRenderable init( source: Base, - customInput: CustomInput, - contentStatus: FocusEligibility.ContentStatus, - highlightColor: UIColor? + customInput: CustomInput?, + focusEligibility: FocusEligibility, + highlightColor: UIColor?, + focusesOnFirstDisplay: Bool ) { self.customInput = customInput self.highlightColor = highlightColor self.base = AnyRenderable(source) - self.focusEligibility = .eligible(contentStatus) + self.focusEligibility = focusEligibility + self.focusesOnFirstDisplay = focusesOnFirstDisplay } func render(in view: ComponentView) { view.inputNodes = customInput + view.isFocusEnabled = focusEligibility.isEligible(skipsPopulatedComponents: false) view.highlightingGesture.didTap = .manual view.highlightingGesture.highlightColor = highlightColor view.highlightingGesture.stylingView = view.containedView view.bind(base) + + if focusesOnFirstDisplay && view.canBecomeFirstResponder && !view.isFirstResponder { + _ = view.becomeFirstResponder() + } } func willDisplay(_ view: CustomInputComponent.ComponentView) { @@ -65,6 +94,8 @@ extension CustomInputComponent { } } + fileprivate(set) var isFocusEnabled: Bool = true + var customInputView: InputView? var focusToolbar: FocusToolbar? var component: AnyRenderable? @@ -103,7 +134,7 @@ extension CustomInputComponent { public override func becomeFirstResponder() -> Bool { if let nodes = inputNodes { customInputView = InputView() - focusToolbar = FocusToolbar(view: self) + focusToolbar = FocusToolbar(view: self, isFocusEnabled: isFocusEnabled) customInputView!.update(nodes) } diff --git a/Bento/Views/FocusToolbar.swift b/Bento/Views/FocusToolbar.swift index 6fe5af1..74463a5 100644 --- a/Bento/Views/FocusToolbar.swift +++ b/Bento/Views/FocusToolbar.swift @@ -6,7 +6,7 @@ public final class FocusToolbar: UIToolbar { private let doneButton: UIBarButtonItem private var view: (UIView & FocusableView)? - public init(view: UIView & FocusableView) { + public init(view: UIView & FocusableView, isFocusEnabled: Bool = true) { backwardButton = UIBarButtonItem( image: UIImage(named: "arrow_down", in: Resources.bundle, compatibleWith: nil)!, style: .plain, @@ -31,12 +31,16 @@ public final class FocusToolbar: UIToolbar { forwardButton.action = #selector(forwardButtonPressed) doneButton.target = self doneButton.action = #selector(doneButtonPressed) - let items = [ - forwardButton, - backwardButton, + + var items = [UIBarButtonItem]() + if isFocusEnabled { + items.append(contentsOf: [forwardButton, backwardButton]) + } + items.append(contentsOf: [ UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil), doneButton - ] + ]) + setItems(items, animated: false) updateFocusEligibility(with: view) }