레이블이 UIScrollView인 게시물을 표시합니다. 모든 게시물 표시
레이블이 UIScrollView인 게시물을 표시합니다. 모든 게시물 표시

2014/05/23

[iOS] Poster Scroll View를 만들어 봅니다.

영화관 앱에서 포스터를 옆으로 넘길 때, 현재 페이지의 이미지와, 다음페이지의 이미지가 약간 겹처서 넘어갑니다.
Yahoo 날씨 앱에서도 각 페이지를 양 옆으로 넘길 때, 이미지가 겹쳐서 넘어가게 됩니다.

이것을 스크롤 뷰를 이용해서 구현해 보도록 하겠습니다.

1. 전체 구현방법

 스크롤 뷰에 현재 frame크기의 각 페이지로 커스텀 뷰(DBUMoviePosterView)를 추가하고, 화면이 이동할 때, 커스텀 뷰에서 이미지의 위치를 조정해주게 된다.
 이 커스텀 뷰에서 추가된 내부 뷰가 이동할 때, 배경이 바깥 부분으로 나가지 않도록 레이어의 masksToBound도 설정해 준다.
 오른쪽에서 왼쪽으로 다음페이지의 이미지가 나올 때는, 현재 위치를 계산해서, 중간에서 오른쪽으로 이동하도록 하고,
 왼쪽에서 오른쪽으로 이전 페이지의 이미지가 나올 때, 중간에서 왼쪽으로 이동하도록 한다.

2. 소스 구현

 - DBUScrollView
source code
@interface DBUScollView()
@property (nonatomic, strong) NSMutableArray *posters;
@end

@implementation DBUScollView
- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        self.delegate = self;
    }
    return self;
}
- (NSMutableArray *)posters
{
    if (_posters == nil) {
        _posters = [NSMutableArray array];
    }
    return _posters;
}
- (void)addPoster:(UIImage *)image
{
    CGSize size = self.frame.size;
    //포스터 전체 개수
    NSUInteger posterTotalCount = [self.posters count];
    //content 영역 설정
    self.contentSize = CGSizeMake(size.width * (posterTotalCount+1), size.height);
    // 포스터 뷰 생성 및 추가
    DBUMoviePosterView *posterView = [[DBUMoviePosterView alloc] initWithFrame:CGRectMake(size.width*(posterTotalCount), 0, size.width, size.height)];
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:posterView.bounds];
    imageView.image = image;
    [posterView addView:imageView];
    [self addSubview:posterView];

    //위치 이동을 위해서, Array로 보관
    [self.posters addObject:posterView];
}

#pragma mark - UIScrollViewDelegate
- (void)posterImagePosition:(NSInteger)posterIndex point:(CGPoint)point
{
    if (posterIndex < 0 || posterIndex >= self.posters.count) {
        return; //페이지 수를 벗어난 것이면, 무시한다.
    }
    DBUMoviePosterView *posterView = (DBUMoviePosterView *)self.posters[posterIndex];
    if (posterView == nil) return;
    [posterView moveViewPosition:point]; //각 포스터 뷰에서 내부 뷰를 이동시킨다.
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    //현재 페이지를 계산해서, 연재와 다음 것만 움직이도록 한다.
    NSUInteger currentPage = scrollView.contentOffset.x / scrollView.frame.size.width;
    //계산은 각 포스터 뷰에서 계산한다.
    [self posterImagePosition:currentPage point:scrollView.contentOffset];
    [self posterImagePosition:currentPage+1 point:scrollView.contentOffset];
}
@end



- DBUMoviePosterView
source code
#import "DBUMoviePosterView.h"
@interface DBUMoviePosterView()
{
    UIView *_view;
}
@end

@implementation DBUMoviePosterView
- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        self.layer.masksToBounds = YES;//
    }
    return self;
}
- (void) addView:(UIView *)view
{
    if (_view != nil) {
        [_view removeFromSuperview];
        _view = nil;
    }
    _view = view;
    [self addSubview:view];
}
- (void) moveViewPosition:(CGPoint)point
{
        CGFloat width = self.frame.size.width;
    CGFloat height = self.frame.size.height;
    CGFloat x = point.x - self.frame.origin.x;
    
    if (x > -width && x < width) {
        //현재 위치와 비교한 값의 절반을 x좌표로 한다.
        _view.frame = CGRectMake(x/2, point.y, width, height);
    }
}
- (void) moveViewPositionToInitial
{
    //(0,0)으로 원위치 시킴.
    [self moveViewPosition:CGPointMake(0, 0)];
}


3. 결과물




2014/05/12

[iOS] UIScrollView에서 현재 중심이 되는 페이지 얻어오기.

ScrollView에서 페이지 단위로 뷰를 로딩해서 화면에 보여줄 경우, 현재 페이지의 앞뒤 페이지만 로딩하도록 해서 지금 필요한 페이지만 메모리를 사용할 수 있게 만들어야 됩니다.
100페이지의 ScrollView라 할지라도, 현재는 3페이지만 로딩하도록 하는 것입니다.
이 경우, 현재 페이지가 몇 페이지인지 알아야 합니다.

페이지를 가로로 이동하는 경우에는,  페이지의 너비와 현재 페이지의 x좌표를 이용하여 알아낼 수 있습니다.

사용자가 페이지를 터치해서 이동할 경우, scrollView.contentOffset.x 값이 변하게 됩니다.
source code
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat pageWidth = self.scrollView.frame.size.width;
    NSInteger page = (NSInteger)floor((self.scrollView.contentOffset.x * 2.0f + pageWidth) / (pageWidth * 2.0f));
    NSLog(@"scrollView.contentOffset.x: %f, page:%d", scrollView.contentOffset.x, page);
    
}

페이지를 왼쪽으로 이동시키다가, 현재 화면의 반을 넘어가는 경우, 다음 페이지가 되고, 오른쪽으로 이동시키다가 현재 페이지의 반 이하가 남게 되면, 이전 페이지로 인식해야 합니다.
현재 페이지 = (x좌표 / 페이지너비 + 0.5)값의 반내림
현재 페이지 = ((x좌표 + 1/2*페이지너비) / 페이지너비) 값의 반내림
현재 페이지 = ((x좌표 + 페이지너비) / (2*페이지너비)) 값의 반내림

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat pageWidth = self.scrollView.frame.size.width;
    NSInteger page = (NSInteger)floor((self.scrollView.contentOffset.x * 2.0f + pageWidth) / (pageWidth * 2.0f));
    NSLog(@"scrollView.contentOffset.x: %f, page:%d", scrollView.contentOffset.x, page);
    
}

scrollView가 가로로 Scroll되었을 때, 현재 페이지를 알 수 있으므로, 페이지가 변경되면, 앞뒤 페이지를 로딩하여, 미리 준비하면 됩니다.


[참조] : http://www.raywenderlich.com/10518/how-to-use-uiscrollview-to-scroll-and-zoom-content