diff --git a/Sources/YadUI/Views/ContainerView.swift b/Sources/YadUI/Views/ContainerView.swift index 33c0b1c..79f9168 100644 --- a/Sources/YadUI/Views/ContainerView.swift +++ b/Sources/YadUI/Views/ContainerView.swift @@ -10,59 +10,55 @@ import Combine open class ContainerView: UIView { - private var body: UIView = UIView() public var cancellables: [AnyCancellable] = [] +} + +class BindingModel { - public override init(frame: CGRect) { - super.init(frame: frame) - - translatesAutoresizingMaskIntoConstraints = false - - body = getBody() - body.translatesAutoresizingMaskIntoConstraints = false - addSubview(body) - body.pin(to: self) - } + var srcTag: Int + var dstTag: Int + var bind: (ContainerView, Int, Int) -> Void - required public init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - @RootViewBuilder open func getBody() -> UIView { - UIView() + init(srcTag: Int, dstTag: Int, bind: @escaping (ContainerView, Int, Int) -> Void) { + self.srcTag = srcTag + self.dstTag = dstTag + self.bind = bind } } extension UIView { - private func findContainer() -> ContainerView? { - - var currentView: UIView = self - while currentView.superview != nil { - if currentView is ContainerView { - return currentView as? ContainerView - } - currentView = currentView.superview! - } - - return nil + private struct AssociatedKeys { + static var bindings: Int = 0 } - public func bind(_ prop: ReferenceWritableKeyPath, - to dstTag: Int, - dstProp: KeyPath.Publisher>) -> Self where SrcView: UIView, Src: InitConvertible, Src.Param == Dst { - - guard let container = findContainer(), let srcView = self as? SrcView else { - return self + var bindings: [BindingModel] { + get { + objc_getAssociatedObject(self, &AssociatedKeys.bindings) as? [BindingModel] ?? [] } - - guard let view = container.viewWithTag(dstTag) as? DstView else { - return self + set { + objc_setAssociatedObject(self, + &AssociatedKeys.bindings, + newValue, + objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } + + public func bind(_ dstProp: ReferenceWritableKeyPath, + to srcTag: Int, + srcProp: KeyPath.Publisher>) -> Self where DstView: UIView, Dst: InitConvertible, Dst.Param == Src { - let pub = view[keyPath: dstProp].map { Src($0) } - pub.assign(to: prop, on: srcView) - .store(in: &container.cancellables) + bindings.append(.init(srcTag: srcTag, dstTag: tag, bind: { container, srcTag, dstTag in + guard let srcView = container.viewWithTag(srcTag) as? SrcView, + let dstView = container.viewWithTag(dstTag) as? DstView + else { + return + } + + let publisher = srcView[keyPath: srcProp].map { Dst($0) } + publisher.assign(to: dstProp, on: dstView) + .store(in: &container.cancellables) + })) return self } diff --git a/Sources/YadUI/Views/Stack/Stack.swift b/Sources/YadUI/Views/Stack/Stack.swift index 70d7d9b..56ba1fa 100644 --- a/Sources/YadUI/Views/Stack/Stack.swift +++ b/Sources/YadUI/Views/Stack/Stack.swift @@ -8,8 +8,7 @@ import UIKit import Combine -@available(iOS 13.0, *) -public class Stack: UIView { +public class Stack: ContainerView { public var arrangedSubviews: [UIView] public var distribution: Distribution diff --git a/Sources/YadUI/YadUI.swift b/Sources/YadUI/YadUI.swift index 6c2b843..9e0c351 100644 --- a/Sources/YadUI/YadUI.swift +++ b/Sources/YadUI/YadUI.swift @@ -3,8 +3,8 @@ import UIKit @resultBuilder public struct RootViewBuilder { - public static func buildBlock(_ components: UIView...) -> UIView { - let view = components.first ?? UIView() + public static func buildBlock(_ components: ContainerView...) -> ContainerView { + let view = components.first ?? ContainerView() return view.withoutAutoresizing() } } diff --git a/YadUIDemo/YadUIDemo/ViewController.swift b/YadUIDemo/YadUIDemo/ViewController.swift index dc31685..041c132 100644 --- a/YadUIDemo/YadUIDemo/ViewController.swift +++ b/YadUIDemo/YadUIDemo/ViewController.swift @@ -22,19 +22,17 @@ extension Stack.Alignment: InitConvertible { class ViewController: UIViewController { - let child = TestView() + var child = UIView() override func viewDidLoad() { super.viewDidLoad() + child = buildView() view.addSubview(child) child.pin(toSafeArea: view, insets: .init(all: 16)) } -} - -class TestView: ContainerView { - override func getBody() -> UIView { + @RootViewBuilder func buildView() -> ContainerView { VStack(alignment: .start, spacing: 16) { UILabel() .text("qwe") @@ -49,7 +47,7 @@ class TestView: ContainerView { YASegmentedControl(items: ["Left", "Center", "Right"]) .tag(100) } - .bind(\VStack.alignment, to: 100, dstProp: \YASegmentedControl.$selectedIndex) + .bind(\VStack.alignment, to: 100, srcProp: \YASegmentedControl.$selectedIndex) } }