いくつかの制約を介して画面に配置されるUIViewがあります。制約の一部はスーパービューによって所有され、他の制約は他の祖先によって所有されます(たとえば、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
、またはそれらの変形に対処しています。これらの方法は、これらのメソッドが制約所有のみを返し、制約ではなく影響を返すため、機能しません。
これらのソリューションの問題を明確にするために、次のビュー階層を検討してください。
次の制約を作成し、それらを常に最も近い共通の祖先にアタッチすることを想像してください。
ここで、Me
に影響するすべての制約を削除することを想像してください。適切な解決策は、[C0,C1,C2,C3,C4]
を削除し、それ以外は削除しないでください。
self.constraints
(selfはMe)を使用すると、[C0,C1,C7]
を取得します。これらはMeが所有する唯一の制約です。明らかに[C2,C3,C4]
がないため、これを削除するだけでは十分ではありません。さらに、C7
を不必要に削除しています。
self.superview.constraints
(selfはMe)を使用すると、[C2,C5]
を取得します。これらは、Fatherが所有する制約であるためです。 C5
はMe
とは完全に無関係なので、これらすべてを削除することはできません。
grandfather.constraints
を使用すると、[C3,C4,C6]
を取得します。繰り返しますが、C6
はそのままにしておく必要があるため、これらすべてを削除することはできません。
ブルートフォースアプローチは、ビューの祖先(それ自体を含む)をループし、firstItem
またはsecondItem
がビュー自体であるかどうかを確認することです。その場合、その制約を削除します。これは、正しいソリューションにつながり、[C0,C1,C2,C3,C4]
、およびそれらの制約のみを返します。
ただし、祖先のリスト全体をループするよりもエレガントなソリューションがあることを望んでいます。
私がこれまでに見つけた唯一の解決策は、そのスーパービューからビューを削除することです。
[view removeFromSuperview]
これは、レイアウトに影響するすべての制約を削除し、スーパービューに追加して新しい制約を追加する準備ができているように見えます。ただし、サブビューも階層から誤って削除され、[C7]
が誤って削除されます。
このアプローチは私のために働いた:
@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
自動サイズ変更の制約が作成されるため、ビューの実行後もビューは元の場所に残ります。これを行わないと、通常、ビューが消えます。さらに、スーパービューから制約を削除するだけでなく、祖先ビューで制約に影響を与える可能性があるため、上に移動します。
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
}
}
これを行うことにより、ビュー内のすべての制約を削除できます。
[_cell.contentView removeConstraints:_cell.contentView.constraints];
編集:すべてのサブビューの制約を削除するには、Swiftで次の拡張子を使用します。
extension UIView {
func clearConstraints() {
for subview in self.subviews {
subview.clearConstraints()
}
self.removeConstraints(self.constraints)
}
}
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 Developer Documentationによると、それを達成する方法には2つの方法があります
NSLayoutConstraint.deactivateConstraints
これは、1回の呼び出しで一連の制約を簡単に無効にする便利な方法です。このメソッドの効果は、各制約のisActiveプロパティをfalseに設定することと同じです。通常、この方法を使用すると、各制約を個別に非アクティブ化するよりも効率的です。
// Declaration
class func deactivate(_ constraints: [NSLayoutConstraint])
// Usage
NSLayoutConstraint.deactivate(yourView.constraints)
UIView.removeConstraints
(> = iOS 8.0で非推奨)IOS 8.0以降向けに開発する場合、removeConstraints:メソッドを直接呼び出すのではなく、NSLayoutConstraintクラスのdeactivateConstraints:メソッドを使用します。 deactivateConstraints:メソッドは、正しいビューから制約を自動的に削除します。
// Declaration
func removeConstraints(_ constraints: [NSLayoutConstraint])`
// Usage
yourView.removeConstraints(yourView.constraints)
Storyboard
sまたはXIB
sを使用すると、シナリオで説明したように制約を構成するのが非常に面倒になる可能性があるため、削除する各制約に対してIBOutletを作成する必要があります。それでも、ほとんどの場合、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()
}
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()
次の方法を使用して、ビューからすべての制約を削除します。
.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 に切り替えることをお勧めします。これは、いつでも使用できる強力なフレームワーククラスです。プログラムで制約を適切に処理する必要があります。
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];
}
Swiftソリューション:
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
}
}
}
2つの要素間の制約は共通の祖先によって保持されているため、すべての親を確認することが重要です。したがって、 this answer で詳述されているようにスーパービューをクリアするだけでは十分ではありません。後で悪い驚き。
階層全体をクロールしない場合は、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{
$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
}
}
(2017年7月31日現在)
Swift
self.yourCustomView.removeFromSuperview()
self.yourCustomViewParentView.addSubview(self.yourCustomView)
目的C
[self.yourCustomView removeFromSuperview];
[self.yourCustomViewParentView addSubview:self.yourCustomView];
これは、UIViewに存在するすべての制約をすばやく削除する最も簡単な方法です。 UIViewを新しい制約または新しいフレームで追加し直してください=)
次のようなものを使用できます。
[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に関連するすべての制約を削除します。
次に、2番目の部分は、viewAの制約の配列を使用してviewAから制約を削除します。