Skip to content

Instantly share code, notes, and snippets.

@justAnotherDev
Last active May 11, 2016 10:04
Show Gist options
  • Save justAnotherDev/5b42692f72975c1d0606363d0b11ffd9 to your computer and use it in GitHub Desktop.
Save justAnotherDev/5b42692f72975c1d0606363d0b11ffd9 to your computer and use it in GitHub Desktop.
A different approach to laying out views. Designed for Swift, works in ObjC
//
// ViewPlace.swift
//
import UIKit
extension UIView {
/**
Add `self` and `otherView` to a new view and align them along the provided `edge`.
- Returns: A new view that is now the superview of `self` and `otherView`
*/
@objc func place(edge: ViewEdgeRelationship, _ otherView: UIView, offset: CGFloat = 0) -> UIView {
// make sure self and otherView don't already have a superview
if self.superview != nil || otherView.superview != nil {
assertionFailure("views already have superviews. this feature is not handled yet")
}
// add self and otherView to a container view
let containerView = UIView()
self.translatesAutoresizingMaskIntoConstraints = false
otherView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(self)
containerView.addSubview(otherView)
// pin self to the passed in edge and its perpendicular edges
let perpendicular = edge.perpendicular
containerView.match([perpendicular.0, perpendicular.1, edge], to: self)
// pin otherView to the passed in edge's inverse and perpendicular edges
containerView.match([perpendicular.0, perpendicular.1, edge.inverse], to: otherView)
// pin self and otherView to each other with the provided offset
containerView.align(self, edge: edge.inverse, to: otherView, edge: edge, offset: offset*edge.offsetMultiplier)
return containerView
}
}
/**
Helpers for adding layout constraints
*/
private extension UIView {
private func match(edges: [ViewEdgeRelationship], to otherView: UIView) {
for edge in edges {
align(self, edge: edge, to: otherView, edge: edge)
}
}
private func align(view: UIView, edge: ViewEdgeRelationship, to otherView: UIView, edge otherViewEdge: ViewEdgeRelationship, offset: CGFloat = 0) {
addConstraint(NSLayoutConstraint(item: view, attribute: edge.attribute, relatedBy: .Equal, toItem: otherView, attribute: otherViewEdge.attribute, multiplier: 1, constant: offset))
}
}
@objc enum ViewEdgeRelationship: Int {
case LeftOf
case RightOf
case Above
case Below
}
private extension ViewEdgeRelationship {
/**
Associated NSLayoutAttribute value.
*/
var attribute: NSLayoutAttribute {
switch self {
case .Above:
return .Top
case .Below:
return .Bottom
case .LeftOf:
return .Left
case .RightOf:
return .Right
}
}
/**
The opposite edge of self.
*/
var inverse: ViewEdgeRelationship {
switch self {
case .Above:
return .Below
case .Below:
return .Above
case .LeftOf:
return .RightOf
case .RightOf:
return .LeftOf
}
}
/**
Multiplier to use when applying offsets.
- Note: This makes it so provided offset values don't need to be negative
*/
var offsetMultiplier: CGFloat {
switch self {
case .Above:
return -1
case .Below:
return 1
case .LeftOf:
return -1
case .RightOf:
return 1
}
}
/**
The two edges that are perpendicular to self. For example: Above and Below are perpendicular to Right (and Left)
*/
var perpendicular: (ViewEdgeRelationship, ViewEdgeRelationship) {
switch self {
case .Above:
return (.LeftOf, .RightOf)
case .Below:
return (.LeftOf, .RightOf)
case .LeftOf:
return (.Above, .Below)
case .RightOf:
return (.Above, .Below)
}
}
}
UILabel *label = [UILabel new];
label.text = @"hello world";
label.backgroundColor = [UIColor purpleColor];
UIButton *button = [UIButton new];
[button setTitle:@"asdf" forState: UIControlStateNormal];
button.backgroundColor = [UIColor redColor];
UIView *composite1 = [label place:ViewEdgeRelationshipLeftOf :button offset:0];
UILabel *label2 = [UILabel new];
label2.text = @"qwerty";
label2.backgroundColor = [UIColor greenColor];
UIView *composite2 = [composite1 place:ViewEdgeRelationshipRightOf :label2 offset: 4];
UIButton *button2 = [UIButton new];
[button2 setTitle:@"layers" forState: UIControlStateNormal];
button2.backgroundColor = [UIColor orangeColor];
UIView *randomView = [composite2 place:ViewEdgeRelationshipAbove :button2 offset:0];
let label = UILabel()
label.text = "hello world"
label.backgroundColor = UIColor.purpleColor()
let button = UIButton()
button.setTitle("asdf", forState: .Normal)
button.backgroundColor = UIColor.redColor()
let composite1 = label.place(.Below, button, offset: 0)
let label2 = UILabel()
label2.text = "qwerty"
label2.backgroundColor = UIColor.greenColor()
let composite2 = composite1.place(.RightOf, label2, offset: 4)
let button2 = UIButton()
button2.setTitle("layers", forState: .Normal)
button2.backgroundColor = UIColor.orangeColor()
return composite2.place(.Above, button2, offset: 0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment