20
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

UIScrollViewでハシゴ状レイアウトするカテゴリ拡張

Posted at

iOSでUIViewをレイアウトする場合、UITableViewやUICollectionViewを使う事が多いと思います。ですがこれらはなんというか「お堅い」奴らで、縦横のサイズがばらばらなUIViewを並べるには、けっこうめんどくさいです。
たとえばUITableViewの場合、Delegateでcellの高さを返す実装をしたりしますが、「文字数が増えた時には折り返して行数を増やしたい=縦方向を可変にしたい」な場合は高さの計算もめんどくさいです。
なので、高さや幅が違うUIViewを、適当に、それっぽく並べてくれる仕組みをお手軽実装してみました。

UIScrollView+LadderLayout.m

UIScrollViewをカテゴリ拡張して、layoutAndAddSubviewなんて関数をつくりました。これは以下の事を行います。

  • UIViewの配列を受け取る
  • 配列の中のUIViewを、サイズは変えずにハシゴ状に整列させる
  • 右側に隙間があればそこに置き、隙間が無ければ次の段に置く
  • UIScrollViewにaddSubviewして、contentSizeを広げる
UIScrollView+LadderLayout.m
//
//  UIScrollView+LadderLayout.m
//  UIScrollView+LadderLayout
//

#import "UIScrollView+LadderLayout.h"

// 便利拡張。UIViewのframe.{origin|size}へのアクセスを簡略化
@interface UIView(LadderLayout)
@property(readonly)CGFloat x;
@property(readonly)CGFloat y;
@property(readonly)CGFloat width;
@property(readonly)CGFloat height;
@end

@implementation UIView(LadderLayout)
-(CGFloat)x
{
    return self.frame.origin.x;
}
-(CGFloat)y
{
    return self.frame.origin.y;
}
-(CGFloat)width
{
    return self.frame.size.width;
}
-(CGFloat)height
{
    return self.frame.size.height;
}
@end

//梯子レイアウト本体
@implementation UIScrollView (LadderLayout)
-(void)layoutAndAddSubview:(NSArray *)views
{
    // 配列に含まれるUIViewを整列
    for(int i=0; i<views.count;i++ ){
        NSRange range={0,i};
        NSArray *layoutedViews = [views subarrayWithRange:range];
        UIView *targetView = [views objectAtIndex:i];
        
        // レイアウト済みの最後のViewの右側に、入れ込む隙間があるかどうかチェック
        CGPoint nextStartPoint = CGPointMake(0,0);
        if( i>0 ){
            UIView *lastLayoutView = layoutedViews.lastObject;
            nextStartPoint = CGPointMake(lastLayoutView.x+lastLayoutView.width,
                                         lastLayoutView.y);
            if( nextStartPoint.x + targetView.width > self.width ){
                // はみ出してるので、次の段の左端に移動
                nextStartPoint = CGPointMake(0,0);
                for (UIView *v  in layoutedViews) {
                    nextStartPoint.y = MAX(nextStartPoint.y, v.y + v.height);
                }
            }
        }
        // 移動先の場所が決まったので移動。
        targetView.frame = CGRectMake(nextStartPoint.x,
                                      nextStartPoint.y,
                                      targetView.width,
                                      targetView.height);
    }
    // 整列したUIViewをaddSubviewしていく
    for(UIView *v in views){
        [self addSubview:v];
    }
    
    // ScrollViewのcontentSizeを変更
    CGFloat contentHeight=0;
    for (UIView *v  in views) {
        contentHeight = MAX(contentHeight,v.y+v.height);
    }
    self.contentSize = CGSizeMake(self.width, contentHeight);
}

@end

ViewController側のソースでは、以下のことをやります。

  • ランダムなサイズのUIViewを生成して、配列に格納
    • この段階のframe.origin は{0,0}でOK
  • layoutAndAddSubviewに突っ込む
ViewController.m
-(void)viewWillAppear:(BOOL)animated
{
    NSArray *colors = @[[UIColor redColor],
                        [UIColor orangeColor],
                        [UIColor blueColor],
                        [UIColor greenColor],
                        [UIColor grayColor],
                        [UIColor whiteColor],
                        [UIColor yellowColor],
                        [UIColor purpleColor] ];
    srand((unsigned int)time(NULL)); // rand()の初期化
    
    
    // ランダムサイズのUIViewを100個作って、
    // 配列に入れておく。
    NSMutableArray *views = [[NSMutableArray alloc]init];
    for(int i=0; i<100; i++ ){
        UIView *v = [[UIView alloc]init];
        v.backgroundColor = [colors objectAtIndex:i%colors.count];
        v.bounds = CGRectMake(0,0, 1+rand()%100,1+rand()%100);
        [views addObject:v];
    }
    
    // 配列をScrollViewに叩き込んで整列させる。
    UIScrollView *scrollView = (UIScrollView *)self.view;
    [scrollView layoutAndAddSubview:views];
    
    // Storyboardを使っている場合、AutoLayoutにチェックを入れると
    // スクロールしないので注意。
    // AutoLayout使うならこんな実装要らないだろうけど。
}

これを動かすと、↓のように、UIViewが frame.origin.y でそろった配置となります。
スクリーンショット_2013_08_08_23_20.png

使い道

今回はランダムなUIViewでしたけど、横幅=画面幅なUIView(縦幅はばらばら)を放り込めば、高さ可変のUIViewを縦に並べる事が出来るかなーと思います。
本気利用するには、もっと手を入れる必要があるかと思います。画面回転とか対応してないですし。
でもUITableViewを使って苦労するよりは、楽ができる、かも?しれません。

github

20
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?