У меня есть UIView, который размещается на экране с помощью нескольких ограничений. Некоторые из ограничений принадлежат супервизору, другие принадлежат другим предкам (например, возможно, свойство представления UIViewController).
Я хочу удалить все эти старые ограничения и поместить их в новое место, используя новые ограничения.
Как я могу сделать это, не создавая IBOutlet для каждого ограничения и не запоминая, какое представление владеет указанным ограничением?
Чтобы уточнить, наивный подход заключался бы в создании группы IBOutlets для каждого из ограничений, а затем включал бы вызывающий код, например:
[viewA removeConstraint:self.myViewsLeftConstraint];
[viewB removeConstraint:self.myViewsTopConstraint];
[viewB removeConstraint:self.myViewsBottomConstraint];
[self.view removeConstraint:self.myViewsRightConstraint];
Проблема с этим кодом в том, что даже в простейшем случае мне нужно было бы создать 2 IBOutlet. Для сложных макетов это может легко достичь 4 или 8 требуемых IBOutlets. Кроме того, мне нужно будет убедиться, что мой вызов для удаления ограничения вызывается в правильном представлении. Например, представьте, что myViewsLeftConstraint
это принадлежит viewA
. Если бы я случайно позвонил [self.view removeConstraint:self.myViewsLeftConstraint]
, ничего бы не случилось.
Примечание . Метод constraintsAffectingLayoutForAxis выглядит многообещающим, но предназначен только для целей отладки.
Обновление: Многие из ответов я получаю дело с self.constraints
, self.superview.constraints
или каким - либо вариантом из них. Эти решения не будут работать, поскольку эти методы возвращают только ограничения, принадлежащие представлению, а не те, которые влияют на представление.
Чтобы прояснить проблему с этими решениями, рассмотрим эту иерархию представлений:
- Дедушка
- Отец
- мне
- Сын
- Дочь
- Родной брат
- мне
- Дядя
- Отец
Теперь представьте, что мы создаем следующие ограничения и всегда прикрепляем их к их ближайшему общему предку:
- C0: Я: такой же топ, что и Сын (принадлежит мне)
- C1: Me: width = 100 (принадлежит мне)
- C2: Я: одного роста с братом (принадлежит отцу)
- C3: Я: такая же вершина, как у дяди (принадлежит дедушке)
- C4: Я: то же самое, что и дедушка (принадлежит дедушке)
- C5: Брат: тот же, что и отец (принадлежит отцу)
- C6: Дядя: то же самое, что и дедушка (принадлежит дедушке)
- C7: Сын: такой же, как и дочь (принадлежит Мне)
Теперь представьте, что мы хотим удалить все влияющие ограничения Me
. Любой правильный раствор должен удалять [C0,C1,C2,C3,C4]
и ничего больше.
Если я использую self.constraints
(где self - это Я), я получу [C0,C1,C7]
, поскольку это единственные ограничения, которыми я владею . Очевидно, этого было бы недостаточно, так как он отсутствует [C2,C3,C4]
. Кроме того, он удаляется C7
без надобности.
Если я использую self.superview.constraints
(где я - это Я), я получу [C2,C5]
, поскольку это ограничения, принадлежащие Отцу. Очевидно, мы не можем удалить все это, поскольку C5
это совершенно не связано с Me
.
Если воспользуюсь grandfather.constraints
, получу [C3,C4,C6]
. Опять же, мы не можем удалить все это, поскольку они C6
должны остаться нетронутыми.
Подход грубой силы состоит в том, чтобы перебирать каждого из предков представления (включая его самого) и проверять, является ли само представление firstItem
или оно secondItem
есть; если да, удалите это ограничение. Это приведет к правильному решению, возврату [C0,C1,C2,C3,C4]
и только этим ограничениям.
Однако я надеюсь, что есть более элегантное решение, чем необходимость перебирать весь список предков.
источник
Ответы:
Этот подход сработал для меня:
@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
После завершения выполнения ваше представление остается на месте, потому что оно создает ограничения автоизменения размеров. Когда я этого не делаю, вид обычно исчезает. Кроме того, он не просто удаляет ограничения из супервизора, но и проходит весь путь вверх, так как могут быть ограничения, влияющие на него в представлениях предков.
Версия Swift 4
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 } }
источник
C7
. Однако это должно быть легко исправить, если вы удалите[self removeConstraints:self.constraints];
и изменитеUIView *superview = self.superview;
наUIView *superview = self;
.Единственное решение, которое я нашел до сих пор, - удалить представление из его супервизора:
Похоже, что он удаляет все ограничения, влияющие на его макет, и готов к добавлению в супервизор и присоединению новых ограничений. Однако он также неправильно удалит из иерархии любые подпредставления и избавится от них
[C7]
неправильно.источник
Вы можете удалить все ограничения в представлении, выполнив следующие действия:
self.removeConstraints(self.constraints)
РЕДАКТИРОВАТЬ: Чтобы удалить ограничения всех подвидов, используйте следующее расширение в Swift:
extension UIView { func clearConstraints() { for subview in self.subviews { subview.clearConstraints() } self.removeConstraints(self.constraints) } }
источник
self.constraints
возвращает только ограничения, которымиself
владеет, а не все ограничения, которые влияютself
.[C6]
, а неправильно не удалит[C0,C1,C2]
.Согласно документации разработчика Apple, есть два способа добиться этого.
1.
NSLayoutConstraint.deactivateConstraints
// Declaration class func deactivate(_ constraints: [NSLayoutConstraint]) // Usage NSLayoutConstraint.deactivate(yourView.constraints)
2.
UIView.removeConstraints
(Не рекомендуется для> = iOS 8.0)// Declaration func removeConstraints(_ constraints: [NSLayoutConstraint])` // Usage yourView.removeConstraints(yourView.constraints)
подсказки
Использование
Storyboard
s илиXIB
s может быть такой проблемой при настройке ограничений, как указано в вашем сценарии, вам нужно создать IBOutlets для каждого из них, которые вы хотите удалить. Даже в этом случае в большинстве случаевInterface Builder
создает больше проблем, чем решает.Поэтому, имея очень динамичный контент и разные состояния представления, я бы предложил:
Простой код
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() }
Ссылки
источник
В 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) } }
источник
if c.firstItem === self
вместо приведенияas? UIView
и проверить равенство с помощью==
Детали
Решение
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) } } }
Применение
print("constraints: \(view.getAllConstraints().count), subviews: \(view.getAllSubviews().count)") view.deactivateAllConstraints()
источник
Быстрое решение:
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 } } }
Важно пройти через всех родителей, поскольку ограничения между двумя элементами удерживаются общими предками, поэтому просто очистить супервизор, как подробно описано в этом ответе , недостаточно, и вы можете в конечном итоге получить неприятный сюрприз позже.
источник
На основе предыдущих ответов (быстрый 4)
Если вы не хотите сканировать целые иерархии, вы можете использовать немедленные ограничения.
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 } }
источник
Я использую следующий метод для удаления всех ограничений из представления:
.h файл:
+ (void)RemoveContraintsFromView:(UIView*)view removeParentConstraints:(bool)parent removeChildConstraints:(bool)child;
.m файл:
+ (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]]; } }
Вы также можете прочитать этот пост в моем блоге, чтобы лучше понять, как это работает.
Если вам нужен более детальный контроль, я настоятельно рекомендую перейти на Masonry , мощный класс фреймворка, который вы можете использовать всякий раз, когда вам нужно правильно обрабатывать ограничения программно.
источник
Более простой и эффективный подход - удалить представление из суперпредставления и снова добавить его как подпредставление. это приводит к тому, что все ограничения subview удаляются автоматически.
источник
С объективомC
[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]; }
источник
Вы можете использовать что-то вроде этого:
[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];
По сути, это перечисляет все ограничения супервизора viewA и удаляет все ограничения, связанные с viewA.
Затем вторая часть удаляет ограничения из viewA, используя массив ограничений viewA.
источник
[C7]
неправильно и не удаляет неправильно[C3,C4]
.(По состоянию на 31 июля 2017 г.)
SWIFT 3
self.yourCustomView.removeFromSuperview() self.yourCustomViewParentView.addSubview(self.yourCustomView)
Цель C
[self.yourCustomView removeFromSuperview]; [self.yourCustomViewParentView addSubview:self.yourCustomView];
Это самый простой способ быстро удалить все ограничения, существующие в UIView. Только не забудьте после этого добавить UIView с новыми ограничениями или новым фреймом =)
источник
Использование многоразовой последовательности
Я решил подойти к этому более «многоразовым» способом. Поскольку поиск всех ограничений, влияющих на представление, является основой для всего вышеперечисленного, я решил реализовать настраиваемую последовательность, которая возвращает их все вместе с собственными представлениями.
Первое , что нужно сделать , это определить расширение на
Arrays
из того,NSLayoutConstraint
что возвращает все элементы , влияющие на вид конкретный.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 } } }
Затем мы используем это расширение в настраиваемой последовательности, которая возвращает все ограничения, влияющие на это представление, вместе с представлениями, которым они фактически принадлежат (которые могут быть где угодно вверх по иерархии представлений).
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) } } } }
Наконец, мы объявляем расширение
UIView
для отображения всех влияющих на него ограничений в простом свойстве, к которому вы можете получить доступ с помощью простого синтаксиса for-each.extension UIView { var constraintsAffectingView:AllConstraintsSequence { return AllConstraintsSequence(view:self) } }
Теперь мы можем перебирать все ограничения, влияющие на представление, и делать с ними все, что захотим ...
Перечислите их идентификаторы ...
for (constraint, _) in someView.constraintsAffectingView{ print(constraint.identifier ?? "No identifier") }
Деактивировать их ...
for (constraint, _) in someView.constraintsAffectingView{ constraint.isActive = false }
Или удалите их полностью ...
for (constraint, owningView) in someView.constraintsAffectingView{ owningView.removeConstraints([constraint]) }
Наслаждайтесь!
источник
Swift
Следующее расширение UIView удалит все ограничения Edge представления:
extension UIView { func removeAllConstraints() { if let _superview = self.superview { self.removeFromSuperview() _superview.addSubview(self) } } }
источник
Это способ отключить все ограничения из определенного представления
NSLayoutConstraint.deactivate(myView.constraints)
источник