Skip to content

Instantly share code, notes, and snippets.

@kateinoigakukun
Created September 29, 2019 11:08
Show Gist options
  • Save kateinoigakukun/9de0b01aa22173cc19aed3e306c5d0cd to your computer and use it in GitHub Desktop.
Save kateinoigakukun/9de0b01aa22173cc19aed3e306c5d0cd to your computer and use it in GitHub Desktop.
import UIKit
protocol View {
func body() -> UIView
}
struct ModifiedView<T: View>: View {
let content: T
let modifier: (UIView) -> Void
init(content: T, modifier: @escaping (UIView) -> Void) {
self.content = content
self.modifier = modifier
}
func body() -> UIView {
let view = content.body()
modifier(view)
return view
}
}
extension View {
func background(_ background: UIColor) -> ModifiedView<Self> {
return ModifiedView(content: self) { body in
body.backgroundColor = background
}
}
func bind<Binder: AnyObject, Target: UIView>(_ keyPath: ReferenceWritableKeyPath<Binder, Target?>, binder: Binder) -> ModifiedView<Self> {
return ModifiedView(content: self) { body in
binder[keyPath: keyPath] = body as? Target
}
}
}
@_functionBuilder
final class ViewBuilder {
static func buildBlock<Content>(_ content: Content) -> some View where Content : View {
return content
}
static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) -> some View where C0 : View, C1 : View {
return TupleView(t1: c0, t2: c1)
}
}
protocol TupleViewAnchor {
func flattenViews() -> [UIView]
}
final class TupleView<T1, T2>: View, TupleViewAnchor where T1: View, T2: View {
let t1: T1
let t2: T2
init(t1: T1, t2: T2) {
self.t1 = t1
self.t2 = t2
}
func flattenViews() -> [UIView] {
var selfContents: [UIView] = []
if let t1 = t1 as? TupleViewAnchor {
selfContents.append(contentsOf: t1.flattenViews())
} else {
selfContents.append(t1.body())
}
if let t2 = t2 as? TupleViewAnchor {
selfContents.append(contentsOf: t2.flattenViews())
} else {
selfContents.append(t2.body())
}
return selfContents
}
func body() -> UIView {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fillEqually
for subview in flattenViews() {
stackView.addArrangedSubview(subview)
}
return stackView
}
}
final class SimpleView: View {
let makeContent: () -> View
init(@ViewBuilder makeContent: @escaping () -> View) {
self.makeContent = makeContent
}
func body() -> UIView {
let view = UIView()
let content = makeContent().body()
view.addSubview(content)
content.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(
[
view.topAnchor.constraint(equalTo: content.topAnchor),
view.bottomAnchor.constraint(equalTo: content.bottomAnchor),
view.leadingAnchor.constraint(equalTo: content.leadingAnchor),
view.trailingAnchor.constraint(equalTo: content.trailingAnchor)
]
)
return view
}
}
final class Label: View {
let text: String
init(text: String) {
self.text = text
}
func body() -> UIView {
let label = UILabel()
label.text = text
label.sizeToFit()
label.textAlignment = .center
return label
}
}
final class UserView: View {
var userNameLabel: UILabel!
var userName: String? {
didSet { userNameLabel.text = userName }
}
func body() -> UIView {
SimpleView {
Label(text: "Hello")
Label(text: "John")
.bind(\.userNameLabel, binder: self)
}
.background(.white)
.body()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment