Last active
April 9, 2023 19:54
-
-
Save yoching/8d44eb0016df020dbd8703e8b196c943 to your computer and use it in GitHub Desktop.
Functional view style sample
This file contains hidden or 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 | |
import PlaygroundSupport | |
// 1. define style as function | |
typealias ViewStyle = (UIView) -> UIView | |
let cornerRadius10: ViewStyle = { view -> UIView in | |
view.layer.cornerRadius = 10.0 | |
return view | |
} | |
let backgroundColorOrange: ViewStyle = { view -> UIView in | |
view.backgroundColor = .orange | |
return view | |
} | |
// cornerRadius10(backgroundColorOrange(someView)) | |
// 2. make syntax readable (pipe operator) | |
precedencegroup ForwardApplication { | |
associativity: left | |
} | |
infix operator |>: ForwardApplication | |
func |> (view: UIView, style: ViewStyle) -> UIView { | |
return style(view) | |
} | |
/* | |
someView | |
|> backgroundColorOrange | |
|> cornerRadius10 | |
*/ | |
// 3. make parameter configurable (currying) | |
let cornerRadiusNotCurried: (CGFloat, UIView) -> UIView = { radius, view -> UIView in | |
view.layer.cornerRadius = radius | |
return view | |
} | |
// view |> cornerRadius doesn't work | |
// currying | |
let cornerRadius: (CGFloat) -> ViewStyle = { radius -> ViewStyle in | |
return { view -> UIView in | |
view.layer.cornerRadius = radius | |
return view | |
} | |
} | |
cornerRadius(5.0) // (UIView) -> UIView, ViewStyle | |
// view |> cornerRadius(5.0) works | |
let backgroundColor: (UIColor) -> ViewStyle = { color -> ViewStyle in | |
return { view -> UIView in | |
view.backgroundColor = color | |
return view | |
} | |
} | |
// 4. make same style reusable (function composition) | |
func compose(style1: @escaping ViewStyle, style2: @escaping ViewStyle) -> ViewStyle { | |
return { view -> UIView in | |
return style2(style1(view)) // view |> style1 |> style2 | |
} | |
} | |
// let cardStyle = compose(cornerRadius(5.0), backgroundColor(.orange)) | |
// let someComplexStyle = compose(cornerRadius(5.0), compose(backgroundColor(.orange), ...)) | |
infix operator >>>: ForwardApplication | |
func >>> (style1: @escaping ViewStyle, style2: @escaping ViewStyle) -> ViewStyle { | |
return { view -> UIView in | |
return view |> style1 |> style2 | |
} | |
} | |
// 5. generalize custom operators | |
func |> <A, B> (lhs: A, f: (A) -> B) -> B { | |
return f(lhs) | |
} | |
func >>> <A, B, C> (f1: @escaping (A) -> B, f2: @escaping (B) -> C) -> (A) -> C { | |
return { a -> C in | |
return a |> f1 |> f2 | |
} | |
} | |
class ViewController: UIViewController { | |
override func loadView() { | |
self.view = UIView() | |
self.view.backgroundColor = .darkGray | |
let someView = UIView() | |
someView.frame = CGRect(x: 37.5, y: 200, width: 300, height: 300) | |
// 1 | |
cornerRadius10(backgroundColorOrange(someView)) | |
// 2 | |
let orangeView: UIView = someView |> backgroundColorOrange | |
orangeView |> cornerRadius10 | |
someView | |
|> backgroundColorOrange | |
|> cornerRadius10 | |
/* | |
someView | |
. backgroundColorOrange | |
. cornerRadius10 | |
*/ | |
// 3 | |
someView | |
|> cornerRadius(5.0) | |
|> backgroundColor(.orange) | |
view.addSubview(someView) | |
// 4 | |
let cardStyle = cornerRadius(5) >>> backgroundColor(.orange) | |
someView |> cardStyle | |
} | |
} | |
// Present the view controller in the Live View window | |
PlaygroundPage.current.liveView = ViewController() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment