Удалите все ограничения, влияющие на UIView



у меня есть UIView, который размещается на экране с помощью нескольких ограничений. Некоторые из ограничений принадлежат superview, другие принадлежат другим предкам (например, возможно, свойство view UIViewController).



я хочу удалить все эти старые ограничения, и поместить его где-то новый, используя новые ограничения.



как я могу это сделать без создания IBOutlet для каждого ограничения и необходимости помнить, какой вид имеет сказал принуждение?



чтобы разработать, наивный подход заключался бы в создании группы IBOutlets для каждого из ограничений, а затем включал бы вызов кода, такого как:



[viewA removeConstraint:self.myViewsLeftConstraint];
[viewB removeConstraint:self.myViewsTopConstraint];
[viewB removeConstraint:self.myViewsBottomConstraint];
[self.view removeConstraint:self.myViewsRightConstraint];


проблема с этим кодом заключается в том, что даже в самом простом случае мне нужно будет создать 2 IBOutlets. Для сложных планов, это смогло легко достигнуть 4 или 8 необходимых IBOutlets. Кроме того, мне нужно будет убедиться, что мой вызов для удаления ограничения вызывается в правильном представлении. Для например, представьте себе, что myViewsLeftConstraint принадлежит viewA. Если бы я случайно позвонил [self.view removeConstraint:self.myViewsLeftConstraint] ничего не случится.



Примечание: метод constraintsAffectingLayoutForAxis выглядит многообещающе, но предназначен только для отладки.





обновление: многие из ответов, которые я получаю, имеют дело с self.constraints,self.superview.constraints, или какой-то их вариант. Эти решения не будут работать, так как эти методы возвращают только ограничения принадлежащего судя по виду, не те влияет вид.



чтобы прояснить проблему с этими решениями, рассмотрим эту иерархию представлений:




  • Дед


    • отец


      • меня


        • сын

        • дочь



      • брат



    • дядя




теперь представьте, что мы создаем следующие ограничения, и всегда прикрепляйте их к своему ближайшему общему предку:




  • C0: Me: тот же Топ, что и у сына (принадлежит мне)

  • C1: Me: width = 100 (принадлежит мне)

  • C2: Me: тот же рост, что и у брата (принадлежит отцу)

  • C3: Me: тот же Топ, что и у дяди (принадлежит дедушке)

  • C4: Me: то же самое осталось от дедушки (принадлежит дедушке)

  • C5: брат: тот же левый, что и отец (принадлежит Отец)

  • C6: дядя: то же, что и дедушка (принадлежит дедушке)

  • C7: сын: то же самое осталось как дочь (принадлежит мне)


теперь представьте, что мы хотим удалить все ограничения, влияющие Me. Любое правильное решение должно удалить [C0,C1,C2,C3,C4] и больше ничего.



если я использую self.constraints (где я сам), я получу [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] и только те ограничения.



тем не менее, я надеюсь, что есть более элегантное решение, чем необходимость перебирать весь список предков.

738   12  

12 ответов:

этот подход работал для меня:

@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

после выполнения вашего просмотра остается там, где он был, потому что он создает трудности, функциями автоматического изменения. Когда я этого не делаю, вид обычно исчезает. Кроме того, он не просто удаляет ограничения из супервизора, но и проходит весь путь вверх, поскольку могут быть ограничения, влияющие на него в представлениях предков.

вы можете удалить все ограничения в представлении, сделав это:

[_cell.contentView removeConstraints:_cell.contentView.constraints];

EDIT: чтобы удалить ограничения всех подвидов, используйте следующее расширение в Swift:

extension UIView {
    func clearConstraints() {
        for subview in self.subviews {
            subview.clearConstraints()
        }
        self.removeConstraints(self.constraints)
    }
}

единственное решение, которое я нашел до сих пор, это удалить представление из его супервизора:

[view removeFromSuperview]

похоже, что он удаляет все ограничения, влияющие на его макет, и готов к добавлению в супервизор и присоединению новых ограничений. Однако он также неправильно удалит все подвиды из иерархии и избавится от [C7] неправильно.

В Swift:

import UIKit

extension UIView {

    /**
     Removes all constrains for this view
     */
    func removeConstraints() {

        let constraints = self.superview?.constraints.filter{
            .firstItem as? UIView == self || .secondItem as? UIView == self
        } ?? []

        self.superview?.removeConstraints(constraints)
        self.removeConstraints(self.constraints)
    }
}

есть два способа о том, как достичь этого в соответствии с Документация Для Разработчиков Apple

