web-dev-qa-db-ja.com

UIScrollviewを使用したiOS自動レイアウト:スクロールビューのコンテンツビューがスクロールビューに表示されないのはなぜですか?

次のコード(viewDidLoadで呼び出されます)は、完全に赤い画面になります。私はそれが完全に緑色の画面になると期待しています。なぜ赤いの?そして、どうすればそれをすべてグリーンにすることができますか?

UIScrollView* scrollView = [UIScrollView new];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrollView];

UIView* contentView = [UIView new];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
contentView.backgroundColor = [UIColor greenColor];
[scrollView addSubview:contentView];

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView,contentView);

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics:0 views:viewDict]];

[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:0 views:viewDict]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:0 views:viewDict]];
43
dragosaur

スクロールビューの制約は、他のビューの制約とはわずかに異なります。 contentViewとそのsuperviewscrollView)の間の制約は、scrollViewではなく、contentSizeframeに対するものです。これはわかりにくいかもしれませんが、実際には非常に便利です。つまり、contentSizeを調整する必要はなく、contentSizeはコンテンツに合わせて自動的に調整されます。この動作は Technical Note TN2154 で説明されています。

画面などにcontentViewサイズを定義する場合は、たとえばcontentViewとメインビューの間に制約を追加する必要があります。確かに、これはコンテンツをスクロールビューに入れることとは相反するものなので、おそらくお勧めしませんが、それは可能です。


この概念を説明するために、contentViewのサイズはboundsscrollViewではなくそのコンテンツによって決定されるため、contentViewにラベルを追加します。

UIScrollView* scrollView = [UIScrollView new];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrollView];

UIView* contentView = [UIView new];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
contentView.backgroundColor = [UIColor greenColor];
[scrollView addSubview:contentView];

UILabel *randomLabel = [[UILabel alloc] init];
randomLabel.text = @"this is a test";
randomLabel.translatesAutoresizingMaskIntoConstraints = NO;
randomLabel.backgroundColor = [UIColor clearColor];
[contentView addSubview:randomLabel];

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView, contentView, randomLabel);

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics:0 views:viewDict]];

[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:0 views:viewDict]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:0 views:viewDict]];

[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[randomLabel]-|" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[randomLabel]-|" options:0 metrics:0 views:viewDict]];

これで、contentView(したがって、contentSizescrollView)が調整され、ラベルが標準のマージンに収まることがわかります。また、ラベルの幅/高さを指定しなかったため、ラベルに挿入したテキストに基づいて調整されます。


contentViewもメインビューの幅に合わせて調整したい場合は、viewDictのように再定義し、これらの追加の制約を追加できます(他のすべてに加えて)。

UIView *mainView = self.view;

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView, contentView, randomLabel, mainView);

[mainView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[contentView(==mainView)]" options:0 metrics:0 views:viewDict]];

既知の問題(バグ?) scrollviewsのマルチラインラベルには、テキストの量に応じてサイズを変更したい場合、次のようないくつかの巧妙な作業が必要です。

dispatch_async(dispatch_get_main_queue(), ^{
    randomLabel.preferredMaxLayoutWidth = self.view.bounds.size.width;
});
71
Rob

Robの答えを最初は問題ないように試してみました。 BUT!有効にした場合ズーム、この自動レイアウトコードが邪魔になります。 ズーム時にcontentViewのサイズを変更し続けます。つまり、ズームイン(zoomScale> 1)すると、画面の外側の部分をスクロールできなくなります。

Autolayoutと何日も戦いましたが、残念ながら解決策が見つかりませんでした。最後に、それは重要ではありません(ワット?OK、ごめんなさい)。最後に、contentViewのAutolayoutを削除し(固定サイズのcontentViewを使用)、次にlayoutSubviewsで調整しますself.scrollView.contentInset。 (Xcode5、iOS 7を使用しています)

これはこの質問に対する直接的な解決策ではないことを知っています。しかし、簡単な回避策を指摘したいだけです。わずか2行のコードで、スクロールビューで固定サイズのcontentViewを中央に配置するのに最適です。それが誰かを助けることを願っています;)

- (void)layoutSubviews {
    [super layoutSubviews];

    // move contentView to center of scrollView by changing contentInset,
    // because I CANNOT set this with AUTOLAYOUT!!!
    CGFloat topInset = (self.frame.size.height - self.contentView.frame.size.height)/2;
    self.scrollView.contentInset = UIEdgeInsetsMake(topInset, 0, 0, 0);
}
1
Hlung