web-dev-qa-db-ja.com

Objectivecにプログラムで制約を追加する方法

プログラムで制約を追加したいので、以下のコードを使用してTOPおよびLEFT制約を追加します。

NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:label1
                                                       attribute:NSLayoutAttributeTop
                                                       relatedBy:NSLayoutRelationEqual
                                                          toItem:self.view
                                                       attribute:NSLayoutAttributeTop
                                                      multiplier:1
                                                        constant:110];


NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:label1
                                                        attribute:NSLayoutAttributeLeading
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self.view
                                                        attribute:NSLayoutAttributeLeading
                                                       multiplier:1
                                                         constant:10];

lable1がビューコントローラに追加されました。この2つの制約を次のようなビューに追加すると

[self.view addConstraint:top];
[self.view addConstraint:left];

コンソルにエラーが発生し、制約はラベルに影響しません。

2016-02-09 19:36:59.824 testinMRC[99160:313382] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x7fa5c8738610 V:|-(110)-[UILabel:0x7fa5c8626940'Label']   (Names: '|':UIView:0x7fa5c86267b0 )>",
    "<NSIBPrototypingLayoutConstraint:0x7fa5c8628a70 'IB auto generated at build time for view with fixed frame' V:|-(88)-[UILabel:0x7fa5c8626940'Label']   (Names: '|':UIView:0x7fa5c86267b0 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fa5c8738610 V:|-(110)-[UILabel:0x7fa5c8626940'Label']   (Names: '|':UIView:0x7fa5c86267b0 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2016-02-09 19:36:59.889 testinMRC[99160:313382] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x7fa5c87334b0 H:|-(10)-[UILabel:0x7fa5c8626940'Label']   (Names: '|':UIView:0x7fa5c86267b0 )>",
    "<NSIBPrototypingLayoutConstraint:0x7fa5c86285c0 'IB auto generated at build time for view with fixed frame' H:|-(188)-[UILabel:0x7fa5c8626940'Label'](LTR)   (Names: '|':UIView:0x7fa5c86267b0 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fa5c87334b0 H:|-(10)-[UILabel:0x7fa5c8626940'Label']   (Names: '|':UIView:0x7fa5c86267b0 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

このエラーが発生する理由を誰かに教えてもらえますか?ソリューションをショートさせるのを手伝ってください

6
Crazy Developer

あなたの場合、エラーはあなたがあなたの見解に対して矛盾する制約を持っていることを明確に示しています。おそらく、InterfaceBuilderからの制約がすでにあるビューに制約を追加しようとしているためです。制約を明示的に設定しなかった場合でも、IBは、いくつかが欠落していることを検出すると、制約を提供します。

コメントで、すべてをプログラムで実行したいとおっしゃっています。その場合は、次のコードをご覧ください。

- (void)viewDidLoad {
    [super viewDidLoad];

    UIView *view = [UIView new];
    view.backgroundColor = [UIColor redColor];

    [view setTranslatesAutoresizingMaskIntoConstraints:NO];

    [self.view addSubview:view];

    NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:100];
    NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:100];

    NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:50];
    NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:50];

    [self.view addConstraints:@[left, top]];
    [view addConstraints:@[height, width]];

}

それはかなり自明です。ご覧のとおり、ビューに幅と高さの制約を追加する必要がありました。これは、左と上だけではビューの位置を完全に記述していないためです。

結果:

enter image description here

VisualFormatLanguageを試してみることをお勧めします。はるかに少ないコードで同じ結果を得ることができます。このコードは、まったく同じ結果になります。

NSArray *horizontal = [NSLayoutConstraint constraintsWithVisualFormat:@"|-100-[view(50)]" options:0 metrics:nil views:@{@"view" : view}];
NSArray *vertical = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-100-[view(50)]" options:0 metrics:nil views:@{@"view" : view}];

[self.view addConstraints:vertical];
[self.view addConstraints:horizontal];

これが役に立ったかどうか教えてください。乾杯。

10
NKorotkov

問題はあなたのコードではなく、IB auto generated at build time for view with fixed frameメッセージです。

自分で制約を追加しない場合、InterfaceBuilderは自動的に制約を生成します。あなたはどちらかによってそれを避けることができます

  • プロジェクトナビゲータでペン先またはストーリーボードを選択し、ファイルインスペクタに移動して、Use Auto Layoutのチェックを外します。

または

  • Interface Builderにいくつかの制約を追加し、それらをremove at build timeに設定します。
1
Jörn Buitink

レイアウトの制約が競合しているようです。ストーリーボードに制約が設定されている可能性が高く、プログラムで直接競合する制約を追加しようとしています。

アクションが発生した後、UIViewを邪魔にならない場所に移動しようとしているようです。つまり、ボタンを押して、幸せそうな顔の写真を画面の中央から左上隅に移動します。

コードに直接制約を追加および削除すると、コードの膨張、単体テスト、およびコンソールがいっぱいになる警告を伴う大きな頭痛の種になる可能性があります。

ストーリーボードでメインUIViewを作成し、ボタンを押した後にUIViewに必要なすべての制約を保持する非表示のプレースホルダーUIViewを用意することをお勧めします。次に、それらのUIViewのすべての制約をプログラムで切り替えることができます。したがって、変更するビューにはプレースホルダーの位置、幅、高さがあり、プレースホルダーには変更するビューの位置と高さがあります。

このようにすると、プログラムでレイアウト制約を変更しているときにこのエラーが発生することはありません。エラーはストーリーボードによってキャッチされるためです。

1行または2行のコードでこれを行うオープンソースがいくつかあります。 (ドキュメントにもいくつかのYouTubeビデオチュートリアルがあります) https://cocoapods.org/pods/SBP

Cocoapodをインストールすると、コードは次のようになります。

これは、変更するUIViewとプレースホルダーUIViewを含むViewController内のコードになります

//UIView* viewBeforeChange
//UIView* hiddenViewAfterChange
[self switchViewConst:viewBeforeChange secondView:hiddenViewAfterChange];
0
Arjay Waran