删除影响UIView的所有约束
我有一个UIView通过几个约束放置在屏幕上。 一些约束由superview拥有,其他约束由其他祖先拥有(例如,也许是UIViewController的视图属性)。
我想删除所有这些旧的约束,并使用新的约束将其放置在新的地方。
我怎样才能做到这一点,而不必为每个约束创build一个IBOutlet,并且必须记住哪个视图拥有所述约束?
详细说,天真的方法是为每个约束创build一堆IBOutlets,然后包含调用代码,如:
[viewA removeConstraint:self.myViewsLeftConstraint]; [viewB removeConstraint:self.myViewsTopConstraint]; [viewB removeConstraint:self.myViewsBottomConstraint]; [self.view removeConstraint:self.myViewsRightConstraint];
这个代码的问题是即使在最简单的情况下,我也需要创build2个IBOutlets。 对于复杂的布局,可以轻松达到4或8个IBOutlet。 此外,我需要确保我的呼叫,以消除约束正在呼吁正确的看法。 例如,假设myViewsLeftConstraint
属于viewA
。 如果我不小心调用了[self.view removeConstraint:self.myViewsLeftConstraint]
,什么都不会发生。
注意: constraintAffectingLayoutForAxis方法看起来很有前景,但是仅用于debugging目的。
更新:我收到的许多答案处理self.constraints
, self.superview.constraints
,或这些的一些变体。 这些解决scheme将不起作用,因为这些方法只返回视图所拥有的约束,而不是影响视图的约束。
为了澄清这些解决scheme的问题,请考虑这个视图层次结构:
- 祖父
- 父亲
- 我
- 儿子
- 女儿
- 哥哥
- 我
- 叔叔
- 父亲
现在想象一下,我们创build下面的约束条件,并且总是将它们附加到最近的共同祖先:
- C0:我:和儿子一样(拥有我)
- C1:Me:宽度= 100(由Me拥有)
- C2:我:和兄弟一样高(父亲)
- C3:我:和叔叔一样(爷爷拥有)
- C4:我:和爷爷一样(祖父所有)
- C5:兄弟:跟爸爸一样(父亲)
- C6:叔叔:和祖父(爷爷)一样,
- C7:儿子:和女儿一样(由我拥有)
现在想象一下,我们想删除所有影响Me
约束。 任何适当的解决scheme应该删除[C0,C1,C2,C3,C4]
而不是别的。
如果我使用self.constraints
(self是me),我会得到[C0,C1,C7]
,因为这些是我拥有的唯一约束。 显然这是不够的,因为它是缺less[C2,C3,C4]
。 此外,它不必要地删除C7
。
如果我使用self.superview.constraints
(self是me),我会得到[C2,C5]
,因为这些都是Father所拥有的约束。 由于C5
与Me
完全无关,显然我们不能删除所有这些。
如果我使用grandfather.constraints
,我会得到[C3,C4,C6]
。 再次,我们不能删除所有这些,因为C6
应保持完整。
暴力方法是遍历每个视图的祖先(包括它自己),并查看firstItem
或secondItem
是否是视图本身; 如果是的话,删除这个约束。 这将导致一个正确的解决scheme,返回[C0,C1,C2,C3,C4]
,只有这些约束。
但是,我希望有一个更优雅的解决scheme,而不是遍历整个祖先列表。
这种方法对我有用:
@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
执行完成后,视图仍然保留在原来的位置,因为它会创build自动调整限制。 当我不这样做时,视图通常会消失。 另外,它不仅仅从超视图中删除约束,而且一直遍历,因为在祖先视图中可能会有影响它的约束。
您可以通过执行以下操作删除视图中的所有约束:
[_cell.contentView removeConstraints:_cell.contentView.constraints];
编辑:要删除所有子视图的约束,在Swift中使用以下扩展:
extension UIView { func clearConstraints() { for subview in self.subviews { subview.clearConstraints() } self.removeConstraints(self.constraints) } }
我迄今发现的唯一解决scheme是从其超级视图中删除视图:
[view removeFromSuperview]
这看起来像删除了所有影响其布局的约束条件,并且已经准备好添加到超级视图中,并且附加了新的约束条件。 但是,它也会错误地从层次结构中删除任何子视图,并错误地清除[C7]
。
在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) } }
根据Apple开发者文档 ,有两种方法可以实现
1. NSLayoutConstraint.deactivateConstraints
这是一种方便的方法,它提供了一种简单的方法来通过一次调用来停用一组约束。 此方法的效果与将每个约束的isActive属性设置为false相同。 通常,使用此方法比单独禁用每个约束更有效。
迅速
// Declaration class func deactivate(_ constraints: [NSLayoutConstraint]) // Usage NSLayoutConstraint.deactivate(yourView.constraints)
Objective-C的
// 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)
Objective-C的
// Declaration - (void)removeConstraints:(NSArray<__kindof NSLayoutConstraint *> *)constraints; // Usage [yourView removeConstraints:yourView.constraints];
参考
- NSLayoutConstraint
- 的UIView
我使用以下方法从视图中删除所有约束:
.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]]; } }
你可以在这里看到它是如何工作在引擎盖后面。
如果你需要更细粒度的控制,我强烈build议切换到Masonry ,一个强大的框架类,你可以使用,只要你需要正确处理约束编程。
(截至2017年7月31日)
SWIFT 3
self.yourCustomView.removeFromSuperview() self.yourCustomViewParentView.addSubview(self.yourCustomView)
目标C
[self.yourCustomView removeFromSuperview]; [self.yourCustomViewParentView addSubview:self.yourCustomView];
这是快速删除UIView上存在的所有约束的最简单的方法。 只要确保添加新的约束或新的框架UIView之后=)
Swift解决scheme:
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 } } }
因为两个元素之间的约束是由共同的祖先所保留的,所以只要清除这个答案中详细描述的超级观点是不够好的,那么最终可能会在以后出现不好的惊喜。
细节
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()
完整的示例
视图控制器
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>
结果
你可以使用这样的东西:
[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的superview的所有约束,并删除了所有与viewA有关的约束。
然后,第二部分使用viewA的约束数组去除viewA中的约束。