Last active
May 17, 2016 20:59
-
-
Save FranDepascuali/f82e35ed663c38ca6bba6614175c8ed3 to your computer and use it in GitHub Desktop.
Collapse & uncollapse views
This file contains hidden or 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
extension UIView { | |
private var previousHeightConstraint: NSLayoutConstraint? { | |
return constraints.filterFirst { $0.firstAttribute == .Height } | |
} | |
} | |
/** | |
Collapse a view by adding/modifying constraint height. | |
**Warning:** It is intended to be used to collapse a view *without* subviews. | |
- Parameter view: The view to collapse | |
- Parameter animated: Indicates if the collapse should be animated. | |
- Parameter animationDuration: The animationDuration of the collapse. | |
*/ | |
func collapse(view: UIView, animated: Bool = true, animationDuration: NSTimeInterval = 1) { | |
if let previousHeightConstraint = view.previousHeightConstraint { | |
// We save the previous height value | |
setAssociatedObject(view, value: previousHeightConstraint.constant, key: &hideableUIViewConstraintKey, policy: .OBJC_ASSOCIATION_RETAIN_NONATOMIC) | |
previousHeightConstraint.constant = 0 | |
} else { | |
// We create a new height constraint with constant 0 | |
let zeroheightConstraint = NSLayoutConstraint(item: view, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 0) | |
view.addConstraint(zeroheightConstraint) | |
} | |
view.setNeedsLayout() | |
if animated { | |
UIView.animateWithDuration(animationDuration) { | |
view.layoutIfNeeded() | |
} | |
} else { | |
view.layoutIfNeeded() | |
} | |
} | |
/** | |
Uncollapse a view by removing/modifying constraint height. | |
**Warning:** It is intended to be used to uncollapse a view *without* subviews. | |
- Parameter view: The view to collapse | |
- Parameter animated: Indicates if the collapse should be animated. | |
- Parameter animationDuration: The animationDuration of the collapse. | |
*/ | |
func uncollapse(view: UIView, animated: Bool, animationDuration: NSTimeInterval = 1) { | |
view.constraints.forEach { constraint in | |
// We should find a height constraint | |
if constraint.firstAttribute == .Height { | |
// If we have a previous height, it means that before collapsing it had another height constraint. | |
if let previousHeight: CGFloat = getAssociatedObject(view, key: &hideableUIViewConstraintKey) { | |
constraint.constant = previousHeight | |
} else { | |
// We remove the height constraint added because it didn't have a height before collapsing. | |
view.removeConstraint(constraint) | |
} | |
} | |
} | |
view.setNeedsLayout() | |
if animated { | |
UIView.animateWithDuration(animationDuration) { | |
view.layoutIfNeeded() | |
} | |
} else { | |
view.layoutIfNeeded() | |
} | |
} | |
private final class AssociatedObjectBox<T> { | |
let value: T | |
init(_ value: T) { | |
self.value = value | |
} | |
} | |
private func lift<T>(value: T) -> AssociatedObjectBox<T> { | |
return AssociatedObjectBox(value) | |
} | |
private func setAssociatedObject<T>(object: AnyObject, value: T, key: UnsafePointer<Void>, policy: objc_AssociationPolicy) { | |
if let v: AnyObject = value as? AnyObject { | |
objc_setAssociatedObject(object, key, v, policy) | |
} else { | |
objc_setAssociatedObject(object, key, lift(value), policy) | |
} | |
} | |
private func getAssociatedObject<T>(object: AnyObject, key: UnsafePointer<Void>) -> T? { | |
if let v = objc_getAssociatedObject(object, key) as? T { | |
return v | |
} else if let v = objc_getAssociatedObject(object, key) as? AssociatedObjectBox<T> { | |
return v.value | |
} else { return nil } | |
} | |
private var hideableUIViewConstraintKey: UInt8 = 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment