Experimenting with bindings
This commit is contained in:
parent
b64927ebc2
commit
1d57a17edc
@ -10,59 +10,55 @@ import Combine
|
||||
|
||||
open class ContainerView: UIView {
|
||||
|
||||
private var body: UIView = UIView()
|
||||
public var cancellables: [AnyCancellable] = []
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
class BindingModel {
|
||||
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
var srcTag: Int
|
||||
var dstTag: Int
|
||||
var bind: (ContainerView, Int, Int) -> Void
|
||||
|
||||
body = getBody()
|
||||
body.translatesAutoresizingMaskIntoConstraints = false
|
||||
addSubview(body)
|
||||
body.pin(to: self)
|
||||
}
|
||||
|
||||
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<Src, SrcView, Dst, DstView>(_ prop: ReferenceWritableKeyPath<SrcView,Src>,
|
||||
to dstTag: Int,
|
||||
dstProp: KeyPath<DstView,Published<Dst>.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)
|
||||
}
|
||||
}
|
||||
|
||||
let pub = view[keyPath: dstProp].map { Src($0) }
|
||||
pub.assign(to: prop, on: srcView)
|
||||
.store(in: &container.cancellables)
|
||||
public func bind<Src, SrcView, Dst, DstView>(_ dstProp: ReferenceWritableKeyPath<DstView,Dst>,
|
||||
to srcTag: Int,
|
||||
srcProp: KeyPath<SrcView,Published<Src>.Publisher>) -> Self where DstView: UIView, Dst: InitConvertible, Dst.Param == Src {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user