Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/alexdrone/Render
UIKit a-là SwiftUI.framework [min deployment target iOS10]
https://github.com/alexdrone/Render
elm-architecture ios layout-engine reconciliation swift swiftui uikit uiview unidirectional-data-flow virtual-dom
Last synced: 6 days ago
JSON representation
UIKit a-là SwiftUI.framework [min deployment target iOS10]
- Host: GitHub
- URL: https://github.com/alexdrone/Render
- Owner: alexdrone
- Created: 2016-04-13T08:39:19.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2019-12-13T14:45:11.000Z (almost 5 years ago)
- Last Synced: 2024-11-21T07:22:45.933Z (21 days ago)
- Topics: elm-architecture, ios, layout-engine, reconciliation, swift, swiftui, uikit, uiview, unidirectional-data-flow, virtual-dom
- Language: Swift
- Homepage:
- Size: 871 MB
- Stars: 2,161
- Watchers: 38
- Forks: 95
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
- awesome-ios - Render - Swift and UIKit a la React. (Reactive Programming / React-Like)
- awesome-ios-star - Render - Swift and UIKit a la React. (Reactive Programming / React-Like)
- awesome-ios-architecture - Renderer
- awesome-github-star - Render - là SwiftUI.framework [min deployment target iOS10] | alexdrone | 2159 | (Swift)
- awesome - Render - UIKit a-là SwiftUI.framework [min deployment target iOS10] (virtual-dom)
- awesome - Render - UIKit a-là SwiftUI.framework [min deployment target iOS10] (unidirectional-data-flow)
README
# Render [![Swift](https://img.shields.io/badge/swift-5.1-orange.svg?style=flat)](#) [![ObjC++](https://img.shields.io/badge/ObjC++-blue.svg?style=flat)](#) [![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://opensource.org/licenses/MIT)
CoreRender is a SwiftUI inspired API for UIKit (that is compatible with iOS 10+ and ObjC).
### Introduction
* **Declarative:** CoreRender uses a declarative API to define UI components. You simply describe the layout for your UI based on a set of inputs and the framework takes care of the rest (*diff* and *reconciliation* from virtual view hierarchy to the actual one under the hood).
* **Flexbox layout:** CoreRender includes the robust and battle-tested Facebook's [Yoga](https://facebook.github.io/yoga/) as default layout engine.
* **Fine-grained recycling:** Any component such as a text or image can be recycled and reused anywhere in the UI.### TL;DR
Let's build the classic *Counter-Example*.
The DSL to define the vdom representation is similiar to SwiftUI.
```swift
func makeCounterBodyFragment(context: Context, coordinator: CounterCoordinator) -> OpaqueNodeBuilder {
Component(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` and `Button` are just specialized versions of the `Node` pure function.
That means you could wrap any UIView subclass in a vdom node. e.g.
```swiftNode(UIScrollView.self) {
Node(UILabel.self).withLayoutSpec { spec in
// This is where you can have all sort of custom view configuration.
}
Node(UISwitch.self)
}```
The `withLayoutSpec` modifier allows to specify a custom configuration closure for your view.`Coordinators` are the only non-transient objects in CoreRender. They yeld the view internal state and
they are able to manually access to the concrete view hierarchy (if one desires to do so).
By calling `setNeedsReconcile` the vdom is being recomputed and reconciled against the concrete view hiearchy.```swift
class CounterCoordinator: Coordinator{
var count: UInt = 0func incrementCounter() {
self.count += 1 // Update the state.
setNeedsReconcile() // Trigger the reconciliation algorithm on the view hiearchy associated to this coordinator.
}
}
```Finally, `Components` are yet again transient value types that bind together a body fragment with a
given coordinator.```swift
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 can be nested in the node hierarchy.
```swift
func makeFragment(context: Context) {
Component(context: context) { context, coordinator in
VStackNode {
LabelNode(text: "Foo")
Component(context: context) { context, coordinator in
HStackNode {
LabelNode(text: "Bar")
LabelNode(text: "Baz")
}
}
}
}
}```
### Use it with SwiftUI
Render nodes can be nested inside SwiftUI bodies by using `CoreRenderBridgeView`:
```swiftstruct 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()
}
}```
# Credits:
Layout engine:* [facebook/yoga](https://github.com/facebook/yoga)