1. NSLayoutConstraint.deactivateConstraints

это удобный метод, который обеспечивает простой способ деактивации набор ограничений с одним вызовом. Эффект этого метода заключается в следующем то же самое, что установить свойство isActive каждого ограничения в false. Как правило, использование этого метода более эффективно, чем отключение каждого ограничение индивидуально.

Свифт

// Declaration
class func deactivate(_ constraints: [NSLayoutConstraint])

// Usage
NSLayoutConstraint.deactivate(yourView.constraints)

С

// Declaration
+ (void)deactivateConstraints:(NSArray<NSLayoutConstraint *> *)constraints;

// Usage
[NSLayoutConstraint deactivateConstraints:yourView.constraints];

2. UIView.removeConstraints (не рекомендуется для > = iOS 8.0)

при разработке для iOS 8.0 или более поздней версии, используйте NSLayoutConstraint класс deactivateConstraints: метод вместо вызова removeConstraints: метод непосредственно. В deactivateConstraints: способ автоматически удаляет ограничения из правильного просмотр.

Свифт

// Declaration
func removeConstraints(_ constraints: [NSLayoutConstraint])`

// Usage
yourView.removeConstraints(yourView.constraints)

С

// Declaration
- (void)removeConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints;

// Usage
[yourView removeConstraints:yourView.constraints];

ссылки

  1. NSLayoutConstraint
  2. UIView

подробности

Xcode 9.1, Swift 4

решение

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 { (view) -> [NSLayoutConstraint] in
            return view.constraints
        }

        if let superview = self.superview {
            subviewsConstraints += superview.constraints.flatMap{ (constraint) -> NSLayoutConstraint? in
                if let view = constraint.firstItem as? UIView {
                    if view == self {
                        return constraint
                    }
                }
                return nil
            }
        }

        return subviewsConstraints + constraints
    }

    class func getAllSubviews(view: UIView) -> [UIView] {
        return view.subviews.flatMap { subView -> [UIView] in
            return [subView] + getAllSubviews(view: subView)
        }
    }
}

использование

view.deactivateAllConstraints()

Полная Выборка

ViewController

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var simpleView: UIView!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        sampleWithViewFromStoryBoard()
        sampleWithViewGeneratedProgrammaticallyted()
    }

    private func removeConstraints(view: UIView) {
        print("constraints: \(view.getAllConstraints().count), subviews: \(view.getAllSubviews().count)")
        view.deactivateAllConstraints()
        print("constraints: \(view.getAllConstraints().count), subviews: \(view.getAllSubviews().count)")
    }

    func sampleWithViewFromStoryBoard() {
        print("\nStoryboard:")
        removeConstraints(view: simpleView)
    }

    func sampleWithViewGeneratedProgrammaticallyted() {
        print("\nProgrammatically:")
        let view = UIView(frame: .zero)
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .blue
        self.view.addSubview(view)

        view.widthAnchor.constraint(equalToConstant: 200).isActive = true
        view.heightAnchor.constraint(equalToConstant: 200).isActive = true
        view.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 100).isActive = true
        view.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100).isActive = true

        removeConstraints(view: view)
    }

}

раскадровка

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="stackoverflow_24418884" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5Ni-DW-acW">
                                <rect key="frame" x="16" y="20" width="240" height="128"/>
                                <subviews>
                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="phZ-9f-16a">
                                        <rect key="frame" x="188" y="8" width="42" height="21"/>
                                        <constraints>
                                            <constraint firstAttribute="width" constant="42" id="QHb-On-hfY"/>
                                            <constraint firstAttribute="height" constant="21" id="Sfy-Pn-YGi"/>
                                        </constraints>
                                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                        <nil key="textColor"/>
                                        <nil key="highlightedColor"/>
                                    </label>
                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6gs-OS-OBa">
                                        <rect key="frame" x="97" y="98" width="46" height="30"/>
                                        <constraints>
                                            <constraint firstAttribute="height" constant="30" id="L0A-My-PAS"/>
                                        </constraints>
                                        <state key="normal" title="Button"/>
                                    </button>
                                    <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="TvD-24-wfT">
                                        <rect key="frame" x="20" y="20" width="60" height="80"/>
                                        <subviews>
                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hIi-fL-pqH">
                                                <rect key="frame" x="0.0" y="0.0" width="60" height="26.5"/>
                                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                                <nil key="textColor"/>
                                                <nil key="highlightedColor"/>
                                            </label>
                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ahb-L9-itj">
                                                <rect key="frame" x="0.0" y="26.5" width="60" height="27"/>
                                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                                <nil key="textColor"/>
                                                <nil key="highlightedColor"/>
                                            </label>
                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="A5a-DJ-Mem">
                                                <rect key="frame" x="0.0" y="53.5" width="60" height="26.5"/>
                                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                                <nil key="textColor"/>
                                                <nil key="highlightedColor"/>
                                            </label>
                                        </subviews>
                                        <constraints>
                                            <constraint firstAttribute="height" constant="80" id="uwc-lA-VQH"/>
                                            <constraint firstAttribute="width" constant="60" id="xzx-yt-Svs"/>
                                        </constraints>
                                    </stackView>
                                </subviews>
                                <color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
                                <constraints>
                                    <constraint firstItem="phZ-9f-16a" firstAttribute="top" secondItem="5Ni-DW-acW" secondAttribute="top" constant="8" id="1y5-vl-Tar"/>
                                    <constraint firstAttribute="trailing" secondItem="phZ-9f-16a" secondAttribute="trailing" constant="10" id="8DX-L0-aFJ"/>
                                    <constraint firstItem="TvD-24-wfT" firstAttribute="leading" secondItem="5Ni-DW-acW" secondAttribute="leading" constant="20" id="azT-VR-ipJ"/>
                                    <constraint firstAttribute="height" constant="128" id="dDD-5k-oXP"/>
                                    <constraint firstAttribute="bottom" secondItem="6gs-OS-OBa" secondAttribute="bottom" id="jjx-wV-0pX"/>
                                    <constraint firstAttribute="width" constant="240" id="ojW-Rq-z0h"/>
                                    <constraint firstItem="TvD-24-wfT" firstAttribute="top" secondItem="5Ni-DW-acW" secondAttribute="top" constant="20" id="vgM-Do-HvO"/>
                                    <constraint firstItem="6gs-OS-OBa" firstAttribute="centerX" secondItem="5Ni-DW-acW" secondAttribute="centerX" id="wjV-CZ-xYY"/>
                                </constraints>
                            </view>
                        </subviews>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="5Ni-DW-acW" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="546-OU-Up3"/>
                            <constraint firstItem="5Ni-DW-acW" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="16" id="Okf-rU-y08"/>
                        </constraints>
                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
                    </view>
                    <connections>
                        <outlet property="simpleView" destination="5Ni-DW-acW" id="a56-pn-GXE"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="32.799999999999997" y="70.614692653673174"/>
        </scene>
    </scenes>
</document>

результаты

enter image description here enter image description here

Я использую следующий метод, чтобы удалить все ограничения с целью:

.H-файл:

+ (void)RemoveContraintsFromView:(UIView*)view 
    removeParentConstraints:(bool)parent 
    removeChildConstraints:(bool)child;

.файл м:

+ (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]];
    }
}

Вы можете читать здесь как это работает за капот.

Если вам нужен более детальный контроль, я бы сильно советую переход на кладка, мощный класс framework, который можно использовать всякий раз, когда вам нужно правильно обрабатывать ограничения программно.

на основе предыдущих ответов (swift 4)

вы можете использовать immediateConstraints, когда вы не хотите сканировать целые иерархии.

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{
        .firstItem as? UIView === self || .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 .firstItem as? UIView === self || .secondItem as? UIView === self
        }
        view = view?.superview
    }
    return 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.

быстрое решение:

extension UIView {
  func removeAllConstraints() {
    var view: UIView? = self
    while let currentView = view {
      currentView.removeConstraints(currentView.constraints.filter {
        return .firstItem as? UIView == self || .secondItem as? UIView == self
      })
      view = view?.superview
    }
  }
}

важно пройти через все родители, так как ограничения между двумя элементами удерживаются общими предками, поэтому просто очистите супервизор, как описано в ответ не достаточно хорошо, и вы можете в конечном итоге иметь плохой сюрприз позже.

(по состоянию на 31 июля 2017 года)

SWIFT 3

self.yourCustomView.removeFromSuperview()
self.yourCustomViewParentView.addSubview(self.yourCustomView)

С

[self.yourCustomView removeFromSuperview];
[self.yourCustomViewParentView addSubview:self.yourCustomView];

Это самый простой способ быстро удалить все ограничения, которые существуют в UIView. Просто не забудьте добавить UIView обратно с его новыми ограничениями или новым фреймом после этого=)

Это способ отключить все ограничения из определенного представления

 NSLayoutConstraint.deactivate(myView.constraints)

Comments

    Ничего не найдено.