337 lines
8.7 KiB
Objective-C
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
|