// // 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() @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)dataSource { if(dataSource != _dataSource){ _dataSource = dataSource; _didLoad = NO; } } - (void)setDelegate:(id)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_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