Skip to content

Instantly share code, notes, and snippets.

@RoshanNindrai
Last active February 13, 2019 06:00
Show Gist options
  • Save RoshanNindrai/860d13cb93233ef7cb84a10ae0e69eb9 to your computer and use it in GitHub Desktop.
Save RoshanNindrai/860d13cb93233ef7cb84a10ae0e69eb9 to your computer and use it in GitHub Desktop.
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
public struct Renderable {
private let handle: () -> Void
public init(_ handle: @escaping () -> Void) {
self.handle = handle
}
func render() {
handle()
}
}
public typealias Configuration = [String: Any]
public protocol JSONRepresentable {
var identifier: String { get }
func render(with configuration: Configuration) -> Renderable
}
extension JSONRepresentable where Self: NSObject {
public var identifier: String {
return String(describing: Self.classForCoder())
}
public func render(with configuration: Configuration) -> Renderable {
return Renderable {
let myReflection = Mirror(reflecting: self)
configuration.forEach { [myReflection] key, configuration in
// if we can configure the property using KVC else use mirroring
if Self.instancesRespond(to: Selector(key)) {
// For not straight forward keys like layer we need to perform it's render pass
if let element = self.value(forKey: key) as? JSONRepresentable,
let elementConfiguration = configuration as? Configuration {
element.render(with: elementConfiguration).render()
} else {
self.setValue(configuration, forKeyPath: key)
}
} else {
// There can't be two properties with same name, so taking the first one
if let property = myReflection.children.filter({ $0.label == key }).first {
if let renderable = property.value as? JSONRepresentable,
let elementConfiguration = configuration as? Configuration {
renderable.render(with: elementConfiguration).render()
}
} else {
print("Invalid '\(key)' key found while rendering \(self.identifier)")
}
}
}
}
}
}
extension CALayer: JSONRepresentable {}
extension UILabel: JSONRepresentable {}
class TestView: UIView, JSONRepresentable {
var titleLabel = UILabel()
var subtitleLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(titleLabel)
addSubview(subtitleLabel)
}
override func layoutSubviews() {
super.layoutSubviews()
titleLabel.frame = CGRect(origin: .zero, size: .init(width: 144, height: 44))
subtitleLabel.frame = CGRect(origin: .init(x: 0, y: 46), size: .init(width: 144, height: 44))
}
required init?(coder aDecoder: NSCoder) {
return nil
}
}
let view = TestView(frame: .init(origin: .zero, size: .init(width: 144, height: 144)))
view.backgroundColor = .red
let testViewRenderable = view.render(with: [
"backgroundColor": UIColor.darkGray,
"titleLabel": [
"text": "Hello",
"textAlignment": NSTextAlignment.center.rawValue,
"numberOfLines": 1,
"textColor": UIColor.white,
"layer": [
"borderColor": UIColor.blue.cgColor,
"borderWidth": 2
]
],
"subtitleLabel": [
"text": "World",
"textAlignment": NSTextAlignment.center.rawValue,
"numberOfLines": 1,
"textColor": UIColor.white,
"layer": [
"borderColor": UIColor.blue.cgColor,
"borderWidth": 2
]
],
"layer": [
"cornerRadius": 5,
"borderWidth": 1,
"borderColor": UIColor.red.cgColor
]
])
PlaygroundPage.current.liveView = view
testViewRenderable.render()
PlaygroundPage.current.needsIndefiniteExecution = true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment