web-dev-qa-db-ja.com

プログラムで制約を設定する

UIScrollViewの使用方法を試しています。多くのトラブルの後、私はついにそれのこつを得ました。しかし今、私は別の障害にぶつかったようです。

このシンプルなアプリでは、スクロールビューがあり、それを機能させるために、 here で説明されているように、ビューの下部スペースをscrollview制約に0に設定する必要があります。いいよ私はIBを通じてそれをやっています。

今、私はその部分をプログラムで行う必要があるシナリオに出会いました。 viewDidLoadメソッドに以下のコードを追加しました。

NSLayoutConstraint *bottomSpaceConstraint = [NSLayoutConstraint constraintWithItem:self.view
                                                                             attribute:NSLayoutAttributeBottom
                                                                             relatedBy:NSLayoutRelationEqual
                                                                                toItem:self.scrollView
                                                                             attribute:NSLayoutAttributeBottom
                                                                            multiplier:1.0
                                                                              constant:0.0];
[self.view addConstraint:bottomSpaceConstraint];

しかし、うまくいかないようです。コンソールウィンドウに次のメッセージを出力します。何をすべきかわかりません。

制約を同時に満たすことができません。おそらく、次のリストにある制約の少なくとも1つは、望ましくないものです。これを試してください:(1)各制約を見て、予期しないものを見つけてください。 (2)不要な制約を追加したコードを見つけて修正します。 (注:理解できないNSAutoresizingMaskLayoutConstraintsが表示されている場合は、UIViewプロパティtranslatesAutoresizingMaskIntoConstraintsのドキュメントを参照してください)( ""、 "")

誰かがこれを行う方法を教えてもらえますか?また、問題に関するより良いアイデアを得ることができるように、デモプロジェクト here を添付しました。

UPDATE:

最初の回答に感謝します。回答に記載されている方法を使用して、機能させることができました。ただし、わずかに異なるシナリオではそうではありません。今、私はプログラムでViewControllerにビューをロードしようとしています。

私がさらに説明する場合があります。 2つのView Controllerがあります。最初はUITableViewControllerで、2番目はUIViewControllerです。その中にUIScrollViewがあります。また、複数のUIViewsがあり、それらのビューの一部の高さは画面の通常の高さを超えています。

UITableViewControllerはオプションのリストを表示します。ユーザーの選択に基づいて、ロットから特定のUIViewUIViewControllerとともにUIScrollViewにロードされます。

このシナリオでは、上記の方法は機能しません。スクロールは行われていません。ビューを個別にロードするため、何か別のことをする必要がありますか?

デモプロジェクト をここにアップロードしました 。実際の動作を確認できます。 Email.xibを選択し、テーブルビューリストからEmailを選択します。

26
Isuru

コードのレビューに基づいて、いくつかのコメント:

  1. 通常、ビューが表示されるときに制約を調整する必要はありません。これを実行していることに気付いた場合、それは多くの場合、ストーリーボードを正しく構成していないことを意味します(少なくとも、非効率的です)。本当に制約を設定/作成する必要があるのは、(a)プログラムでビューを追加することだけです(価値がある以上の作業をお勧めします)。または(b)制約の実行時調整を行う必要があります(以下のポイント3の3番目の項目を参照)。

  2. 私はそれを酷使するつもりはありませんが、あなたのプロジェクトにはたくさんの冗長なコードがありました。例えば。

    • スクロールビューのフレームを設定していましたが、それは制約によって管理されているため、何もしません(つまり、制約が適用されると、手動で設定されたframe設定は置き換えられます)。一般に、自動レイアウトでは、frameを直接変更しないでください。制約を編集します。しかし、とにかく制約を変更する必要はまったくないので、ポイントは重要ではありません。

    • スクロールビューのコンテンツサイズを設定していましたが、自動レイアウトでは、それも(サブビューの)制約によって管理されるため、不要でした。

    • スクロールビューに制約を設定していましたが(すでにゼロでした)、NIBからビューをスクロールビューに追加せず、その目的もすべて無効にしました。元の質問は、スクロールビューの下部の制約を変更する方法でした。しかし、そのための最下位の制約はすでにゼロであるため、再びゼロに設定する理由はありません。

  3. あなたのプロジェクトをより根本的に簡素化することをお勧めします。

    • ビューをNIBに保存することで、自分の人生をより困難にしている。ストーリーボードの世界にいる方がずっと簡単です。本当に必要な場合は、NIBをサポートできますが、なぜ自分の人生をそんなに難しくするのですか?

    • セルプロトタイプを使用して、テーブル内のセルの設計を容易にします。また、セグエを定義して、セルから次のシーンに移動することもできます。これにより、didSelectRowAtIndexPathまたはprepareForSegueコードを記述する必要がなくなります。明らかに、次のシーンに渡す必要があるものがある場合は、必ずprepareForSegueを使用しますが、これまでに提示したものではそれを必要としないため、例でコメントアウトしました。

    • プログラムで制約を変更する実用的な例を探していると仮定して、テキストビューのテキストに基づいて、テキストビューの高さをプログラムで変更するようにシーンを設定しました。いつものように、問題の制約を見つけるために制約を反復処理するのではなく、IBが作成した既存の制約を変更する場合、制約にIBOutletを設定して編集する方がはるかに効率的だと思います制約のconstantプロパティは直接なので、私がやったことです。そこで、View Controllerをテキストビューのデリゲートとして設定し、テキストビューの高さの制約を更新するtextViewDidChangeを作成しました。

      #pragma mark - UITextViewDelegate
      
      - (void)textViewDidChange:(UITextView *)textView
      {
          self.textViewHeightConstraint.constant = textView.contentSize.height;
          [self.scrollView layoutIfNeeded];
      }
      

      テキストビューには、必須の最小の高さ制約と、テキストの量に基づいて上記で変更する中優先度の制約という2つの高さ制約があります。要点は、制約をプログラムで変更する実際的な例を示していることです。 scrollviewの下部の制約をいじる必要はまったくありませんが、これは、制約を調整する場合の実際の例を示しています。


IBでスクロールビューを追加すると、必要なすべての制約が自動的に取得されます。おそらく、プログラムで制約を追加したくないでしょう(少なくとも、既存の下部の制約を削除せずに)。

2つのアプローチの方が簡単かもしれません。

  1. 既存の下部制約に対してIBOutletを作成します(たとえば、scrollViewBottomConstraint)。その後、あなたはちょうどすることができます

    self.scrollViewBottomConstraint.constant = 0.0;
    
  2. または、ボトム制約が0.0であるIBで最初にビューを作成してから、プログラムで何もする必要はありません。長いスクロールビューとそのサブビューをレイアウトする場合は、コントローラーを選択し、シミュレートされたメトリックを「推測」から「自由形式」に設定します。次に、ビューのサイズを変更し、スクロールビューの上部と下部の制約をゼロに設定し、スクロールビュー内に必要なものをすべてレイアウトし、実行時にビューが表示されると、ビューのサイズが適切に変更されます。 'ScrollViewの上部および下部の制約を0.0に定義した場合、適切にサイズ変更されます。 IBでは少し奇妙に見えますが、アプリを実行すると魅力のように機能します。

  3. 新しい制約を追加することに決めた場合、プログラムで古い下の制約を削除するか、古い下の制約の優先度を可能な限り低く設定すると、新しい制約(優先度の高い)が優先されます。また、古い優先度の低い最下位の制約は適切に適用されません。

ただし、新しい制約を追加するだけでは間違いありません。

56
Rob

View Controllerでレイアウトの制約を表すアウトレットを作成することができます。インターフェイスビルダーで必要な制約を選択するだけです(たとえば、配置するビューの測定ペインで[選択して編集]を使用)。次に、アウトレットペインに移動し、「新しい参照アウトレット」をコードファイル(.hまたは.m)にドラッグします。これにより、コントローラーからアクセスして動的に調整できるNSLayoutConstraintインスタンスに制約がバインドされます(一般的にはconstantプロパティを介して調整されます。 )。

enter image description here

(XCode 6では、編集するために制約をダブルクリックして選択できることに注意してください。)

enter image description here

ただし、インターフェイスビルダーでレイアウトを調整するときは、制約を削除してアウトレットに再バインドする必要があるため、注意してください。

21
devios1

コンソール情報を見ると、同じタイプの制約を2つ追加すると、あいまいさが生じているように感じます。

そのため、新しい制約を作成して追加する代わりに、既に制約配列にある以前の制約を更新してみてください。

for(NSLayoutConstraint *constraint in self.view.constraints)
    {
        if(constraint.firstAttribute == NSLayoutAttributeBottom && constraint.secondAttribute == NSLayoutAttributeBottom &&
           constraint.firstItem == self.view && constraint.secondItem == self.scrollView)
        {
            constraint.constant = 0.0;
        }
    }

お役に立てれば

ロブの答えでさえ機能します!

12
Kunal

https://github.com/SnapKit/Masonry を使用して、プログラムで制約を追加できます。

AutoLayout NSLayoutConstraintsのパワーであり、単純化され、チェーン可能で表現力豊かな構文です。 iOSおよびOSX自動レイアウトをサポートします。

UIView *superview = self.view;

UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[superview addConstraints:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
                             attribute:NSLayoutAttributeTop
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeTop
                            multiplier:1.0
                              constant:padding.top],

[NSLayoutConstraint constraintWithItem:view1
                             attribute:NSLayoutAttributeLeft
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeLeft
                            multiplier:1.0
                              constant:padding.left],

[NSLayoutConstraint constraintWithItem:view1
                             attribute:NSLayoutAttributeBottom
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeBottom
                            multiplier:1.0
                              constant:-padding.bottom],

[NSLayoutConstraint constraintWithItem:view1
                             attribute:NSLayoutAttributeRight
                             relatedBy:NSLayoutRelationEqual
                                toItem:superview
                             attribute:NSLayoutAttributeRight
                            multiplier:1
                              constant:-padding.right],]];

ほんの数行で

MASConstraintMakerを使用して作成された同じ制約

UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
    make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
    make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];

またはさらに短く

[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.edges.equalTo(superview).with.insets(padding);
}];

あなたのベストはソートです;)

2
ERbittuu