UIScrollView.pagingEnabled = YES とするとページスクロールが可能になる。この場合、写真同士がぴったりとくっついたようになる。
しかし標準の写真アプリだとフリックした時、写真と写真の間に黒い空白(40ピクセル)が入る。一工夫しているようだ。
写真アプリの方が見やすい。これを真似してみよう。
スペースを入れる
通常の UIScrollView ではこんな感じで内部のビューがぴったりと配置されている。
そこで SPACE_WIDTH(40ピクセル)分だけ間を開けてみた(実際にはビューの両側に SPACE_WIDTH/2.0分開けてある)。
1ページ毎に異なった色で塗りつぶしてスクロールが確認できるサンプルアプリを作ってみた。
初期状態。
フリックすると隙間(灰色)が見える。
ページスクロールが完了したところ。SPACE_WIDTHの分だけずれてる。
スクロールを繰り返すとズレ幅が大きくなっていく。
多分こんなふうになっている。
フリックすると frameの大きさだけスクロールされている。
だからズレる。
うーむ。
UIScrollViewの幅を広げる
リファレンス
UIScrollView - pagingEnabledによればページスクロールは UIScrollViewの bounds が単位になると書いてある。
If the value of this property is YES, the scroll view stops on multiples of the view bounds when the user scrolls. The default value is NO.
ということは bounds の幅を SPACE_WIDTH分だけ広げてやればいいのでは?
そこでこんな風にしてみた。
scrollView.frame を (x=0, width=320) から (x=-SPACE_WIDTH/2, wdith=320+SPACE_WIDTH) とした。つまり可視範囲をはみ出して設定した。これによって bounds の幅が 320+SPACE_WIDTHとなり、ページスクロール時にスペースを考慮してスクロールが起こるようになるはず。
実装
実装してみよう。まずカスタムビューを定義しておく。
@interface CustomView : UIView {
UIColor* color;
}
@end
初期化時にランダムもどきな色を決めてその色で塗りつぶす。
@implementation CustomView
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Initialization code
CGFloat red = (rand()%255) / 255.0;
CGFloat green = (rand()%255) / 255.0;
CGFloat blue = (rand()%255) / 255.0;
color = [UIColor colorWithRed:red
green:green
blue:blue
alpha:1.0];
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGRect rect1 = CGRectMake(0, 0, 320, 460);
CGContextRef context = UIGraphicsGetCurrentContext();
[color set];
CGContextFillRect(context, rect1);
[[UIColor whiteColor] set];
CGContextSetLineWidth(context, 5);
CGContextStrokeRect(context, rect1);
}
次にスクロール処理のメイン。
@interface ScrollViewPagingViewController : UIViewController {
UIScrollView* scrollView;
}
@property (nonatomic, retain) IBOutlet UIScrollView* scrollView;
@end
5つの CustomViewを UIScrollView内に配置してページスクロールさせる。
#define SPACE_WIDTH 40
#define NUMBER_OF_VIEWS 5
- (void)viewDidLoad {
[super viewDidLoad];
CGRect scrollViewFrame = self.scrollView.frame;
scrollViewFrame.origin.x -= SPACE_WIDTH/2;
scrollViewFrame.size.width += SPACE_WIDTH;
self.scrollView.frame = scrollViewFrame;
self.scrollView.contentSize = CGSizeMake((320+SPACE_WIDTH)*NUMBER_OF_VIEWS, 460);
self.scrollView.pagingEnabled = YES;
CGFloat x = 0;
for (int i=0; i < NUMBER_OF_VIEWS; i++) {
// left space
x += SPACE_WIDTH/2.0;
// content
CGRect rect = CGRectMake(x, 0, 320, 460);
CustomView* view = [[[CustomView alloc] initWithFrame:rect] autorelease];
[self.scrollView addSubview:view];
x += rect.size.width;
// right space
x += SPACE_WIDTH/2.0;
}
}
ポイントは、UIScrollViewの frameを可視範囲をはみ出すように設定していることと、CustomViewの両側に SPACE_WIDTH/2.0 分のスペースをいれていること。
さて実行してみよう。まず初期画面。
フリックすると灰色の空白が確認できる。
ページスクロール終了。ぴったりと意図した境界で止まる。
スクロールを繰り返してもズレは起きない。
ソースコード
GitHubからどうぞ。
ScrollViewPaging at 2010-09-21 from xcatsan's iOS-Sample-Code - GitHub
- - - -
わかってみたら簡単だったが、最初は scrollViewDidEndDecelerating: を使って強制的に位置を補正したりなんかしていた(これだと計算が面倒)。