26

UIScrollView の使用方法を実験しています。苦労の末、やっとコツをつかめました。しかし今、私は別の障害にぶつかったようです。

この単純なアプリでは、スクロール ビューがあり、それを機能させるには、ここで説明されているようにビューの下部スペースを 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 のドキュメントを参照してください) ( "", "" )

誰かがこれを行う方法を教えてもらえますか? この問題についてよりよく理解できるように、ここにデモ プロジェクトも添付しました。

アップデート:

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

さらに説明することがあれば。2 つのビュー コントローラーがあります。最初のものは aUITableViewControllerで、2 つ目は aUIViewControllerです。その中にUIScrollView. また、複数UIViewの があり、それらのビューの一部の高さが画面の通常の高さを超えています。

オプションのUITableViewControllerリストが表示されます。ユーザーの選択に基づいてUIView、ロットから特定の 1 つが にロードされUIViewControllerますUIScrollView

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

デモ プロジェクトをここにアップロードして、動作を確認できるようにしました。を参照して、テーブル ビュー リストから[電子メール]Email.xibを選択してください。

4

4 に答える 4

57

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

  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 つの高さ制約、必須の最小高さ制約、およびテキストの量に基づいて上で変更する中優先度制約があることに注意してください。要点は、プログラムで制約を変更する実際的な例を示していることです。スクロールビューの下部の制約をいじる必要はまったくありませんが、これは、制約を調整したい場合の実際の例を示しています。


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

次の 2 つのアプローチの方が簡単です。

  1. IBOutletたとえば、既存の下部制約の を作成しますscrollViewBottomConstraint。それからあなたはただすることができます

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

  3. 新しい制約を追加することに決めた場合は、プログラムで古い下部制約を削除するか、古い下部制約の優先度をできるだけ低く設定することができます。そうすれば、新しい制約 (優先度の高い) が優先されます。古い優先度の低い下部制約は適切に適用されません。

しかし、新しい制約を追加するだけでは絶対に望ましくありません。

于 2013-04-09T04:55:20.757 に答える
12

コンソール情報を見ると、同じタイプの制約を 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;
        }
    }

お役に立てれば

ロブの答えでさえうまくいきます!

于 2013-04-09T04:56:59.500 に答える
2

プログラムで制約を追加するには、 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);
}];

頑張ってください ;)

于 2016-08-18T17:02:14.697 に答える