Ich habe eine UIView, die über verschiedene Einschränkungen auf dem Bildschirm angezeigt wird. Einige der Einschränkungen gehören der Übersicht, andere anderen Vorfahren (z. B. die Ansichtseigenschaft eines UIViewControllers).
Ich möchte all diese alten Einschränkungen entfernen und sie mithilfe neuer Einschränkungen an einem neuen Ort platzieren.
Wie kann ich dies tun, ohne für jede einzelne Einschränkung ein IBOutlet zu erstellen und mich daran erinnern zu müssen, welcher Ansicht diese Einschränkung gehört?
Um dies zu erläutern, würde der naive Ansatz darin bestehen, eine Reihe von IBOutlets für jede der Einschränkungen zu erstellen und dann Code aufzurufen, wie z.
[viewA removeConstraint:self.myViewsLeftConstraint];
[viewB removeConstraint:self.myViewsTopConstraint];
[viewB removeConstraint:self.myViewsBottomConstraint];
[self.view removeConstraint:self.myViewsRightConstraint];
Das Problem mit diesem Code ist, dass ich selbst im einfachsten Fall 2 IBOutlets erstellen müsste. Bei komplexen Layouts können leicht 4 oder 8 erforderliche IBOutlets erreicht werden. Außerdem müsste ich sicherstellen, dass mein Aufruf zum Entfernen der Einschränkung in der richtigen Ansicht aufgerufen wird. Stellen Sie sich zum Beispiel vor, das myViewsLeftConstraint
gehört viewA
. Wenn ich versehentlich anrufen [self.view removeConstraint:self.myViewsLeftConstraint]
würde, würde nichts passieren.
Hinweis: Die Methode ConstrainctionsAffectingLayoutForAxis sieht vielversprechend aus, ist jedoch nur für Debugging-Zwecke vorgesehen.
Update: Viele der Antworten , die ich viel erhalte mit self.constraints
, self.superview.constraints
oder irgendeine Variante davon. Diese Lösungen funktionieren nicht, da diese Methoden nur die Einschränkungen zurückgeben, die der Ansicht gehören, nicht diejenigen , die sich auf die Ansicht auswirken .
Berücksichtigen Sie diese Ansichtshierarchie, um das Problem mit diesen Lösungen zu klären:
- Großvater
- Vater
- Mich
- Sohn
- Tochter
- Bruder
- Mich
- Onkel
- Vater
Stellen Sie sich nun vor, wir erstellen die folgenden Einschränkungen und hängen sie immer an den nächsten gemeinsamen Vorfahren an:
- C0: Me: das gleiche Top wie Son (im Besitz von Me)
- C1: Me: width = 100 (im Besitz von Me)
- C2: Ich: gleiche Größe wie Bruder (im Besitz von Vater)
- C3: Ich: das gleiche Top wie Onkel (im Besitz des Großvaters)
- C4: Ich: gleich links wie Großvater (im Besitz von Großvater)
- C5: Bruder: links wie Vater (im Besitz von Vater)
- C6: Onkel: links wie Großvater (im Besitz von Großvater)
- C7: Sohn: links wie Tochter (im Besitz von mir)
Stellen Sie sich nun vor, wir möchten alle betroffenen Einschränkungen entfernen Me
. Jede richtige Lösung sollte entfernt werden [C0,C1,C2,C3,C4]
und sonst nichts.
Wenn ich benutze self.constraints
(wo ich selbst bin), werde ich bekommen [C0,C1,C7]
, da dies die einzigen Einschränkungen sind, die mir gehören. Offensichtlich würde es nicht ausreichen, dies zu entfernen, da es fehlt [C2,C3,C4]
. Darüber hinaus wird es C7
unnötig entfernt.
Wenn ich benutze self.superview.constraints
(wo ich selbst bin), werde ich es bekommen [C2,C5]
, da dies die Zwänge sind, die Vater gehört. Offensichtlich können wir nicht alle diese entfernen, da dies C5
völlig unabhängig ist Me
.
Wenn ich benutze grandfather.constraints
, werde ich bekommen [C3,C4,C6]
. Auch hier können wir nicht alle entfernen, da dies C6
intakt bleiben sollte.
Der Brute-Force-Ansatz besteht darin, alle Vorfahren der Ansicht (einschließlich sich selbst) zu durchlaufen und zu prüfen, ob firstItem
oder ob secondItem
die Ansicht selbst vorhanden ist. Wenn ja, entfernen Sie diese Einschränkung. Dies führt zu einer korrekten Lösung, der Rückgabe [C0,C1,C2,C3,C4]
und nur diesen Einschränkungen.
Ich hoffe jedoch, dass es eine elegantere Lösung gibt, als die gesamte Liste der Vorfahren durchlaufen zu müssen.
quelle
Antworten:
Dieser Ansatz hat bei mir funktioniert:
@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
Nach Abschluss der Ausführung bleibt Ihre Ansicht dort, wo sie war, da sie Einschränkungen für die automatische Größenänderung erstellt. Wenn ich das nicht mache, verschwindet die Ansicht normalerweise. Darüber hinaus werden nicht nur Einschränkungen aus der Übersicht entfernt, sondern ganz nach oben verschoben, da in den Ansichten der Vorfahren möglicherweise Einschränkungen auftreten.
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 } }
quelle
C7
. Allerdings sollte es leicht sein , zu beheben , wenn Sie entfernen[self removeConstraints:self.constraints];
und ändernUIView *superview = self.superview;
zuUIView *superview = self;
.Die einzige Lösung, die ich bisher gefunden habe, besteht darin, die Ansicht aus der Übersicht zu entfernen:
Dies sieht so aus, als würde es alle Einschränkungen entfernen, die sich auf das Layout auswirken, und kann einer Übersicht hinzugefügt und mit neuen Einschränkungen versehen werden. Es werden jedoch auch Unteransichten fälschlicherweise aus der Hierarchie entfernt und
[C7]
falsch entfernt.quelle
Sie können alle Einschränkungen in einer Ansicht folgendermaßen entfernen:
self.removeConstraints(self.constraints)
BEARBEITEN: Um die Einschränkungen aller Unteransichten zu entfernen, verwenden Sie die folgende Erweiterung in Swift:
extension UIView { func clearConstraints() { for subview in self.subviews { subview.clearConstraints() } self.removeConstraints(self.constraints) } }
quelle
self.constraints
Gibt nur die eigenen Einschränkungen zurückself
, nicht alle betroffenen Einschränkungenself
.[C6]
und fälschlicherweise nicht entfernt[C0,C1,C2]
.Laut Apple Developer Documentation gibt es zwei Möglichkeiten, wie dies erreicht werden kann
1.
NSLayoutConstraint.deactivateConstraints
// Declaration class func deactivate(_ constraints: [NSLayoutConstraint]) // Usage NSLayoutConstraint.deactivate(yourView.constraints)
2.
UIView.removeConstraints
(Veraltet für> = iOS 8.0)// Declaration func removeConstraints(_ constraints: [NSLayoutConstraint])` // Usage yourView.removeConstraints(yourView.constraints)
Tipps
Die Verwendung von
Storyboard
s oderXIB
s kann bei der Konfiguration der in Ihrem Szenario genannten Einschränkungen so schwierig sein. Sie müssen IBOutlets für alle Einschränkungen erstellen, die Sie entfernen möchten. Trotzdem verursacht die meiste ZeitInterface Builder
mehr Probleme als es löst.Daher würde ich bei sehr dynamischen Inhalten und unterschiedlichen Ansichtszuständen Folgendes vorschlagen:
Einfacher 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() }
Verweise
quelle
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) } }
quelle
if c.firstItem === self
anstatt sie zuas? UIView
==
Einzelheiten
Lösung
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) } } }
Verwendung
print("constraints: \(view.getAllConstraints().count), subviews: \(view.getAllSubviews().count)") view.deactivateAllConstraints()
quelle
Eine schnelle Lösung:
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 } } }
Es ist wichtig, alle Eltern durchzugehen, da die Einschränkungen zwischen zwei Elementen von den gemeinsamen Vorfahren eingehalten werden. Daher ist es nicht gut genug, nur die in dieser Antwort beschriebene Übersicht zu löschen , und Sie könnten später eine schlechte Überraschung erleben .
quelle
Basierend auf früheren Antworten (schnell 4)
Sie können sofortige Einschränkungen verwenden, wenn Sie nicht ganze Hierarchien crawlen möchten.
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 } }
quelle
Ich verwende die folgende Methode, um alle Einschränkungen aus einer Ansicht zu entfernen:
.h Datei:
+ (void)RemoveContraintsFromView:(UIView*)view removeParentConstraints:(bool)parent removeChildConstraints:(bool)child;
.m Datei:
+ (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]]; } }
Sie können diesen Beitrag auch in meinem Blog lesen, um besser zu verstehen, wie er hinter der Haube funktioniert.
Wenn Sie eine genauere Steuerung benötigen, empfehle ich dringend, zu Masonry zu wechseln , einer leistungsstarken Framework-Klasse, die Sie verwenden können, wenn Sie Einschränkungen programmgesteuert richtig handhaben müssen.
quelle
Der einfachere und effizientere Ansatz besteht darin, die Ansicht aus superView zu entfernen und erneut als Unteransicht hinzuzufügen. Dadurch werden alle Einschränkungen der Unteransicht automatisch entfernt
quelle
Mit objectivC
[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]; }
quelle
Sie könnten so etwas verwenden:
[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];
Grundsätzlich werden alle Einschränkungen in der Übersicht von viewA aufgelistet und alle Einschränkungen entfernt, die sich auf viewA beziehen.
Anschließend entfernt der zweite Teil die Einschränkungen aus viewA mithilfe des Arrays der Einschränkungen von viewA.
quelle
[C7]
und fälschlicherweise nicht entfernt[C3,C4]
.(Stand 31. Juli 2017)
SWIFT 3
self.yourCustomView.removeFromSuperview() self.yourCustomViewParentView.addSubview(self.yourCustomView)
Ziel c
[self.yourCustomView removeFromSuperview]; [self.yourCustomViewParentView addSubview:self.yourCustomView];
Dies ist der einfachste Weg, um alle in einer UIView vorhandenen Einschränkungen schnell zu entfernen. Stellen Sie einfach sicher, dass Sie das UIView mit seinen neuen Einschränkungen oder dem neuen Frame danach wieder hinzufügen =)
quelle
Verwenden einer wiederverwendbaren Sequenz
Ich beschloss, dies auf eine "wiederverwendbarere" Weise anzugehen. Da das Auffinden aller Einschränkungen, die sich auf eine Ansicht auswirken, die Grundlage für alle oben genannten Punkte ist, habe ich beschlossen, eine benutzerdefinierte Sequenz zu implementieren, die sie alle zusammen mit den eigenen Ansichten für mich zurückgibt.
Das erste , was zu tun ist , eine Erweiterung definieren auf
Arrays
derNSLayoutConstraint
alle Elemente , die Erträge eine bestimmte Ansicht zu beeinflussen.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 } } }
Wir verwenden diese Erweiterung dann in einer benutzerdefinierten Sequenz, die alle Einschränkungen zurückgibt, die diese Ansicht betreffen, sowie die Ansichten, deren Eigentümer sie tatsächlich sind (die sich an einer beliebigen Stelle in der Ansichtshierarchie befinden können).
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) } } } }
Schließlich deklarieren wir eine Erweiterung für
UIView
, um alle sie betreffenden Einschränkungen in einer einfachen Eigenschaft verfügbar zu machen, auf die Sie mit einer einfachen Syntax für jede zugreifen können.extension UIView { var constraintsAffectingView:AllConstraintsSequence { return AllConstraintsSequence(view:self) } }
Jetzt können wir alle Einschränkungen, die sich auf eine Ansicht auswirken, iterieren und mit ihnen tun, was wir wollen ...
Listen Sie ihre Kennungen auf ...
for (constraint, _) in someView.constraintsAffectingView{ print(constraint.identifier ?? "No identifier") }
Deaktiviere sie ...
for (constraint, _) in someView.constraintsAffectingView{ constraint.isActive = false }
Oder entfernen Sie sie vollständig ...
for (constraint, owningView) in someView.constraintsAffectingView{ owningView.removeConstraints([constraint]) }
Genießen!
quelle
Schnell
Die folgende UIView-Erweiterung entfernt alle Kanteneinschränkungen einer Ansicht:
extension UIView { func removeAllConstraints() { if let _superview = self.superview { self.removeFromSuperview() _superview.addSubview(self) } } }
quelle
Auf diese Weise können Sie alle Einschränkungen in einer bestimmten Ansicht deaktivieren
NSLayoutConstraint.deactivate(myView.constraints)
quelle