Created
September 29, 2019 11:08
-
-
Save kateinoigakukun/9de0b01aa22173cc19aed3e306c5d0cd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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