Skip to content

Instantly share code, notes, and snippets.

@pcperini
Created December 11, 2015 00:20
Show Gist options
  • Save pcperini/efec6c717ec0a6dc2579 to your computer and use it in GitHub Desktop.
Save pcperini/efec6c717ec0a6dc2579 to your computer and use it in GitHub Desktop.
don't try this at home, kids
//
// LayoutConstraint.swift
// LayoutConstraints
//
// Created by PATRICK PERINI on 12/10/15.
// Copyright © 2015 atomic. All rights reserved.
//
import UIKit
public struct LayoutConstraint {
// MARK: Properties
let relation: NSLayoutRelation?
let multiplier: CGFloat?
let constant: CGFloat?
let firstView: UIView?
let firstAttribute: NSLayoutAttribute?
let secondView: UIView?
let secondAttribute: NSLayoutAttribute?
var layoutConstraint: NSLayoutConstraint? {
guard let firstView = self.firstView else { return nil }
return NSLayoutConstraint(item: firstView,
attribute: self.firstAttribute ?? .NotAnAttribute,
relatedBy: self.relation ?? .Equal,
toItem: self.secondView,
attribute: self.secondAttribute ?? .NotAnAttribute,
multiplier: self.multiplier ?? 1.0,
constant: self.constant ?? 0.0)
}
}
extension NSLayoutConstraint {
// MARK: Properties
var constraint: LayoutConstraint {
return LayoutConstraint(relation: self.relation,
multiplier: self.multiplier,
constant: self.constant,
firstView: self.firstItem as? UIView,
firstAttribute: self.firstAttribute,
secondView: self.secondItem as? UIView,
secondAttribute: self.secondAttribute)
}
}
// MARK: Conversion
extension LayoutConstraint: FloatLiteralConvertible {
// MARK: Initializers
public init(floatLiteral value: FloatLiteralType) {
self.relation = nil
self.multiplier = nil
self.constant = CGFloat(value)
self.firstView = nil
self.firstAttribute = nil
self.secondView = nil
self.secondAttribute = .NotAnAttribute
}
}
// MARK: Operators
func *(lhs: CGFloat, rhs: LayoutConstraint) -> LayoutConstraint {
return LayoutConstraint(relation: rhs.relation,
multiplier: lhs,
constant: rhs.constant,
firstView: rhs.firstView,
firstAttribute: rhs.firstAttribute,
secondView: rhs.secondView,
secondAttribute: rhs.secondAttribute)
}
func +(lhs: LayoutConstraint, rhs: CGFloat) -> LayoutConstraint {
return LayoutConstraint(relation: lhs.relation,
multiplier: lhs.multiplier,
constant: rhs,
firstView: lhs.firstView,
firstAttribute: lhs.firstAttribute,
secondView: lhs.secondView,
secondAttribute: lhs.secondAttribute)
}
func -(lhs: LayoutConstraint, rhs: CGFloat) -> LayoutConstraint {
return LayoutConstraint(relation: lhs.relation,
multiplier: lhs.multiplier,
constant: -rhs,
firstView: lhs.firstView,
firstAttribute: lhs.firstAttribute,
secondView: lhs.secondView,
secondAttribute: lhs.secondAttribute)
}
prefix operator >= {}
prefix func >=(rhs: LayoutConstraint) -> LayoutConstraint {
return LayoutConstraint(relation: .GreaterThanOrEqual,
multiplier: rhs.multiplier,
constant: rhs.constant,
firstView: rhs.firstView,
firstAttribute: rhs.firstAttribute,
secondView: rhs.secondView,
secondAttribute: rhs.secondAttribute)
}
prefix operator <= {}
prefix func <=(rhs: LayoutConstraint) -> LayoutConstraint {
return LayoutConstraint(relation: .LessThanOrEqual,
multiplier: rhs.multiplier,
constant: rhs.constant,
firstView: rhs.firstView,
firstAttribute: rhs.firstAttribute,
secondView: rhs.secondView,
secondAttribute: rhs.secondAttribute)
}
//
// UIView+LayoutConstraint.swift
// LayoutConstraints
//
// Created by PATRICK PERINI on 12/4/15.
// Copyright © 2015 atomic. All rights reserved.
//
import UIKit
extension UIView {
// MARK: Properties
// ... Center / Size
var centerX: LayoutConstraint {
get { return self.constraint(attribute: .CenterX) }
set { self.addConstraint(constraint: newValue, attribute: .CenterX) }
}
var centerY: LayoutConstraint {
get { return self.constraint(attribute: .CenterY) }
set { self.addConstraint(constraint: newValue, attribute: .CenterY) }
}
var width: LayoutConstraint {
get { return self.constraint(attribute: .Width) }
set { self.addConstraint(constraint: newValue, attribute: .Width) }
}
var height: LayoutConstraint {
get { return self.constraint(attribute: .Height) }
set { self.addConstraint(constraint: newValue, attribute: .Height) }
}
// ... Edges
var left: LayoutConstraint {
get { return self.constraint(attribute: .Left) }
set { self.addConstraint(constraint: newValue, attribute: .Left) }
}
var right: LayoutConstraint {
get { return self.constraint(attribute: .Right) }
set { self.addConstraint(constraint: newValue, attribute: .Right) }
}
var top: LayoutConstraint {
get { return self.constraint(attribute: .Top) }
set { self.addConstraint(constraint: newValue, attribute: .Top) }
}
var bottom: LayoutConstraint {
get { return self.constraint(attribute: .Bottom) }
set { self.addConstraint(constraint: newValue, attribute: .Bottom) }
}
}
extension UIView {
// MARK: Accessors
private func constraint(attribute attribute: NSLayoutAttribute) -> LayoutConstraint {
return LayoutConstraint(relation: nil,
multiplier: nil,
constant: nil,
firstView: self,
firstAttribute: attribute,
secondView: nil,
secondAttribute: nil)
}
private func layoutConstraintForAttribute(attribute: NSLayoutAttribute) -> NSLayoutConstraint? {
return (self.constraints + (self.superview?.constraints ?? [])).filter { (possibleConstraint: NSLayoutConstraint) in
return ((possibleConstraint.firstItem === self && possibleConstraint.firstAttribute == attribute) ||
(possibleConstraint.secondItem === self && possibleConstraint.secondAttribute == attribute))
}.first
}
// MARK: Mutators
private func addConstraint(constraint constraint: LayoutConstraint, attribute: NSLayoutAttribute) {
let targetView = constraint.firstView ?? self
targetView.removeConstraint(constraint: constraint, toView: self, attribute: attribute)
guard let layoutConstraint = LayoutConstraint(relation: constraint.relation,
multiplier: constraint.multiplier,
constant: constraint.constant,
firstView: self,
firstAttribute: attribute,
secondView: constraint.firstView,
secondAttribute: constraint.firstAttribute).layoutConstraint else { return }
targetView.addConstraint(layoutConstraint)
}
private func removeConstraint(constraint constraint: LayoutConstraint, toView view: UIView, attribute: NSLayoutAttribute) {
guard let layoutConstraint = self.constraints.filter({ (possibleConstraint: NSLayoutConstraint) in
return ((possibleConstraint.firstItem === view || possibleConstraint.secondItem === view) &&
(possibleConstraint.firstAttribute == attribute) &&
(possibleConstraint.secondAttribute == constraint.firstAttribute ?? NSLayoutAttribute(rawValue: 0)))
}).first else { return }
self.removeConstraint(layoutConstraint)
}
}
// MARK: Operators
func +=(lhs: LayoutConstraint, rhs: CGFloat) {
guard let layoutConstraint = lhs.firstView?.layoutConstraintForAttribute(lhs.firstAttribute ?? .NotAnAttribute) else { return }
layoutConstraint.constant += rhs
}
func -=(lhs: LayoutConstraint, rhs: CGFloat) {
guard let layoutConstraint = lhs.firstView?.layoutConstraintForAttribute(lhs.firstAttribute ?? .NotAnAttribute) else { return }
layoutConstraint.constant += rhs
}
//
// ViewController.swift
// LayoutConstraints
//
// Created by PATRICK PERINI on 12/4/15.
// Copyright © 2015 atomic. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let childView = UIView()
self.view.addSubview(childView)
childView.backgroundColor = UIColor.redColor()
self.view.translatesAutoresizingMaskIntoConstraints = false
childView.translatesAutoresizingMaskIntoConstraints = false
childView.height = 0.5 * self.view.height - 5.0
childView.width = 100.5
childView.left = self.view.left + 50.0
self.view.layoutIfNeeded()
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2) * Int64(NSEC_PER_SEC)), dispatch_get_main_queue()) {
UIView.animateWithDuration(1.0, delay: 0.0, options: UIViewAnimationOptions(), animations: {
childView.left += 100.0
childView.height = self.view.height - 50.0
self.view.layoutIfNeeded()
}, completion: nil)
}
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment