Skip to content

Instantly share code, notes, and snippets.

@LinusU
Last active March 6, 2018 15:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LinusU/a733c27de2a9df1db898e44f8e1b45da to your computer and use it in GitHub Desktop.
Save LinusU/a733c27de2a9df1db898e44f8e1b45da to your computer and use it in GitHub Desktop.
React like renderer concept for Swift
import UIKit
import PlaygroundSupport
/**********************************************
* Library Types *
**********************************************/
protocol REElement {
func render() -> REElement?
func toNativeView() -> UIView?
}
class REComponent<Props>: REElement {
var children: [REElement]
var props: [Props]
var state: Any?
init(_ props: [Props], _ children: [REElement] = []) {
self.props = props
self.children = children
self.state = nil
}
func render() -> REElement? { return nil }
func toNativeView() -> UIView? { return nil }
}
/**********************************************
* Built-in components *
**********************************************/
enum RELabelProps {
case backgroundColor(UIColor)
case text(String)
case textColor(UIColor)
}
class RELabel: REComponent<RELabelProps> {
override func toNativeView() -> UIView? {
let view = UILabel()
for prop in props {
switch prop {
case .backgroundColor(let value): view.backgroundColor = value
case .text(let value): view.text = value
case .textColor(let value): view.textColor = value
}
}
return view
}
}
enum REStackProps {
case alignment(UIStackViewAlignment)
case axis(UILayoutConstraintAxis)
case distribution(UIStackViewDistribution)
}
class REStack: REComponent<REStackProps> {
override func toNativeView() -> UIView? {
let view = UIStackView()
view.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
for prop in props {
switch prop {
case .alignment(let value): view.alignment = value
case .axis(let value): view.axis = value
case .distribution(let value): view.distribution = value
}
}
for child in children {
RE_render(node: child, toTargetView: view)
}
return view
}
}
/**********************************************
* Rendering function *
**********************************************/
func RE_render(node: REElement, toTargetView targetView: UIView) {
if let view = node.toNativeView() {
if let stackedView = targetView as? UIStackView {
stackedView.addArrangedSubview(view)
} else {
targetView.addSubview(view)
}
return
}
if let child = node.render() {
RE_render(node: child, toTargetView: targetView)
return
}
}
/**********************************************
* User code *
**********************************************/
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let tree = REStack([.alignment(.center)], [
RELabel([.backgroundColor(.cyan), .text("Hello:")]),
RELabel([.textColor(.blue), .text("World!")])
])
RE_render(node: tree, toTargetView: view)
self.view = view
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment