Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@duemunk
Last active May 6, 2016 08:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save duemunk/e0005bc865fbcba594e6 to your computer and use it in GitHub Desktop.
Save duemunk/e0005bc865fbcba594e6 to your computer and use it in GitHub Desktop.
Auto Layout in Swift
//
// AutoLayout.swift
// Tobias Due Munk
//
// Created by Tobias Due Munk on 26/08/14.
// Copyright (c) 2014 Tobias Due Munk. All rights reserved.
//
// Example usage:
//
// view1.topInset(20, relation: .Equal, priority: 200)
// view1.topInset(20, relation: .GreaterThanOrEqual)
// view1.horizontalInset(0)
// view2.horizontalInset(0)
// view2.topOffset(view1, value: 0)
// view2.heightEqual(view1)
//
// or
//
// view.inset(top: 0, left: 10)
// view.width(100, .GreaterThanOrEqual)
import UIKit
extension UIView {
/* Inset */
func inset(insetValue: Float, priority: UILayoutPriority = 1000) {
inset(top: insetValue, left: insetValue, bottom: insetValue, right: insetValue, priority: priority)
}
func inset(top: Float? = nil, left: Float? = nil, bottom: Float? = nil, right: Float? = nil, margin: Bool = false, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
if let left = left {
leftInset(left, margin: margin, relation: relation, priority: priority)
}
if let top = top {
topInset(top, margin: margin, relation: relation, priority: priority)
}
if let right = right {
rightInset(right, margin: margin, relation: relation, priority: priority)
}
if let bottom = bottom {
bottomInset(bottom, margin: margin, relation: relation, priority: priority)
}
}
func horizontalInset(_ insetValue: Float = 0, margin: Bool = false, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
inset(left: 0, right: 0, margin: margin, relation: relation, priority: priority)
}
func verticalInset(_ insetValue: Float = 0, margin: Bool = false, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
inset(top: 0, bottom: 0, margin: margin, relation: relation, priority: priority)
}
func leftInset(_ value: Float = 0, margin: Bool = false, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
inset(margin ? .LeftMargin : .Left, value: value, relation: relation, priority: priority)
}
func topInset(_ value: Float = 0, margin: Bool = false, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
inset(margin ? .TopMargin : .Top, value: value, relation: relation, priority: priority)
}
func rightInset(_ value: Float = 0, margin: Bool = false, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
let (flippedValue, flippedRelation) = flip(value: value, relation: relation)
inset(margin ? .RightMargin : .Right, value: flippedValue, relation: flippedRelation, priority: priority)
}
func bottomInset(_ value: Float = 0, margin: Bool = false, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) -> NSLayoutConstraint {
let (flippedValue, flippedRelation) = flip(value: value, relation: relation)
return inset(margin ? .BottomMargin : .Bottom, value: flippedValue, relation: flippedRelation, priority: priority)
}
func bottomBaselineInset(_ value: Float = 0, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
let (flippedValue, flippedRelation) = flip(value: value, relation: relation)
inset(.Baseline, value: flippedValue, relation: flippedRelation, secondAttribute: .Bottom, priority: priority)
}
private func inset(attribute: NSLayoutAttribute, value: Float = 0, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) -> NSLayoutConstraint {
let constraint = NSLayoutConstraint(item: self, attribute: attribute, relatedBy: relation, toItem: self.superview, attribute: attribute, multiplier: 1, constant: CGFloat(value))
constraint.priority = priority
return installConstraint(constraint)
}
private func inset(attribute: NSLayoutAttribute, value: Float = 0, relation: NSLayoutRelation = .Equal, secondAttribute: NSLayoutAttribute, priority: UILayoutPriority = 1000) -> NSLayoutConstraint {
let constraint = NSLayoutConstraint(item: self, attribute: attribute, relatedBy: relation, toItem: self.superview, attribute: secondAttribute, multiplier: 1, constant: CGFloat(value))
constraint.priority = priority
return installConstraint(constraint)
}
/* Height and width */
func width(width: Float, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
dimension(.Width, value: width, relation: relation, priority: priority)
}
func height(height: Float, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
dimension(.Height, value: height, relation: relation, priority: priority)
}
private func dimension(attribute: NSLayoutAttribute, value: Float, relation: NSLayoutRelation, priority: UILayoutPriority = 1000) -> NSLayoutConstraint {
let constraint = NSLayoutConstraint(item: self, attribute: attribute, relatedBy: relation, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: CGFloat(value))
constraint.priority = priority
return installConstraint(constraint)
}
/* Center */
func center(relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
centerX(priority: priority)
centerY(priority: priority)
}
func centerX(relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
center(.CenterX, relation: relation, priority: priority)
}
func centerY(relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
center(.CenterY, relation: relation)
}
private func center(attribute: NSLayoutAttribute, value: Float = 0, relation: NSLayoutRelation, priority: UILayoutPriority = 1000) -> NSLayoutConstraint {
let constraint = NSLayoutConstraint(item: self, attribute: attribute, relatedBy: relation, toItem: self.superview, attribute: attribute, multiplier: 1, constant: CGFloat(value))
constraint.priority = priority
return installConstraint(constraint)
}
/* Relation */
func topOffset(toView: UIView, value: Float = 0, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
relate(.Top, toView: toView, toAttribute: .Bottom, value: value, relation: relation, priority: priority)
}
func rightOffset(toView: UIView, value: Float = 0, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
let (flippedValue, flippedRelation) = flip(value: value, relation: relation)
relate(.Right, toView: toView, toAttribute: .Left, value: flippedValue, relation: flippedRelation, priority: priority)
}
func heightEqual(toView: UIView, relation: NSLayoutRelation = .Equal, priority: UILayoutPriority = 1000) {
relate(.Height, toView: toView, toAttribute: .Height, relation: relation, priority: priority)
}
private func relate(attribute: NSLayoutAttribute, toView: UIView, toAttribute: NSLayoutAttribute, value: Float = 1, relation: NSLayoutRelation, priority: UILayoutPriority = 1000) {
let constraint = NSLayoutConstraint(item: self, attribute: attribute, relatedBy: relation, toItem: toView, attribute: toAttribute, multiplier: 1, constant: CGFloat(value))
constraint.priority = priority
let installedConstraint = installConstraint(constraint)
}
/* Clean up */
func removeAllConstraints() {
for installedConstraint in relatedConstraints() {
installedConstraint.active = false
}
}
/* Private helpers */
private func relatedConstraints() -> [NSLayoutConstraint] {
var directConstraints = self.constraints() as [NSLayoutConstraint]
if let indirectConstraints = relatedConstraintsInSuperViews(self) {
directConstraints += indirectConstraints
}
return directConstraints
}
private func relatedConstraintsInSuperViews(view: UIView) -> [NSLayoutConstraint]? {
return relatedConstraintsInSuperViews(view, relatedToView: view)
}
private func relatedConstraintsInSuperViews(view: UIView, relatedToView: UIView) -> [NSLayoutConstraint]? {
if let superview = view.superview {
var relatedConstraints = [NSLayoutConstraint]()
for constraint in superview.relatedConstraints() {
if let firstItem = constraint.firstItem as? UIView {
if firstItem == relatedToView {
relatedConstraints.append(constraint)
}
} else {
if let secondItem = constraint.secondItem as? UIView {
if secondItem == relatedToView {
relatedConstraints.append(constraint)
}
}
}
}
// Recursively add constraint in super view
if let superRelatedConstraints = relatedConstraintsInSuperViews(superview, relatedToView: relatedToView) {
for constraint in superRelatedConstraints {
relatedConstraints.append(constraint)
}
}
if relatedConstraints.count > 0 {
return relatedConstraints
}
return nil
}
return nil
}
private func installConstraint(constraint: NSLayoutConstraint) -> NSLayoutConstraint {
if let installedConstraint = constraintSimilarToConstraint(constraint) {
installedConstraint.constant = constraint.constant
return installedConstraint
} else {
setTranslatesAutoresizingMaskIntoConstraints(false)
constraint.active = true
return constraint
}
}
private func constraintSimilarToConstraint(constraint: NSLayoutConstraint) -> NSLayoutConstraint? {
for installedConstraint in relatedConstraints() {
let installedSecondItem = installedConstraint.secondItem as? UIView
let secondItem = constraint.secondItem as? UIView
if installedConstraint.firstAttribute == constraint.firstAttribute &&
installedConstraint.relation == constraint.relation &&
installedSecondItem == secondItem &&
installedConstraint.secondAttribute == constraint.secondAttribute {
return installedConstraint
}
}
return nil
}
private func flip(#value: Float, relation: NSLayoutRelation) -> (flippedValue: Float, flippedRelation: NSLayoutRelation) {
let flippedValue = -value
let flippedRelation: NSLayoutRelation = {
switch relation {
case .GreaterThanOrEqual: return .LessThanOrEqual
case .LessThanOrEqual: return .GreaterThanOrEqual
default: return relation
}
}()
return (flippedValue, flippedRelation)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment