Skip to content

Instantly share code, notes, and snippets.

@FranDepascuali
Last active May 17, 2016 20:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FranDepascuali/f82e35ed663c38ca6bba6614175c8ed3 to your computer and use it in GitHub Desktop.
Save FranDepascuali/f82e35ed663c38ca6bba6614175c8ed3 to your computer and use it in GitHub Desktop.
Collapse & uncollapse views
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