前回のつづき

今回はCustom ViewをScrollViewで動作させる方法を説明します。

Custom Viewが自身のサイズを返すようにする

Auto Layout環境では、UIScrollView#contentSizeは、スクロールするView(UIScrollViewのSubview)のサイズに合わせて自動設定されます。誤解しやすいのは、スクロールするViewのframeを設定すればうまくいくと思うことです。frameの値はcontentSizeには影響しません。

UIView#intrinsicContentSizeというメソッドがあります。このメソッドが返すサイズを元にAuto Layoutは適切なサイズで配置を行います。ScrollViewの場合は、このサイズを元にcontentSizeを設定します。(前回のように上下左右の余白が0のConstraintも設定されている必要があります)

前回はUIImageViewの画像をスクロールする画面を説明しました。UIImageViewはimageプロパティが設定されると、intrinsicContentSizeはimage.sizeと同じ値を返すようになります。image.size => intrinsicContentSize => UIScrollView#contentSizeというふうにサイズが同期して、うまく画像がスクロールしたわけです。

つまり、Custom ViewでintrinsicContentSizeをオーバーライドして自身の適切なサイズを返せば、ただしくスクロールさせることができます。

サンプルコード

https://github.com/stack3/iOSAutoLayoutSamples

まずは、簡単なCustom Viewで試してみましょう。サンプルを起動しLarge View ScrollViewを選択すると以下のように赤いViewがスクロールする画面になります。

STLargeScrollContentViewクラス

スクロールする赤いViewはUIViewのSubclassです。

@interface STLargeScrollContentView : UIView

@end

初期化時にbackgroundColorに赤色を設定しています。

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self largeScrollContentViewCommonInit];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self largeScrollContentViewCommonInit];
    }
    return self;
}

- (void)largeScrollContentViewCommonInit
{
    self.backgroundColor = [UIColor redColor];
}

本サンプルではinitWithFrameしか使いません。一応、お約束としてinitWithCoderでも初期化するようにしています。

intrinsicContentSizeは1024×1024のサイズを返すようにします。

- (CGSize)intrinsicContentSize
{
    return CGSizeMake(1024, 1024);
}

STLargeViewScrollViewControllerクラス

画面表示を行うViewControllerです。STLargeViewScrollViewController.storyboardでレイアウトしていて、ScrollViewを全面表示されるようにしています。

viewDidLoadで、先ほどの赤いViewをUIScrollViewに追加します。Constraintの設定は前回説明したとおり上下左右の空白が0pxになるようにします。Visual Format Languageを使うかどうかも前回説明したとおりお好みで。

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // frameは仮。ただしCGRectZeroは指定しないほうが無難。
    // 場合によってログにAuto Layoutの警告が出力されることがある。
    // CustomViewがSubviewを持ちAuto Layoutでレイアウトされている時、
    // サイズを0にしてしまうとConstraintの整合性が取れなくなる。
    // ここではCGRectZeroでも警告は出ないが、一応気をつけておく。
    STLargeScrollContentView *contentView = 
      [[STLargeScrollContentView alloc] initWithFrame:self.view.bounds];
    contentView.translatesAutoresizingMaskIntoConstraints = NO;
    [_scrollView addSubview:contentView];
    
    [_scrollView addConstraint:
       [NSLayoutConstraint constraintWithItem:contentView
                                    attribute:NSLayoutAttributeLeading
                                    relatedBy:NSLayoutRelationEqual
                                       toItem:_scrollView
                                    attribute:NSLayoutAttributeLeading
                                   multiplier:1.0f
                                     constant:0]];
    [_scrollView addConstraint:
       [NSLayoutConstraint constraintWithItem:contentView
                                    attribute:NSLayoutAttributeTrailing
                                    relatedBy:NSLayoutRelationEqual
                                       toItem:_scrollView
                                    attribute:NSLayoutAttributeTrailing
                                   multiplier:1.0f
                                     constant:0]];
    [_scrollView addConstraint:
       [NSLayoutConstraint constraintWithItem:contentView
                                    attribute:NSLayoutAttributeTop
                                    relatedBy:NSLayoutRelationEqual
                                       toItem:_scrollView
                                    attribute:NSLayoutAttributeTop
                                   multiplier:1.0f
                                     constant:0]];
    [_scrollView addConstraint:
       [NSLayoutConstraint constraintWithItem:contentView
                                    attribute:NSLayoutAttributeBottom
                                    relatedBy:NSLayoutRelationEqual
                                       toItem:_scrollView
                                    attribute:NSLayoutAttributeBottom
                                   multiplier:1.0f
                                     constant:0]];
}

これでCustom Viewがスクロールするようになります。

その3へつづく