337 lines
8.7 KiB
Objective-C

//
// CLPickerDrum.m
//
// Created by sho yakushiji on 2013/12/15.
// Copyright (c) 2013年 CALACULU. All rights reserved.
//
#import "CLPickerDrum.h"
#import "UIView+Frame.h"
#define MAX_SCROLLABLE_VIEWS 10000
@interface CLPickerDrum()
<UIScrollViewDelegate>
@property (nonatomic, assign) NSInteger centerContentIndex;
@end
@implementation CLPickerDrum
{
CGFloat _VIEW_WIDTH;
CGFloat _VIEW_HEIGHT;
NSInteger _VIEW_NUM;
NSInteger _ROW_NUM;
NSInteger _topContentIndex;
NSInteger _topViewIndex;
NSInteger _bottomViewIndex;
NSInteger _centerViewIndex;
UIImageView *_imageView;
UIScrollView *_scrollView;
BOOL _didLoad;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self customInit];
}
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self customInit];
}
- (void)customInit
{
_didLoad = NO;
_centerContentIndex = 0;
self.foregroundColor = [UIColor colorWithWhite:1 alpha:0.5];
_imageView = [[UIImageView alloc] initWithFrame:self.bounds];
_imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self addSubview:_imageView];
_scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.pagingEnabled = NO;
_scrollView.delegate = self;
[self insertSubview:_scrollView atIndex:0];
}
- (void)setDataSource:(id<CLPickerDrumDataSource>)dataSource
{
if(dataSource != _dataSource){
_dataSource = dataSource;
_didLoad = NO;
}
}
- (void)setDelegate:(id<CLPickerDrumDelegate>)delegate
{
if(delegate != _delegate){
_delegate = delegate;
_didLoad = NO;
}
}
#pragma mark- Build foreground image
- (UIImage*)foregroundImage
{
UIGraphicsBeginImageContextWithOptions(self.frame.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, self.foregroundColor.CGColor);
CGContextFillRect(context, self.bounds);
CGRect rct = CGRectMake(0, (self.height - _VIEW_HEIGHT)/2, self.width, _VIEW_HEIGHT);
CGContextClearRect(context, rct);
UIImage *tmp = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tmp;
}
#pragma mark- Instance method
- (void)reload
{
_didLoad = NO;
[self layoutSubviews];
}
- (void)selectRow:(NSInteger)row animated:(BOOL)animated
{
_centerContentIndex = row;
if(_didLoad){
_didLoad = NO;
[self refreshViews];
}
else{
[self layoutSubviews];
}
}
- (NSInteger)selectedRow
{
return self.centerContentIndex;
}
#pragma mark- Info from delegate
- (CGFloat)rowHeight
{
if([self.delegate respondsToSelector:@selector(rowHeightInPickerDrum:)]){
return [self.delegate rowHeightInPickerDrum:self];
}
return ceil(self.height/3.0);
}
- (NSInteger)rowNumberFromIndex:(NSInteger)index
{
NSInteger N = _ROW_NUM;
if(N!=0){ index = (index+N)%N; }
return index;
}
- (UIView*)viewForIndex:(NSInteger)index reusingView:(UIView*)view
{
NSInteger row = [self rowNumberFromIndex:index];
if(row >=0 && row<_ROW_NUM && [self.delegate respondsToSelector:@selector(pickerDrum:viewForRow:reusingView:)]){
return [self.delegate pickerDrum:self viewForRow:row reusingView:view];
}
return nil;
}
- (NSString*)titleForIndex:(NSInteger)index
{
NSInteger row = [self rowNumberFromIndex:index];
if(row >=0 && row<_ROW_NUM && [self.delegate respondsToSelector:@selector(pickerDrum:titleForRow:)]){
return [self.delegate pickerDrum:self titleForRow:row];
}
return @"";
}
#pragma mark- View layout
- (void)layoutSubviews
{
_scrollView.bounds = self.bounds;
_VIEW_NUM = 0;
_VIEW_WIDTH = self.width;
_VIEW_HEIGHT = self.rowHeight;
_ROW_NUM = [self.dataSource numberOfRowsInPickerDrum:self];
_imageView.image = [self foregroundImage];
[self refreshViews];
}
- (void)refreshViews
{
for(UIView *view in _scrollView.subviews){ [view removeFromSuperview]; }
self.centerContentIndex = _centerContentIndex;
NSInteger marginNum = ceil((self.height-_VIEW_HEIGHT)/(2*_VIEW_HEIGHT));
_VIEW_NUM = 2*marginNum + 3;
NSInteger centerIndex = _VIEW_NUM/2;
_scrollView.contentOffset = CGPointMake(0, _VIEW_HEIGHT*MAX_SCROLLABLE_VIEWS/2);
_scrollView.contentSize = CGSizeMake(0, _VIEW_HEIGHT*MAX_SCROLLABLE_VIEWS);
CGRect viewFrame = CGRectMake(0, _scrollView.contentOffset.y+(_scrollView.height-_VIEW_HEIGHT)/2-centerIndex*_VIEW_HEIGHT, _VIEW_WIDTH, _VIEW_HEIGHT);
for(NSInteger i=0; i<_VIEW_NUM; ++i){
UIView *view = [self viewForIndex:i-centerIndex+self.centerContentIndex reusingView:nil];
if(view==nil){
UILabel *label = [UILabel new];
label.textAlignment = NSTextAlignmentCenter;
label.text = [self titleForIndex:i-centerIndex+self.centerContentIndex];
view = label;
}
view.frame = viewFrame;
[_scrollView addSubview:view];
viewFrame.origin.y += _VIEW_HEIGHT;
}
_topContentIndex = MAX_SCROLLABLE_VIEWS/2;
_topViewIndex = 0;
_bottomViewIndex = _VIEW_NUM-1;
_didLoad = YES;
}
#pragma mark- Scrolling
- (void)setCenterContentIndex:(NSInteger)centerContentIndex
{
if(_ROW_NUM>0){
centerContentIndex = (centerContentIndex + _ROW_NUM)%_ROW_NUM;
}
else{
centerContentIndex = 0;
}
if(centerContentIndex != _centerContentIndex){
_centerContentIndex = centerContentIndex;
if([self.delegate respondsToSelector:@selector(pickerDrum:didSelectRow:)]){
[self.delegate pickerDrum:self didSelectRow:_centerContentIndex];
}
}
}
- (NSInteger)calcViewIndex:(NSInteger)index incremental:(NSInteger)incremental
{
return (index + incremental + _VIEW_NUM) % _VIEW_NUM;
}
- (void)scrollWithDirection:(BOOL)upperDirection
{
NSInteger incremental = 0;
NSInteger viewIndex = 0;
NSInteger contentIndex = 0;
if(upperDirection){
incremental = -1;
viewIndex = _bottomViewIndex;
}
else{
incremental = 1;
viewIndex = _topViewIndex;
}
if(viewIndex<_scrollView.subviews.count){
_topContentIndex = _topContentIndex + incremental;
self.centerContentIndex = self.centerContentIndex + incremental;
if(upperDirection){
contentIndex = self.centerContentIndex - _VIEW_NUM/2;
}
else{
contentIndex = self.centerContentIndex - _VIEW_NUM/2 + _VIEW_NUM - 1;
}
UIView *reuse = [_scrollView.subviews objectAtIndex:viewIndex];
UIView *view = [self viewForIndex:contentIndex reusingView:reuse];
if(view==nil){
if([reuse isKindOfClass:[UILabel class]]){
UILabel *label = (UILabel*)reuse;
label.text = [self titleForIndex:contentIndex];
}
}
else if(view!=reuse){
view.frame = reuse.frame;
[reuse removeFromSuperview];
[_scrollView addSubview:view];
reuse = view;
}
reuse.top = reuse.top + _VIEW_HEIGHT * _VIEW_NUM * incremental;
_topViewIndex = [self calcViewIndex:_topViewIndex incremental:incremental];
_bottomViewIndex = [self calcViewIndex:_bottomViewIndex incremental:incremental];
}
}
#pragma UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)sender
{
CGFloat position = sender.contentOffset.y / _VIEW_HEIGHT;
CGFloat delta = position - (CGFloat)_topContentIndex;
NSInteger count = (NSInteger)MAX(fabs(delta-0.5), fabs(delta+0.5));
for(NSInteger i=0; i<count; ++i){
[self scrollWithDirection:(delta<0)];
}
}
- (void)adjustContentOffset
{
[UIView animateWithDuration:0.25
delay:0
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut
animations:^{
self->_scrollView.contentOffset = CGPointMake(0, self->_VIEW_HEIGHT*self->_topContentIndex);
}
completion:^(BOOL finished) { }
];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender
{
[self adjustContentOffset];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)sender willDecelerate:(BOOL)decelerate
{
[self adjustContentOffset];
}
@end