Experimenting with bindings

This commit is contained in:
Selim Mustafaev 2023-08-02 19:02:06 +03:00
parent b64927ebc2
commit 1d57a17edc
4 changed files with 43 additions and 50 deletions

View File

@ -10,59 +10,55 @@ import Combine
open class ContainerView: UIView { open class ContainerView: UIView {
private var body: UIView = UIView()
public var cancellables: [AnyCancellable] = [] public var cancellables: [AnyCancellable] = []
public override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
body = getBody()
body.translatesAutoresizingMaskIntoConstraints = false
addSubview(body)
body.pin(to: self)
} }
required public init?(coder: NSCoder) { class BindingModel {
fatalError("init(coder:) has not been implemented")
}
@RootViewBuilder open func getBody() -> UIView { var srcTag: Int
UIView() var dstTag: Int
var bind: (ContainerView, Int, Int) -> Void
init(srcTag: Int, dstTag: Int, bind: @escaping (ContainerView, Int, Int) -> Void) {
self.srcTag = srcTag
self.dstTag = dstTag
self.bind = bind
} }
} }
extension UIView { extension UIView {
private func findContainer() -> ContainerView? { private struct AssociatedKeys {
static var bindings: Int = 0
var currentView: UIView = self
while currentView.superview != nil {
if currentView is ContainerView {
return currentView as? ContainerView
}
currentView = currentView.superview!
} }
return nil var bindings: [BindingModel] {
get {
objc_getAssociatedObject(self, &AssociatedKeys.bindings) as? [BindingModel] ?? []
}
set {
objc_setAssociatedObject(self,
&AssociatedKeys.bindings,
newValue,
objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
} }
public func bind<Src, SrcView, Dst, DstView>(_ prop: ReferenceWritableKeyPath<SrcView,Src>, public func bind<Src, SrcView, Dst, DstView>(_ dstProp: ReferenceWritableKeyPath<DstView,Dst>,
to dstTag: Int, to srcTag: Int,
dstProp: KeyPath<DstView,Published<Dst>.Publisher>) -> Self where SrcView: UIView, Src: InitConvertible, Src.Param == Dst { srcProp: KeyPath<SrcView,Published<Src>.Publisher>) -> Self where DstView: UIView, Dst: InitConvertible, Dst.Param == Src {
guard let container = findContainer(), let srcView = self as? SrcView else { bindings.append(.init(srcTag: srcTag, dstTag: tag, bind: { container, srcTag, dstTag in
return self guard let srcView = container.viewWithTag(srcTag) as? SrcView,
let dstView = container.viewWithTag(dstTag) as? DstView
else {
return
} }
guard let view = container.viewWithTag(dstTag) as? DstView else { let publisher = srcView[keyPath: srcProp].map { Dst($0) }
return self publisher.assign(to: dstProp, on: dstView)
}
let pub = view[keyPath: dstProp].map { Src($0) }
pub.assign(to: prop, on: srcView)
.store(in: &container.cancellables) .store(in: &container.cancellables)
}))
return self return self
} }

View File

@ -8,8 +8,7 @@
import UIKit import UIKit
import Combine import Combine
@available(iOS 13.0, *) public class Stack: ContainerView {
public class Stack: UIView {
public var arrangedSubviews: [UIView] public var arrangedSubviews: [UIView]
public var distribution: Distribution public var distribution: Distribution

View File

@ -3,8 +3,8 @@ import UIKit
@resultBuilder @resultBuilder
public struct RootViewBuilder { public struct RootViewBuilder {
public static func buildBlock(_ components: UIView...) -> UIView { public static func buildBlock(_ components: ContainerView...) -> ContainerView {
let view = components.first ?? UIView() let view = components.first ?? ContainerView()
return view.withoutAutoresizing() return view.withoutAutoresizing()
} }
} }

View File

@ -22,19 +22,17 @@ extension Stack.Alignment: InitConvertible {
class ViewController: UIViewController { class ViewController: UIViewController {
let child = TestView() var child = UIView()
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
child = buildView()
view.addSubview(child) view.addSubview(child)
child.pin(toSafeArea: view, insets: .init(all: 16)) child.pin(toSafeArea: view, insets: .init(all: 16))
} }
}
class TestView: ContainerView { @RootViewBuilder func buildView() -> ContainerView {
override func getBody() -> UIView {
VStack(alignment: .start, spacing: 16) { VStack(alignment: .start, spacing: 16) {
UILabel() UILabel()
.text("qwe") .text("qwe")
@ -49,7 +47,7 @@ class TestView: ContainerView {
YASegmentedControl(items: ["Left", "Center", "Right"]) YASegmentedControl(items: ["Left", "Center", "Right"])
.tag(100) .tag(100)
} }
.bind(\VStack.alignment, to: 100, dstProp: \YASegmentedControl.$selectedIndex) .bind(\VStack.alignment, to: 100, srcProp: \YASegmentedControl.$selectedIndex)
} }
} }