CoreRender 是一个受 SwiftUI 启发的 UIKit API(兼容 iOS 10+ 和 ObjC)。
让我们构建经典的计数器示例。
用于定义 vdom 表示的 DSL 与 SwiftUI 类似。
func makeCounterBodyFragment(context: Context, coordinator: CounterCoordinator) -> OpaqueNodeBuilder {
Component<CounterCoordinator>(context: context) { context, coordinator in
VStackNode {
LabelNode(text: "\(coordinator.count)")
.textColor(.darkText)
.background(.secondarySystemBackground)
.width(Const.size + 8 * CGFloat(coordinator.count))
.height(Const.size)
.margin(Const.margin)
.cornerRadius(Const.cornerRadius)
HStackNode {
ButtonNode()
.text("TAP HERE TO INCREASE COUNT")
.setTarget(coordinator, action: #selector(CounterCoordinator.increase), for: .touchUpInside)
.background(.systemTeal)
.padding(Const.margin * 2)
.cornerRadius(Const.cornerRadius)
}
}
.alignItems(.center)
.matchHostingViewWidth(withMargin: 0)
}
}
Label
和 Button
只是 Node<V: UIView>
纯函数的专门版本。 这意味着您可以将任何 UIView 子类包装在 vdom 节点中。 例如:
Node(UIScrollView.self) {
Node(UILabel.self).withLayoutSpec { spec in
// This is where you can have all sort of custom view configuration.
}
Node(UISwitch.self)
}
withLayoutSpec
修饰符允许为您的视图指定自定义配置闭包。
Coordinators
是 CoreRender 中唯一非瞬态的对象。 它们保存视图的内部状态,并且能够手动访问具体的视图层级(如果需要)。
通过调用 setNeedsReconcile
,vdom 将被重新计算并与具体的视图层级进行协调。
class CounterCoordinator: Coordinator{
var count: UInt = 0
func incrementCounter() {
self.count += 1 // Update the state.
setNeedsReconcile() // Trigger the reconciliation algorithm on the view hiearchy associated to this coordinator.
}
}
最后,Components
再次是瞬态值类型,它将一个主体片段与给定的协调器绑定在一起。
class CounterViewCoordinator: UIViewController {
var hostingView: HostingView!
let context = Context()
override func loadView() {
hostingView = HostingView(context: context, with: [.useSafeAreaInsets]) { context in
makeCounterBodyFragment(context: context, coordinator: coordinator)
}
self.view = hostingView
}
override func viewDidLayoutSubviews() {
hostingView.setNeedsLayout()
}
}
Components 可以嵌套在节点层级中。
func makeFragment(context: Context) {
Component<FooCoordinator>(context: context) { context, coordinator in
VStackNode {
LabelNode(text: "Foo")
Component<BarCoordinator>(context: context) { context, coordinator in
HStackNode {
LabelNode(text: "Bar")
LabelNode(text: "Baz")
}
}
}
}
}
通过使用 CoreRenderBridgeView
,可以将 Render 节点嵌套在 SwiftUI 主体内部。
struct ContentView: View {
var body: some View {
VStack {
Text("Hello From SwiftUI")
CoreRenderBridgeView { context in
VStackNode {
LabelNode(text: "Hello")
LabelNode(text: "From")
LabelNode(text: "CoreRender")
}
.alignItems(.center)
.background(UIColor.systemGroupedBackground)
.matchHostingViewWidth(withMargin: 0)
}
Text("Back to SwiftUI")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
布局引擎