UIScrollViewとAutoLayout
UIScrollViewとAutoLayoutに関する話題です。
このコンビネーションですが, 今のところ完全な解決に至っていません。とりあえず所望の動作は得られましたがといったところです。
iOS6からAutoLayoutが導入されました
さてつくりたいUIというのが, UIScrollViewを配置して, その中にUIViewControllerで操作を書いたViewをいれるというものです
通常であれば, UIScrollViewをInterfaceBuilderで配置して, weakリファレンスをして, その中に, UIViewControllerのインスタンスのパラメータviewを取り出して, addSubViewとここまではO.K.
UIScrollViewで起こる問題
- ContentSizeを設定しても反映されない
- UIScrollViewのSubViewにframeを指定しても, 全部左端にいく
筆者がはまったのは, 2つめのやつです. AppleのDocumentには確かにそれらしきことが書いていましたが…
いずれも– (void) viewDidLayoutSubviewsもしくはそのあたりで,
自動的にパラメータを調整する(よけいなお世話だ)のが原因.
てっとり早い解決策としては, AutoLayoutをOffするという手もありますが, ほかのViewもあるので, そうはいかないでしょう.
実装する方法によってかなり違うよう
UIをつくるにはいくつかあるので, それによってデフォルトパラメータなどの問題もあるので結構やっかいです
- storyboardでUIViewControllerを作成
- xibでUIView
- コードでUIScrollViewをつくる
上二つは, InterfaceBuilderでパラメータを指定する
ここで気になるパラメータを…
InterfaceBuilderの Autoresize Subviews, Resize View From NIB,
コードだと, translatesAutoresizingMaskIntoConstraintsは鍵になりそうです。
ContentSize
こちらの方は解決策を書いてらっしゃる方も多いです。
viewDidLayoutSubviewsでコンテンツサイズを指定するといった方法でしょうか(stackoverflow)
UIScrollViewのSubViewにframeを指定しても, 全部左端にいく
Appleさんによると(こちら),
constraintをきちんと設定するとのこと(Appleのサンプルは1ページ分なのでいまいちなんだよ).
上の方に書いてある内容でなんとかなったのでそれを…
サンプル
省略しています, subViewControllersには, ViewControllerを直接叩き込んでいるNSMutableArrayです
self.scrollView = [[UIScrollView alloc] initWithFrame:frame];
self.scrollView.pagingEnabled = YES;
//self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView setScrollEnabled:YES];
[self.scrollView setShowsHorizontalScrollIndicator:YES];
[self.scrollView setShowsVerticalScrollIndicator:NO];
[self.scrollView setHidden:NO];
self.scrollView.delegate = self;
double width = self.scrollView.frame.size.width;
self.scrollView.contentSize = CGSizeMake(width * [self.subViewControllers count], self.scrollView.frame.size.height);
UIView *contentView = [[UIView alloc]
initWithFrame:CGRectMake(0,0,self.scrollView.contentSize.width,self.scrollView.contentSize.height)];
for ( int i=0; i < [self.subViewControllers count]; i++ ) {
UIViewController *vc = [self.subViewControllers objectAtIndex:i];
[contentView addSubview:vc.view];
vc.view.translatesAutoresizingMaskIntoConstraints = NO;
UIView *uiview = vc.view;
NSDictionary* viewDict = NSDictionaryOfVariableBindings(contentView, uiview);
NSString *hconstraints = [NSString stringWithFormat:@"H:|-%f-[uiview]-0-|", width * i];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:hconstraints options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[uiview]|" options:0 metrics:0 views:viewDict]];
}
[self.scrollView addSubview:contentView];
[self.view addSubview:self.scrollView];
[/cpp]
何をやっているかというと, UIScrollViewと, その中につくるUIViewContollerの間に, ContentViewと称する, UIViewを挟んでいます
contentSizeをこれで確実に確定させます
constraintは実際のviewの大きさがないとエラーになるので, 各pageのviewはこのcontentViewに押し込みます
各viewに対して, leftのconstraintをかけますこれにより, contentViewに正しい位置で配置できます
あとは, contentViewをScrollViewに押し込みます
ひとつポイントなのが, translatesAutoresizingMaskIntoConstraints, InterfaceBuilderでweakリファレンスしたときは, YESじゃないと
うまくいかなかった
このあたりはまだまだわからないことが多いです。