Last active
November 29, 2016 10:56
-
-
Save ncreated/45d747633e0515e1d31772afccfbe452 to your computer and use it in GitHub Desktop.
POC of syntax improvement to manual frame layout in Swift.
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
//: Playground - noun: a place where people can play | |
import UIKit | |
import PlaygroundSupport | |
struct Frame { | |
let x: CGFloat | |
let y: CGFloat | |
let width: CGFloat | |
let height: CGFloat | |
// MARK: Margins | |
func with(margins: (top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat)) -> Frame { | |
return Frame(x: x + margins.left, | |
y: y + margins.top, | |
width: width - margins.left - margins.right, | |
height: height - margins.top - margins.bottom) | |
} | |
// MARK: Distribution | |
struct EqualDistribution { | |
struct Horizontal { | |
let frame: Frame | |
func take(index: Int, of count: Int) -> Frame { | |
let distributedSize = frame.width / CGFloat(count) | |
return Frame(x: frame.x + CGFloat(index) * distributedSize, | |
y: frame.y, | |
width: distributedSize, | |
height: frame.height) | |
} | |
} | |
struct Vertical { | |
let frame: Frame | |
func take(index: Int, of count: Int) -> Frame { | |
let distributedSize = frame.height / CGFloat(count) | |
return Frame(x: frame.x, | |
y: frame.y + CGFloat(index) * distributedSize, | |
width: frame.width, | |
height: distributedSize) | |
} | |
} | |
} | |
var distributeHorizontally: EqualDistribution.Horizontal { | |
return EqualDistribution.Horizontal(frame: self) | |
} | |
var distributeVertically: EqualDistribution.Vertical { | |
return EqualDistribution.Vertical(frame: self) | |
} | |
// MARK: Slicing | |
struct Slice { | |
let frame: Frame | |
let size: CGSize | |
func fromTopRightCorner(topMargin: CGFloat, rightMargin: CGFloat) -> Frame { | |
return Frame(x: frame.x + frame.width - rightMargin - size.width, | |
y: topMargin, | |
width: size.width, | |
height: size.height) | |
} | |
/// TODO: fromTopLeftCorner ... | |
/// TODO: fromBottomRightCorner ... | |
func fromBottomLeftCorner(bottomMargin: CGFloat, leftMargin: CGFloat) -> Frame { | |
return Frame(x: frame.x + leftMargin, | |
y: frame.y + frame.height - bottomMargin - size.height, | |
width: size.width, | |
height: size.height) | |
} | |
} | |
func slicedFrame(ofSize size: CGSize) -> Slice { | |
return Slice(frame: self, size: size) | |
} | |
// MARK: Convenience | |
var rect: CGRect { | |
return CGRect(x: x, y: y, width: width, height: height) | |
} | |
} | |
// MARK: Calculating layout | |
let containerFrame = Frame(x: 0, y: 0, width: 300, height: 300) | |
let redFrame = containerFrame | |
.distributeHorizontally.take(index: 0, of: 2) | |
.distributeVertically.take(index: 0, of: 2) | |
let blueFrame = containerFrame | |
.distributeHorizontally.take(index: 1, of: 2) | |
.distributeVertically.take(index: 1, of: 2) | |
.with(margins: (top: 10, left: 10, bottom: 10, right: 10)) | |
let yellowFrame = blueFrame.with(margins: (top: 10, left: 10, bottom: 10, right: 10)) | |
let purpleFrame = containerFrame.slicedFrame(ofSize: CGSize(width: 90, height: 90)) | |
.fromBottomLeftCorner(bottomMargin: 10, leftMargin: 10) | |
// MARK: Drawing | |
let view = UIView(frame: containerFrame.rect) | |
view.backgroundColor = .white | |
let redView = UIView(frame: redFrame.rect) | |
redView.backgroundColor = .red | |
view.addSubview(redView) | |
let blueView = UIView(frame: blueFrame.rect) | |
blueView.backgroundColor = .blue | |
view.addSubview(blueView) | |
let yellowView = UIView(frame: yellowFrame.rect) | |
yellowView.backgroundColor = .yellow | |
view.addSubview(yellowView) | |
let purpleView = UIView(frame: purpleFrame.rect) | |
purpleView.backgroundColor = .purple | |
view.addSubview(purpleView) | |
PlaygroundPage.current.liveView = view |
Improved API idea:
Initialization
let containerFrame = Frame(width: 300, height: 300)
Grid distribution (rows & cols)
// "Divide frame ... into 2x2 grid and take (0, 0) frame"
let redFrame = containerFrame
.distributeTo(columns: 2).take(0)
.distributeTo(rows: 2).take(0)
// "Divide frame ... into 2x2 grid and take (1, 1) frame"
let blueFrame = containerFrame
.distributeTo(columns: 2).take(1)
.distributeTo(rows: 2).take(1)
Margins
// "Create frame by adding 10-points margin for each edge of blueFrame"
let yellowFrame = blueFrame.inset(top: 10, left: 10, bottom: 10, right: 10)
Relative Positioning
// "Put new frame of size ... below yellowFrame, and align it horizontally, then add y-offset
let anotherFrame = Frame(width: 90, height: 90)
.putBelow(frame: yellowFrame)
.alignHorizontally(.center)
.offset(y: 10)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Preview: