Fixing binding
This commit is contained in:
parent
1d57a17edc
commit
1a4abe5a56
67
Sources/YadUI/Extensions/UIView/UIView+Binding.swift
Normal file
67
Sources/YadUI/Extensions/UIView/UIView+Binding.swift
Normal file
@ -0,0 +1,67 @@
|
||||
//
|
||||
// UIView+Binding.swift
|
||||
//
|
||||
//
|
||||
// Created by Мустафаев Селим Мустафаевич on 03.08.2023.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Combine
|
||||
|
||||
extension UIView {
|
||||
|
||||
private struct AssociatedKeys {
|
||||
static var bindings: Int = 0
|
||||
}
|
||||
|
||||
var bindings: NSMutableArray {
|
||||
get {
|
||||
guard let array = objc_getAssociatedObject(self, &AssociatedKeys.bindings) as? NSMutableArray else {
|
||||
let newArray = NSMutableArray()
|
||||
objc_setAssociatedObject(self,
|
||||
&AssociatedKeys.bindings,
|
||||
newArray,
|
||||
objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
print("New array: ", Unmanaged.passUnretained(newArray).toOpaque())
|
||||
return newArray
|
||||
}
|
||||
|
||||
print("Array: \(Unmanaged.passUnretained(array).toOpaque()), count: \(array.count)")
|
||||
return array
|
||||
}
|
||||
set {
|
||||
objc_setAssociatedObject(self,
|
||||
&AssociatedKeys.bindings,
|
||||
newValue,
|
||||
objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
}
|
||||
|
||||
public func bind<Src, SrcView, Dst, DstView>(_ dstProp: ReferenceWritableKeyPath<DstView,Dst>,
|
||||
to srcTag: Int,
|
||||
srcProp: KeyPath<SrcView,Published<Src>.Publisher>) -> Self where Dst: InitConvertible, Dst.Param == Src {
|
||||
|
||||
bindings.add(BindingModel(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 false
|
||||
}
|
||||
|
||||
let publisher = srcView[keyPath: srcProp].map { Dst($0) }
|
||||
publisher.assign(to: dstProp, on: dstView)
|
||||
.store(in: &container.cancellables)
|
||||
|
||||
return true
|
||||
}))
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func forEachSubview(_ closure: (UIView) -> Void) {
|
||||
for view in subviews {
|
||||
closure(view)
|
||||
view.forEachSubview(closure)
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Sources/YadUI/Models/BindingModel.swift
Normal file
21
Sources/YadUI/Models/BindingModel.swift
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// BindingModel.swift
|
||||
//
|
||||
//
|
||||
// Created by Мустафаев Селим Мустафаевич on 03.08.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class BindingModel {
|
||||
|
||||
var srcTag: Int
|
||||
var dstTag: Int
|
||||
var bind: (ContainerView, Int, Int) -> Bool
|
||||
|
||||
init(srcTag: Int, dstTag: Int, bind: @escaping (ContainerView, Int, Int) -> Bool) {
|
||||
self.srcTag = srcTag
|
||||
self.dstTag = dstTag
|
||||
self.bind = bind
|
||||
}
|
||||
}
|
||||
@ -11,55 +11,34 @@ import Combine
|
||||
open class ContainerView: UIView {
|
||||
|
||||
public var cancellables: [AnyCancellable] = []
|
||||
}
|
||||
|
||||
class BindingModel {
|
||||
func processBindings() {
|
||||
|
||||
var srcTag: Int
|
||||
var dstTag: Int
|
||||
var bind: (ContainerView, Int, Int) -> Void
|
||||
bindings.setArray(processBindings(from: bindings))
|
||||
|
||||
init(srcTag: Int, dstTag: Int, bind: @escaping (ContainerView, Int, Int) -> Void) {
|
||||
self.srcTag = srcTag
|
||||
self.dstTag = dstTag
|
||||
self.bind = bind
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
|
||||
private struct AssociatedKeys {
|
||||
static var bindings: Int = 0
|
||||
}
|
||||
|
||||
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>(_ 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
|
||||
forEachSubview { view in
|
||||
if let container = view as? ContainerView {
|
||||
container.processBindings()
|
||||
}
|
||||
|
||||
let publisher = srcView[keyPath: srcProp].map { Dst($0) }
|
||||
publisher.assign(to: dstProp, on: dstView)
|
||||
.store(in: &container.cancellables)
|
||||
}))
|
||||
bindings.addObjects(from: processBindings(from: view.bindings))
|
||||
}
|
||||
}
|
||||
|
||||
return self
|
||||
func processBindings(from array: NSArray) -> [BindingModel] {
|
||||
|
||||
var failedBindings: [BindingModel] = []
|
||||
|
||||
for binding in array {
|
||||
guard let binding = binding as? BindingModel else {
|
||||
continue
|
||||
}
|
||||
|
||||
if !binding.bind(self, binding.srcTag, binding.dstTag) {
|
||||
failedBindings.append(binding)
|
||||
}
|
||||
}
|
||||
|
||||
return failedBindings
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ public struct RootViewBuilder {
|
||||
|
||||
public static func buildBlock(_ components: ContainerView...) -> ContainerView {
|
||||
let view = components.first ?? ContainerView()
|
||||
view.processBindings()
|
||||
return view.withoutAutoresizing()
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,21 +33,24 @@ class ViewController: UIViewController {
|
||||
}
|
||||
|
||||
@RootViewBuilder func buildView() -> ContainerView {
|
||||
VStack(alignment: .start, spacing: 16) {
|
||||
UILabel()
|
||||
.text("qwe")
|
||||
.background(.blue)
|
||||
UILabel()
|
||||
.text("asdf")
|
||||
.background(.blue)
|
||||
UILabel()
|
||||
.text("zxcv")
|
||||
.background(.blue)
|
||||
.hugging(.defaultLowMinus)
|
||||
VStack(alignment: .fill, spacing: 16) {
|
||||
VStack(alignment: .start, spacing: 16) {
|
||||
UILabel()
|
||||
.text("qwe")
|
||||
.background(.blue)
|
||||
UILabel()
|
||||
.text("asdf")
|
||||
.background(.blue)
|
||||
UILabel()
|
||||
.text("zxcv")
|
||||
.background(.blue)
|
||||
.hugging(.defaultLowMinus)
|
||||
}
|
||||
.bind(\VStack.alignment, to: 100, srcProp: \YASegmentedControl.$selectedIndex)
|
||||
|
||||
YASegmentedControl(items: ["Left", "Center", "Right"])
|
||||
.tag(100)
|
||||
}
|
||||
.bind(\VStack.alignment, to: 100, srcProp: \YASegmentedControl.$selectedIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user