Skip to content

Instantly share code, notes, and snippets.

@ncreated
Last active November 29, 2016 10:56
Show Gist options
  • Save ncreated/45d747633e0515e1d31772afccfbe452 to your computer and use it in GitHub Desktop.
Save ncreated/45d747633e0515e1d31772afccfbe452 to your computer and use it in GitHub Desktop.
POC of syntax improvement to manual frame layout in Swift.
//: 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
@ncreated
Copy link
Author

ncreated commented Nov 29, 2016

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