I have convenience extension functions that allow me to add constraints to UIViews:
enum Constraint : String {
case top = "topAnchor"
case bottom = "bottomAnchor"
case right = "rightAnchor"
case left = "leftAnchor"
case centerX = "centerXAnchor"
case centerY = "centerYAnchor"
}
extension UIView {
func constraintLeft(toLeft of: UIView, margin: CGFloat = 0) {
self.deleteConstraints(.left)
print(self.constraints) // prints []
let left = leftAnchor.constraint(equalTo: of.leftAnchor, constant: margin)
left.identifier = Constraint.left.rawValue
NSLayoutConstraint.activate([left])
setNeedsUpdateConstraints()
print(self.constraints) // prints []
}
/* Other functions left out */
func deleteConstraints(_ constraintsToRemove: Constraint...) {
self.removeConstraints(self.constraints.filter({ c in
guard c.identifier != nil else {
return false
}
return constraintsToRemove.contains { constraint in
constraint.rawValue.elementsEqual(c.identifier!)
}
}))
}
}
However, when I am using these extension functions,
the constraints do not fully work. When I add the constraints separately without calling the extension functions, it does work !
Here is my current usage of these functions:
func createButton(icon: String, label: String) -> UIView {
let button = TransparentCardView()
button.translatesAutoresizingMaskIntoConstraints = false
let uiImageView = UIImageView(image: UIImage(named: icon))
button.addSubview(uiImageView)
uiImageView.translatesAutoresizingMaskIntoConstraints = false
uiImageView.constraintCenterVertical(to: button) // works
//uiImageView.constraintLeft(toLeft: button,margin: StyleConstants.contentPadding) // this does not work
uiImageView.leftAnchor.constraint(equalTo: button.leftAnchor,constant: StyleConstants.contentPadding).isActive = true // this does
let textView = UILabel()
button.addSubview(textView)
textView.translatesAutoresizingMaskIntoConstraints = false
textView.constraintCenterVertical(to: button) // works
//textView.constraintLeft(toRight: uiImageView,margin: 0) // This does not work!
textView.leftAnchor.constraint(equalTo: uiImageView.rightAnchor,constant: StyleConstants.contentPadding).isActive = true // this does work!
button.heightAnchor.constraint(equalToConstant: StyleConstants.More.CardViewSize).isActive = true
return button
}
Edit: I have added additional print calls, after removing previous constraint and after activating the new constraint.
Constraints are printed as [] if I use my extension functions.
but not if i constraint them normally.
I now know why Constraints disappear:
The identifier needs to be unique in the whole View Hierarchy it seems.
After procedurally generating identifier names, constraints do not disappear anymore.
func createConstraintName(constraint:Constraint, from: UIView, to: UIView) -> String {
var symbol = ""
switch (constraint) {
case .bottom: symbol = "___"
case .centerX: symbol = "-X-"
case .centerY: symbol = "-Y-"
case .left: symbol = "|__"
case .right: symbol = "__|"
case .top: symbol = "‾‾‾"
}
return String(describing: from) + symbol + String(describing: to)
}
I want to get the reference of all applied constraint using storyboard without any reference:
I had tried many ways but could not able to find the exact solution:
My Approach is as follows:
if let constraint = (self.constraints.filter{$0.firstAttribute == .height}.first) {
}
using the above approach, I am able to find out the height only.
if let topConstraint = (self.constraints.filter{$0.firstAttribute == .top}.first) {
topConstraint.constant = 150//topMargin
}
if let leadingConstraint = (self.constraints.filter{$0.firstAttribute == .leading}.first) {
leadingConstraint.constant = 60 //leadingMargin
}
For topConstraint and leadingConstraint i am getting nil.
self.constraints
self.constraints is giving only one reference that is height only even I had applied leading, trailing and bottom constraint on the same view.
Note: I don't want to take reference from storyboard so please don't suggest that solution. I want reference dynamically.
I am looking for the approach something like below:
if let topConstraint = (self.constraints.filter{$0.firstAttribute == .top}.first) {
topConstraint.constant = 150//topMargin
}
if let leadingConstraint = (self.constraints.filter{$0.firstAttribute == .leading}.first) {
leadingConstraint.constant = 60 //leadingMargin
}
if let trailingConstraint = (self.constraints.filter{$0.firstAttribute == .trailing}.first) {
trailingConstraint.constant = 70//leadingMargin
}
if let bottomConstraint = (self.constraints.filter{$0.firstAttribute == .bottom}.first) {
bottomConstraint.constant = 150//49 + bottomMargin
}
But unfortunately above one is not working for me :(
For a single view you can easily get all the constraints related to it
for constraint in view.constraints {
print(constraint.constant)
}
And for all the subviews of a particular view, you can get like this
func getAllTheConstraintConstantsFor(view:UIView) {
for constraint in view.constraints {
print(constraint.constant)
}
for subview in view.subviews {
self.getAllTheConstraintConstantsFor(view: subview)
}
}
Here you can pass self.view and you will get all the constraints.
With reference to this answer
For a view like UIButton you can find top constraint by using this code.
extension UIButton {
func findTopConstraint() -> NSLayoutConstraint? {
for constraint in (self.superview?.constraints)! {
if isTopConstraint(constraint: constraint) {
return constraint
}
}
return nil
}
func isTopConstraint(constraint: NSLayoutConstraint) -> Bool {
return (firstItemMatchesTopConstraint(constraint: constraint) || secondItemMatchesTopConstraint(constraint: constraint))
}
func firstItemMatchesTopConstraint(constraint: NSLayoutConstraint) -> Bool {
return (constraint.firstItem as? UIButton == self && constraint.firstAttribute == .top )
}
func secondItemMatchesTopConstraint(constraint: NSLayoutConstraint) -> Bool {
return (constraint.secondItem as? UIButton == self && constraint.secondAttribute == .top)
}
}
To get top constaint on UIButton just use this code
button.findTopConstraint()!
Similarly you can find any constraint on any view.
Note : You need to manage nil case yourself.
lets say you have an array of constraints
let constraints = [NSLayoutConstraints]
And I want to access the top anchor somehow using subscripts. I tried
extension Array where Element: NSLayoutConstraint {
enum LayoutAnchor {
case top
//case left
//case bottom
//case right
}
subscript(anchor: LayoutAnchor) -> NSLayoutConstraint? {
switch anchor {
case .top: return self.index(of: topAnchor)
}
}
}
so I can call anchors[.top] to access the top anchor. How would I directly access, in this case, the top anchor from an array of anchors?
I'm not sure what your aim is, but you need to identify the NSLayoutConstraint somehow.
I set the identifier of the top constraint as your LayoutAnchor type, then constraints[.top] was easy to construct. But this is not safe as the array might contain multiple constraints with the same type, or not at all.
Please note that constraints[.bottom] is nil as the identifier is not set for the bottom.
Below is a excerpt from the playground to play with, hope it helps.
enum LayoutAnchor: String {
case top
case left
case bottom
case right
}
extension Array where Element: NSLayoutConstraint {
subscript(anchor: LayoutAnchor) -> NSLayoutConstraint? {
switch anchor {
case .top:
return self.filter { $0.identifier == LayoutAnchor.top.rawValue }.first
case .bottom:
return self.filter { $0.identifier == LayoutAnchor.bottom.rawValue }.first
case .left:
return self.filter { $0.identifier == LayoutAnchor.left.rawValue }.first
case .right:
return self.filter { $0.identifier == LayoutAnchor.right.rawValue }.first
}
}
}
let view1 = UIView()
let view2 = UIView()
let top = view1.topAnchor.constraint(equalTo: view2.topAnchor)
top.identifier = LayoutAnchor.top.rawValue
let constraints: [NSLayoutConstraint] = [
top,
view1.bottomAnchor.constraint(equalTo: view2.bottomAnchor)
]
constraints[.top]
constraints[.bottom]
I have multiple controls on my screen. A collection view on right-top, then a button at left-center and besides the button, i have another collection view. Please refer the attached image for it
I am able to move the focus from button to bottom collection view and vice versa. I have created a focus guide for the same as below:
focusGuide.preferredFocusedView = self.btn
self.view.addLayoutGuide(self.focusGuide)
self.focusGuide.topAnchor.constraintEqualToAnchor(collectionViewHeader.topAnchor).active = true
self.focusGuide.bottomAnchor.constraintEqualToAnchor(collectionViewBottom.topAnchor).active = true
self.focusGuide.leadingAnchor.constraintEqualToAnchor(collectionViewBottom.leadingAnchor).active = true
self.focusGuide.widthAnchor.constraintEqualToAnchor(collectionViewBottom.widthAnchor).active = true
and in didUpdateFocusInContext: , I have write :
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
super.didUpdateFocusInContext(context, withAnimationCoordinator: coordinator)
guard let nextFocusedView = context.nextFocusedView else { return }
if(nextFocusedView .isKindOfClass(bottomCell)) {
self.focusGuide.preferredFocusedView = self.btn
} else {
self.focusGuide.preferredFocusedView = self.collectionViewBottom
}
}
But, I am not able to move focus from button to top collection view. I may need multiple focus guide for this, but I do not know what should come there. Can anyone help me on this?
Thank you
I had similar problems in many parts of my UI so ended up defining a custom view to create larger areas of the screen that represent groups of focusable controls. Because I setup these larger areas to cover empty regions that don't contain controls, they intercept the focus movement and transfer to focus to the controls within the area independently of vertical or horizontal alignment with the original control where focus started from.
This is the custom view. You use it by placing one or more controls inside it in IB.
Note that it has additional features such as forcing focus to a specific control without having to override preferredFocusView but you can drop those if you don't need them).
class FocusGroup : UIView
{
weak private var nextFocusView:UIView? = nil
weak var initialView:UIView? = nil
var captureFocus:Bool = false
func focusOnView(view:UIView, now:Bool=false)
{
if not(view.isDescendantOfView(self))
|| view === self
{ return }
nextFocusView = view
setNeedsFocusUpdate()
if now { updateFocusIfNeeded() }
}
func resetFocus(now now:Bool=false)
{
nextFocusView = nil
setNeedsFocusUpdate()
if now { updateFocusIfNeeded() }
}
override func canBecomeFocused() -> Bool
{
if nextFocusView != nil { return true }
if containsFocus { return false }
return firstFocusableSubView() != nil
}
func firstFocusableSubView() -> UIView?
{
return findSubview({
$0.canBecomeFocused()
&& $0.userInteractionEnabled
&& $0.visible
&& ( not($0 is UIButton)
|| ($0 as! UIButton).enabled )
})
}
override var preferredFocusedView: UIView?
{
if let viewToFocus = ( nextFocusView ?? initialView ) ?? firstFocusableSubView()
{
return viewToFocus
}
return nil
}
override func shouldUpdateFocusInContext(context: UIFocusUpdateContext) -> Bool
{
// when capturing focus, prevent navigation outside of grouped subviews
if captureFocus
&& containsFocus
&& context.previouslyFocusedView!.isDescendantOfView(self)
&& (
context.nextFocusedView == nil
|| context.nextFocusedView === self
|| not(context.nextFocusedView!.isDescendantOfView(self))
)
{ return false }
return true
}
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)
{
// give focus to specific view as requested
if nextFocusView != nil
{
if context.nextFocusedView === nextFocusView
|| not(nextFocusView!.canBecomeFocused())
{ nextFocusView = nil }
return
}
}
}
Just insert a focus guide above the button and to the left of the top collection view, redirecting focus to the top collection view:
focusGuide.preferredFocusedView = self.topView
self.view.addLayoutGuide(focusGuide)
self.focusGuide.topAnchor.constraintEqualToAnchor(topView.topAnchor).active = true
self.focusGuide.bottomAnchor.constraintEqualToAnchor(topView.bottomAnchor).active = true
self.focusGuide.leadingAnchor.constraintEqualToAnchor(btn.leadingAnchor).active = true
self.focusGuide.trailingAnchor.constraintEqualToAnchor(btn.trailingAnchor).active = true
You may also want to insert a focus guide to the right of the button and below the top collection view so that navigating down from the top row redirects focus to the button instead of to the bottom row.
I have a UIView which is placed on the screen via several constraints. Some of the constraints are owned by the superview, others are owned by other ancestors (e.g. perhaps the view property of a UIViewController).
I want to remove all of these old constraints, and place it somewhere new using new constraints.
How can I do this without creating an IBOutlet for every single constraint and having to remember which view owns said constraint?
To elaborate, the naive approach would be to create a bunch of IBOutlets for each of the constraints, and would then involve calling code such as:
[viewA removeConstraint:self.myViewsLeftConstraint];
[viewB removeConstraint:self.myViewsTopConstraint];
[viewB removeConstraint:self.myViewsBottomConstraint];
[self.view removeConstraint:self.myViewsRightConstraint];
The problem with this code is that even in the simplest case, I would need to create 2 IBOutlets. For complex layouts, this could easily reach 4 or 8 required IBOutlets. Furthermore, I would need to ensure that my call to remove the constraint is being called on the proper view. For example, imagine that myViewsLeftConstraint is owned by viewA. If I were to accidentally call [self.view removeConstraint:self.myViewsLeftConstraint], nothing would happen.
Note: The method constraintsAffectingLayoutForAxis looks promising, but is intended for debugging purposes only.
Update: Many of the answers I am receiving deal with self.constraints, self.superview.constraints, or some variant of those. These solutions won't work since those methods return only the constraints owned by the view, not the ones affecting the view.
To clarify the problem with these solutions, consider this view hierarchy:
Grandfather
Father
Me
Son
Daughter
Brother
Uncle
Now imagine we create the following constraints, and always attach them to their nearest common ancestor:
C0: Me: same top as Son (owned by Me)
C1: Me: width = 100 (owned by Me)
C2: Me: same height as Brother (owned by Father)
C3: Me: same top as Uncle (owned by Grandfather)
C4: Me: same left as Grandfather (owned by Grandfather)
C5: Brother: same left as Father (owned by Father)
C6: Uncle: same left as Grandfather (owned by Grandfather)
C7: Son: same left as Daughter (owned by Me)
Now imagine we want to remove all constraints affecting Me. Any proper solution should remove [C0,C1,C2,C3,C4] and nothing else.
If I use self.constraints (where self is Me), I will get [C0,C1,C7], since those are the only constraints owned by Me. Obviously it wouldn't be enough to remove this since it is missing [C2,C3,C4]. Furthermore, it is removing C7 unnecessarily.
If I use self.superview.constraints (where self is Me), I will get [C2,C5], since those are the constraints owned by Father. Obviously we cannot remove all these since C5 is completely unrelated to Me.
If I use grandfather.constraints, I will get [C3,C4,C6]. Again, we cannot remove all of these since C6 should remain intact.
The brute force approach is to loop over each of the view's ancestors (including itself), and seeing if firstItem or secondItem are the view itself; if so, remove that constraint. This will lead to a correct solution, returning [C0,C1,C2,C3,C4], and only those constraints.
However, I'm hoping there is a more elegant solution than having to loop through the entire list of ancestors.
This approach worked for me:
#interface UIView (RemoveConstraints)
- (void)removeAllConstraints;
#end
#implementation UIView (RemoveConstraints)
- (void)removeAllConstraints
{
UIView *superview = self.superview;
while (superview != nil) {
for (NSLayoutConstraint *c in superview.constraints) {
if (c.firstItem == self || c.secondItem == self) {
[superview removeConstraint:c];
}
}
superview = superview.superview;
}
[self removeConstraints:self.constraints];
self.translatesAutoresizingMaskIntoConstraints = YES;
}
#end
After it's done executing your view remains where it was because it creates autoresizing constraints. When I don't do this the view usually disappears. Additionally, it doesn't just remove constraints from superview but traversing all the way up as there may be constraints affecting it in ancestor views.
Swift 4 Version
extension UIView {
public func removeAllConstraints() {
var _superview = self.superview
while let superview = _superview {
for constraint in superview.constraints {
if let first = constraint.firstItem as? UIView, first == self {
superview.removeConstraint(constraint)
}
if let second = constraint.secondItem as? UIView, second == self {
superview.removeConstraint(constraint)
}
}
_superview = superview.superview
}
self.removeConstraints(self.constraints)
self.translatesAutoresizingMaskIntoConstraints = true
}
}
The only solution I have found so far is to remove the view from its superview:
[view removeFromSuperview]
This looks like it removes all constraints affecting its layout and is ready to be added to a superview and have new constraints attached. However, it will incorrectly remove any subviews from the hierarchy as well, and get rid of [C7] incorrectly.
You can remove all constraints in a view by doing this:
self.removeConstraints(self.constraints)
EDIT: To remove the constraints of all subviews, use the following extension in Swift:
extension UIView {
func clearConstraints() {
for subview in self.subviews {
subview.clearConstraints()
}
self.removeConstraints(self.constraints)
}
}
There are two ways of on how to achieve that according to Apple Developer Documentation
1. NSLayoutConstraint.deactivateConstraints
This is a convenience method that provides an easy way to deactivate a
set of constraints with one call. The effect of this method is the
same as setting the isActive property of each constraint to false.
Typically, using this method is more efficient than deactivating each
constraint individually.
// Declaration
class func deactivate(_ constraints: [NSLayoutConstraint])
// Usage
NSLayoutConstraint.deactivate(yourView.constraints)
2. UIView.removeConstraints (Deprecated for >= iOS 8.0)
When developing for iOS 8.0 or later, use the NSLayoutConstraint
class’s deactivateConstraints: method instead of calling the
removeConstraints: method directly. The deactivateConstraints: method
automatically removes the constraints from the correct views.
// Declaration
func removeConstraints(_ constraints: [NSLayoutConstraint])`
// Usage
yourView.removeConstraints(yourView.constraints)
Tips
Using Storyboards or XIBs can be such a pain at configuring the constraints as mentioned on your scenario, you have to create IBOutlets for each ones you want to remove. Even so, most of the time Interface Builder creates more trouble than it solves.
Therefore when having very dynamic content and different states of the view, I would suggest:
Creating your views programmatically
Layout them and using NSLayoutAnchor
Append each constraint that might get removed later to an array
Clear them every time before applying the new state
Simple Code
private var customConstraints = [NSLayoutConstraint]()
private func activate(constraints: [NSLayoutConstraint]) {
customConstraints.append(contentsOf: constraints)
customConstraints.forEach { $0.isActive = true }
}
private func clearConstraints() {
customConstraints.forEach { $0.isActive = false }
customConstraints.removeAll()
}
private func updateViewState() {
clearConstraints()
let constraints = [
view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor),
view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor),
view.topAnchor.constraint(equalTo: parentView.topAnchor),
view.bottomAnchor.constraint(equalTo: parentView.bottomAnchor)
]
activate(constraints: constraints)
view.layoutIfNeeded()
}
References
NSLayoutConstraint
UIView
In Swift:
import UIKit
extension UIView {
/**
Removes all constrains for this view
*/
func removeConstraints() {
let constraints = self.superview?.constraints.filter{
$0.firstItem as? UIView == self || $0.secondItem as? UIView == self
} ?? []
self.superview?.removeConstraints(constraints)
self.removeConstraints(self.constraints)
}
}
Details
Xcode 10.2.1 (10E1001), Swift 5
Solution
import UIKit
extension UIView {
func removeConstraints() { removeConstraints(constraints) }
func deactivateAllConstraints() { NSLayoutConstraint.deactivate(getAllConstraints()) }
func getAllSubviews() -> [UIView] { return UIView.getAllSubviews(view: self) }
func getAllConstraints() -> [NSLayoutConstraint] {
var subviewsConstraints = getAllSubviews().flatMap { $0.constraints }
if let superview = self.superview {
subviewsConstraints += superview.constraints.compactMap { (constraint) -> NSLayoutConstraint? in
if let view = constraint.firstItem as? UIView, view == self { return constraint }
return nil
}
}
return subviewsConstraints + constraints
}
class func getAllSubviews(view: UIView) -> [UIView] {
return view.subviews.flatMap { [$0] + getAllSubviews(view: $0) }
}
}
Usage
print("constraints: \(view.getAllConstraints().count), subviews: \(view.getAllSubviews().count)")
view.deactivateAllConstraints()
The easier and efficient approach is to remove the view from superView and re add as subview again.
this causes all the subview constraints get removed automagically.😉
Swift
Following UIView Extension will remove all Edge constraints of a view:
extension UIView {
func removeAllConstraints() {
if let _superview = self.superview {
self.removeFromSuperview()
_superview.addSubview(self)
}
}
}
A Swift solution:
extension UIView {
func removeAllConstraints() {
var view: UIView? = self
while let currentView = view {
currentView.removeConstraints(currentView.constraints.filter {
return $0.firstItem as? UIView == self || $0.secondItem as? UIView == self
})
view = view?.superview
}
}
}
It's important to go through all the parents, since the constraints between two elements are holds by the common ancestors, so just clearing the superview as detailed in this answer is not good enough, and you might end up having bad surprise later on.
Based on previous answers (swift 4)
You can use immediateConstraints when you don't want to crawl entire hierarchies.
extension UIView {
/**
* Deactivates immediate constraints that target this view (self + superview)
*/
func deactivateImmediateConstraints(){
NSLayoutConstraint.deactivate(self.immediateConstraints)
}
/**
* Deactivates all constrains that target this view
*/
func deactiveAllConstraints(){
NSLayoutConstraint.deactivate(self.allConstraints)
}
/**
* Gets self.constraints + superview?.constraints for this particular view
*/
var immediateConstraints:[NSLayoutConstraint]{
let constraints = self.superview?.constraints.filter{
$0.firstItem as? UIView === self || $0.secondItem as? UIView === self
} ?? []
return self.constraints + constraints
}
/**
* Crawls up superview hierarchy and gets all constraints that affect this view
*/
var allConstraints:[NSLayoutConstraint] {
var view: UIView? = self
var constraints:[NSLayoutConstraint] = []
while let currentView = view {
constraints += currentView.constraints.filter {
return $0.firstItem as? UIView === self || $0.secondItem as? UIView === self
}
view = view?.superview
}
return constraints
}
}
I use the following method to remove all constraints from a view:
.h file:
+ (void)RemoveContraintsFromView:(UIView*)view
removeParentConstraints:(bool)parent
removeChildConstraints:(bool)child;
.m file:
+ (void)RemoveContraintsFromView:(UIView *)view
removeParentConstraints:(bool)parent
removeChildConstraints:(bool)child
{
if (parent) {
// Remove constraints between view and its parent.
UIView *superview = view.superview;
[view removeFromSuperview];
[superview addSubview:view];
}
if (child) {
// Remove constraints between view and its children.
[view removeConstraints:[view constraints]];
}
}
You can also read this post on my blog to better understand how it works behind the hood.
If you need more granular control, I'd strongly advise switching to Masonry, a powerful framework class you could use whenever you need to properly handle constraints programmatically.
With objectiveC
[self.superview.constraints enumerateObjectsUsingBlock:^(__kindof NSLayoutConstraint * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLayoutConstraint *constraint = (NSLayoutConstraint *)obj;
if (constraint.firstItem == self || constraint.secondItem == self) {
[self.superview removeConstraint:constraint];
}
}];
[self removeConstraints:self.constraints];
}
You could use something like this:
[viewA.superview.constraints enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLayoutConstraint *constraint = (NSLayoutConstraint *)obj;
if (constraint.firstItem == viewA || constraint.secondItem == viewA) {
[viewA.superview removeConstraint:constraint];
}
}];
[viewA removeConstraints:viewA.constraints];
Basically, this is enumerates over all the constraints on the superview of viewA and removes all of the constraints that are related to viewA.
Then, the second part removes the constraints from viewA using the array of viewA's constraints.
(As of July 31, 2017)
SWIFT 3
self.yourCustomView.removeFromSuperview()
self.yourCustomViewParentView.addSubview(self.yourCustomView)
Objective C
[self.yourCustomView removeFromSuperview];
[self.yourCustomViewParentView addSubview:self.yourCustomView];
This is the easiest way to quickly remove all constraints that exist on a UIView. Just be sure to add the UIView back with it's new constraints or new frame afterwards =)
Using a Reusable Sequence
I decided to approach this in a more 'reusable' way. Since finding all constraints affecting a view is the basis for all of the above, I decided to implement a custom sequence that returns them all for me, along with the owning views.
First thing to do is define an extension on Arrays of NSLayoutConstraint that returns all elements affecting a specific view.
public extension Array where Element == NSLayoutConstraint {
func affectingView(_ targetView:UIView) -> [NSLayoutConstraint] {
return self.filter{
if let firstView = $0.firstItem as? UIView,
firstView == targetView {
return true
}
if let secondView = $0.secondItem as? UIView,
secondView == targetView {
return true
}
return false
}
}
}
We then use that extension in a custom sequence that returns all constraints affecting that view, along with the views that actually own them (which can be anywhere up the view hierarchy)
public struct AllConstraintsSequence : Sequence {
public init(view:UIView){
self.view = view
}
public let view:UIView
public func makeIterator() -> Iterator {
return Iterator(view:view)
}
public struct Iterator : IteratorProtocol {
public typealias Element = (constraint:NSLayoutConstraint, owningView:UIView)
init(view:UIView){
targetView = view
currentView = view
currentViewConstraintsAffectingTargetView = currentView.constraints.affectingView(targetView)
}
private let targetView : UIView
private var currentView : UIView
private var currentViewConstraintsAffectingTargetView:[NSLayoutConstraint] = []
private var nextConstraintIndex = 0
mutating public func next() -> Element? {
while(true){
if nextConstraintIndex < currentViewConstraintsAffectingTargetView.count {
defer{nextConstraintIndex += 1}
return (currentViewConstraintsAffectingTargetView[nextConstraintIndex], currentView)
}
nextConstraintIndex = 0
guard let superview = currentView.superview else { return nil }
self.currentView = superview
self.currentViewConstraintsAffectingTargetView = currentView.constraints.affectingView(targetView)
}
}
}
}
Finally we declare an extension on UIView to expose all the constraints affecting it in a simple property that you can access with a simple for-each syntax.
extension UIView {
var constraintsAffectingView:AllConstraintsSequence {
return AllConstraintsSequence(view:self)
}
}
Now we can iterate all constraints affecting a view and do what we want with them...
List their identifiers...
for (constraint, _) in someView.constraintsAffectingView{
print(constraint.identifier ?? "No identifier")
}
Deactivate them...
for (constraint, _) in someView.constraintsAffectingView{
constraint.isActive = false
}
Or remove them entirely...
for (constraint, owningView) in someView.constraintsAffectingView{
owningView.removeConstraints([constraint])
}
Enjoy!
This is the way to disable all constraints from a specific view
NSLayoutConstraint.deactivate(myView.constraints)