├── CarouselView ├── PLCarouselView.h ├── PLCarouselView.m └── PLCarouselView.xib ├── CarouselViewDemo ├── CarouselView │ ├── PLCarouselView.h │ ├── PLCarouselView.m │ └── PLCarouselView.xib ├── CarouselViewDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── aniloruc.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── aniloruc.xcuserdatad │ │ └── xcschemes │ │ ├── CarouselViewDemo.xcscheme │ │ └── xcschememanagement.plist └── CarouselViewDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── CarouselView │ ├── PLCarouselView.h │ ├── PLCarouselView.m │ └── PLCarouselView.xib │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── LICENSE.md └── README.md /CarouselView/PLCarouselView.h: -------------------------------------------------------------------------------- 1 | // 2 | // PLCarouselView.h 3 | // OmsaTech 4 | // 5 | // Created by Anil Oruc on 5/4/15. 6 | // Copyright (c) 2015 OmsaTech. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum 12 | { 13 | PLCarouselViewVisibleItemCurrent = 0, 14 | PLCarouselViewVisibleItemNext, 15 | PLCarouselViewVisibleItemPrevious 16 | 17 | } 18 | PLCarouselViewVisibleItem; 19 | 20 | typedef enum 21 | { 22 | PLCarouselViewDirectionStatic = 0, 23 | PLCarouselViewDirectionUp, 24 | PLCarouselViewDirectionDown, 25 | PLCarouselViewDirectionLeft, 26 | PLCarouselViewDirectionRight 27 | } 28 | PLCarouselViewDirection; 29 | 30 | @class PLCarouselView; 31 | 32 | @protocol PLCarouselViewDelegate 33 | 34 | @optional 35 | 36 | -(void)carouselView:(PLCarouselView*)carouselView didSelectItemAtIndex:(NSUInteger)index; 37 | 38 | -(void)carouselCurrentItemIndexDidChange:(PLCarouselView *)carouselView currentIndex:(NSUInteger)currentIndex previousIndex:(NSUInteger)previousIndex; 39 | 40 | -(void)carouselView:(PLCarouselView*)carouselView didScrollDiffrenceRate:(CGFloat)diffRate; 41 | 42 | -(void)carouselView:(PLCarouselView*)carouselView didMoveToView:(UIView*)view; 43 | 44 | -(void)carouselView:(PLCarouselView*)carouselView changedScrollDirection:(PLCarouselViewDirection)direction; 45 | 46 | @end 47 | 48 | @protocol PLCarouselViewDataSource 49 | 50 | @required 51 | 52 | -(UIView*)carouselView:(PLCarouselView *)carouselView viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view; 53 | 54 | -(NSUInteger)numberOfItemsInCarousel:(PLCarouselView *)carouselView; 55 | 56 | @end 57 | 58 | @interface PLCarouselView : UIView 59 | 60 | + (instancetype)init; 61 | 62 | + (instancetype)initWithFrame:(CGRect)frame; 63 | 64 | @property (nonatomic, weak) IBOutlet id dataSource; 65 | 66 | @property (nonatomic, weak) IBOutlet id delegate; 67 | 68 | @property (nonatomic, assign) BOOL vertical; 69 | 70 | @property (nonatomic, assign) BOOL scrollEnabled; 71 | 72 | @property (nonatomic, assign, readonly) NSUInteger currentPageIndex; 73 | 74 | - (void)scrollToItemAtIndex:(NSInteger)index animated:(BOOL)animated; 75 | 76 | - (void)reloadData; 77 | 78 | - (UIView*)visibleItemWithType:(PLCarouselViewVisibleItem)type; 79 | 80 | - (void)resetInset; 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /CarouselView/PLCarouselView.m: -------------------------------------------------------------------------------- 1 | // 2 | // PLCarouselView.m 3 | // platinum 4 | // 5 | // Created by Anil Oruc on 5/4/15. 6 | // Copyright (c) 2015 Turkcell. All rights reserved. 7 | // 8 | 9 | #import "PLCarouselView.h" 10 | 11 | #import 12 | #import 13 | 14 | @interface PLCarouselView () 15 | 16 | @property (weak, nonatomic) IBOutlet UIScrollView *scrollView; 17 | @property (nonatomic, assign) CGFloat lastContentOffset; 18 | @property (nonatomic, assign) PLCarouselViewDirection direction; 19 | 20 | @property (nonatomic,strong) UIView *viewPrevious; 21 | @property (nonatomic,strong) UIView *viewCurrent; 22 | @property (nonatomic,strong) UIView *viewNext; 23 | 24 | @property (nonatomic, assign) NSUInteger currentPageIndex; 25 | @property (nonatomic, assign) NSUInteger numberOfItems; 26 | 27 | @property (nonatomic, assign) BOOL isScrolling; 28 | 29 | @end 30 | 31 | @implementation PLCarouselView 32 | 33 | +(instancetype)init 34 | { 35 | PLCarouselView *view = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil].firstObject; 36 | if (self) { 37 | view.vertical = NO; 38 | [view loadView]; 39 | } 40 | return view; 41 | } 42 | 43 | + (instancetype)initWithFrame:(CGRect)frame 44 | { 45 | PLCarouselView *view = [self init]; 46 | if (view) 47 | { 48 | [view setFrame:frame]; 49 | 50 | } 51 | return view; 52 | } 53 | 54 | -(void)setFrame:(CGRect)frame 55 | { 56 | [super setFrame:frame]; 57 | 58 | [self reloadFrame]; 59 | } 60 | 61 | -(void)setScrollEnabled:(BOOL)scrollEnabled 62 | { 63 | _scrollEnabled = scrollEnabled; 64 | 65 | _scrollView.scrollEnabled = scrollEnabled; 66 | } 67 | 68 | - (void)resetInset 69 | { 70 | _scrollView.contentInset = UIEdgeInsetsZero; 71 | } 72 | 73 | -(void)cloneBaseView:(UIView*)viewBase view:(UIView*)cloneView 74 | { 75 | for (UIView *view in viewBase.subviews) 76 | { 77 | [view removeFromSuperview]; 78 | } 79 | if (cloneView) { 80 | 81 | cloneView.center = self.center; 82 | 83 | //[cloneView addShadow]; 84 | 85 | [viewBase addSubview:cloneView]; 86 | } 87 | } 88 | 89 | -(void)setCurrentPageIndex:(NSUInteger)currentPageIndex 90 | { 91 | if (_currentPageIndex - 1 == currentPageIndex) 92 | { 93 | [self cloneBaseView:_viewNext view:_viewCurrent.subviews.firstObject]; 94 | [self cloneBaseView:_viewCurrent view:_viewPrevious.subviews.firstObject]; 95 | 96 | UIView *view = [self viewForItemAtIndex:currentPageIndex - 1 reusingView:_viewPrevious.subviews.firstObject]; 97 | [self cloneBaseView:_viewPrevious view:view]; 98 | 99 | if ([_delegate respondsToSelector:@selector(carouselView:didMoveToView:)]) { 100 | [_delegate carouselView:self didMoveToView:view]; 101 | } 102 | } 103 | else if (_currentPageIndex == currentPageIndex - 1) 104 | { 105 | [self cloneBaseView:_viewPrevious view:_viewCurrent.subviews.firstObject]; 106 | [self cloneBaseView:_viewCurrent view:_viewNext.subviews.firstObject]; 107 | 108 | UIView *view = [self viewForItemAtIndex:currentPageIndex + 1 reusingView:_viewNext.subviews.firstObject]; 109 | [self cloneBaseView:_viewNext view:view]; 110 | 111 | if ([_delegate respondsToSelector:@selector(carouselView:didMoveToView:)]) { 112 | [_delegate carouselView:self didMoveToView:view]; 113 | } 114 | } 115 | else { 116 | UIView *viewNext = [self viewForItemAtIndex:currentPageIndex + 1 reusingView:_viewNext.subviews.firstObject]; 117 | UIView *viewCurrent = [self viewForItemAtIndex:currentPageIndex reusingView:_viewCurrent.subviews.firstObject]; 118 | UIView *viewPrevious = [self viewForItemAtIndex:currentPageIndex - 1 reusingView:_viewPrevious.subviews.firstObject]; 119 | 120 | [self cloneBaseView:_viewNext view:viewNext]; 121 | [self cloneBaseView:_viewCurrent view:viewCurrent]; 122 | [self cloneBaseView:_viewPrevious view:viewPrevious]; 123 | 124 | 125 | if ([_delegate respondsToSelector:@selector(carouselView:didMoveToView:)]) { 126 | [_delegate carouselView:self didMoveToView:viewPrevious]; 127 | [_delegate carouselView:self didMoveToView:viewCurrent]; 128 | [_delegate carouselView:self didMoveToView:viewNext]; 129 | } 130 | } 131 | 132 | if (_currentPageIndex != currentPageIndex) { 133 | 134 | if ([_delegate respondsToSelector:@selector(carouselCurrentItemIndexDidChange:currentIndex:previousIndex:)]) { 135 | [_delegate carouselCurrentItemIndexDidChange:self currentIndex:currentPageIndex previousIndex:_currentPageIndex]; 136 | } 137 | } 138 | 139 | _currentPageIndex = currentPageIndex; 140 | } 141 | 142 | -(UIView*)viewForItemAtIndex:(NSUInteger)index reusingView:(UIView*)reusingView 143 | { 144 | if (_numberOfItems > index) { 145 | return [_dataSource carouselView:self viewForItemAtIndex:index reusingView:reusingView]; 146 | } 147 | return nil; 148 | 149 | } 150 | 151 | -(void)setVertical:(BOOL)vertical 152 | { 153 | _vertical = vertical; 154 | 155 | [self reloadFrame]; 156 | } 157 | 158 | -(void)reloadFrame 159 | { 160 | _scrollView.contentInset = UIEdgeInsetsZero; 161 | 162 | [_scrollView setFrame:self.bounds]; 163 | 164 | [_viewNext setFrame:self.bounds]; 165 | 166 | [_viewCurrent setFrame:self.bounds]; 167 | 168 | [_viewPrevious setFrame:CGRectMake((_vertical ? 0.0f : - self.bounds.size.width), (_vertical ? - self.bounds.size.height : 0.0f ), self.bounds.size.width, self.bounds.size.height)]; 169 | 170 | [_scrollView setContentSize:CGSizeMake((_vertical ? 1 : _numberOfItems) * self.frame.size.width, (_vertical ? _numberOfItems : 1) * self.frame.size.height)]; 171 | 172 | [self setNeedsLayout]; 173 | [self layoutIfNeeded]; 174 | } 175 | 176 | -(void)setDirection:(PLCarouselViewDirection)direction 177 | { 178 | if (_direction != direction) { 179 | _direction = direction; 180 | 181 | _viewNext.hidden = (_direction == PLCarouselViewDirectionStatic); 182 | _viewPrevious.hidden = (_direction == PLCarouselViewDirectionStatic); 183 | _viewCurrent.hidden = NO; 184 | /* 185 | if (_direction == PLCarouselViewDirectionStatic) { 186 | _viewNext.hidden = YES; 187 | _viewPrevious.hidden = YES; 188 | } 189 | else { 190 | _viewNext.hidden = _vertical ? !(_direction == PLCarouselViewDirectionDown) : !(_direction == PLCarouselViewDirectionRight); 191 | _viewPrevious.hidden = _vertical ? (_direction == PLCarouselViewDirectionDown) : (_direction == PLCarouselViewDirectionRight); 192 | } 193 | */ 194 | if ([_delegate respondsToSelector:@selector(carouselView:changedScrollDirection:)]) { 195 | [_delegate carouselView:self changedScrollDirection:_direction]; 196 | } 197 | } 198 | } 199 | 200 | -(UIView *)visibleItemWithType:(PLCarouselViewVisibleItem)type 201 | { 202 | switch (type) { 203 | case PLCarouselViewVisibleItemCurrent: 204 | { 205 | return _viewCurrent.subviews.firstObject; 206 | } 207 | break; 208 | 209 | case PLCarouselViewVisibleItemNext: 210 | { 211 | return _viewNext.subviews.firstObject; 212 | } 213 | break; 214 | case PLCarouselViewVisibleItemPrevious: 215 | { 216 | return _viewPrevious.subviews.firstObject; 217 | } 218 | break; 219 | default: 220 | break; 221 | } 222 | return nil; 223 | } 224 | 225 | -(void)loadView 226 | { 227 | _currentPageIndex = 0; 228 | _scrollEnabled = YES; 229 | 230 | for (UIView *view in _scrollView.subviews) 231 | { 232 | [view removeFromSuperview]; 233 | } 234 | 235 | [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; 236 | 237 | _scrollView.autoresizesSubviews = YES; 238 | 239 | _scrollView.contentInset = UIEdgeInsetsZero; 240 | 241 | [_scrollView setFrame:self.bounds]; 242 | 243 | _viewNext = [[UIView alloc]initWithFrame:self.bounds]; 244 | [_scrollView addSubview:_viewNext]; 245 | 246 | _viewCurrent = [[UIView alloc]initWithFrame:self.bounds]; 247 | [_scrollView addSubview:_viewCurrent]; 248 | 249 | _viewPrevious = [[UIView alloc]initWithFrame:CGRectMake((_vertical ? 0.0f : - self.bounds.size.width), (_vertical ? - self.bounds.size.height : 0.0f ), self.bounds.size.width, self.bounds.size.height)]; 250 | [_scrollView addSubview:_viewPrevious]; 251 | 252 | UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; 253 | singleTap.cancelsTouchesInView = NO; 254 | [_scrollView addGestureRecognizer:singleTap]; 255 | 256 | _viewNext.hidden = YES; 257 | _viewPrevious.hidden = YES; 258 | _direction = PLCarouselViewDirectionStatic; 259 | } 260 | 261 | -(void)reloadData 262 | { 263 | if (!_dataSource) 264 | { 265 | return; 266 | } 267 | NSUInteger newNumberOfItems = [_dataSource numberOfItemsInCarousel:self]; 268 | if (_numberOfItems != newNumberOfItems) { 269 | _numberOfItems = newNumberOfItems; 270 | 271 | [_scrollView setContentSize:CGSizeMake((_vertical ? 1 : _numberOfItems) * self.frame.size.width, (_vertical ? _numberOfItems : 1) * self.frame.size.height)]; 272 | 273 | NSInteger previousPageIndex = _currentPageIndex; 274 | if (previousPageIndex < 0) { 275 | previousPageIndex = 0; 276 | 277 | } 278 | 279 | _currentPageIndex = previousPageIndex; 280 | } 281 | 282 | self.currentPageIndex = _currentPageIndex; 283 | if ([_delegate respondsToSelector:@selector(carouselCurrentItemIndexDidChange:currentIndex:previousIndex:)]) { 284 | [_delegate carouselCurrentItemIndexDidChange:self currentIndex:_currentPageIndex previousIndex:_currentPageIndex]; 285 | } 286 | [self scrollToItemAtIndex:_currentPageIndex animated:NO]; 287 | } 288 | 289 | - (void)scrollToItemAtIndex:(NSInteger)index animated:(BOOL)animated 290 | { 291 | if (_numberOfItems > 0 && index < _numberOfItems) 292 | { 293 | [_scrollView setContentOffset:CGPointMake((_vertical ? 0 : index) * self.frame.size.width, (_vertical ? index : 0) * self.frame.size.height) animated:animated]; 294 | } 295 | } 296 | 297 | #pragma mark - UIScrollViewDelegates 298 | 299 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 300 | { 301 | CGFloat nowContentOffset = (_vertical ? scrollView.contentOffset.y : scrollView.contentOffset.x); 302 | 303 | CGFloat pageSize = (_vertical ? scrollView.frame.size.height : scrollView.frame.size.width); 304 | 305 | CGFloat fractionalPage = nowContentOffset / pageSize; 306 | 307 | NSInteger page = floorf(fractionalPage); 308 | 309 | if (_lastContentOffset > nowContentOffset) 310 | { 311 | self.direction = _vertical ? PLCarouselViewDirectionUp : PLCarouselViewDirectionLeft; 312 | } 313 | else if (_lastContentOffset < nowContentOffset) 314 | { 315 | self.direction = _vertical ? PLCarouselViewDirectionDown : PLCarouselViewDirectionRight; 316 | } 317 | 318 | if (_currentPageIndex * pageSize > nowContentOffset) 319 | { 320 | page = ceilf(fractionalPage); 321 | _viewCurrent.frame = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y, self.bounds.size.width, self.bounds.size.height); 322 | } 323 | 324 | _viewNext.frame = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y, self.bounds.size.width, self.bounds.size.height); 325 | 326 | _lastContentOffset = nowContentOffset; 327 | 328 | if (_currentPageIndex != page && page >= 0) { 329 | 330 | _viewPrevious.frame = CGRectMake((_vertical ? 0.0f : (page - 1) * self.bounds.size.width),(_vertical ? (page - 1) * self.bounds.size.height : 0.0f), self.bounds.size.width, self.bounds.size.height); 331 | _viewNext.frame = CGRectMake((_vertical ? 0.0f : page) * self.bounds.size.width, (_vertical ? page : 0.0f) * self.bounds.size.height, self.bounds.size.width, self.bounds.size.height); 332 | _viewCurrent.frame = _viewNext.frame; 333 | 334 | self.currentPageIndex = page; 335 | 336 | } 337 | 338 | if ([_delegate respondsToSelector:@selector(carouselView:didScrollDiffrenceRate:)]) { 339 | [_delegate carouselView:self didScrollDiffrenceRate:fractionalPage]; 340 | } 341 | } 342 | 343 | -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 344 | { 345 | self.direction = PLCarouselViewDirectionStatic; 346 | } 347 | 348 | #pragma mark - UIGestureRecognizerDelegates 349 | 350 | -(void)handleTap:(UITapGestureRecognizer*)gesture 351 | { 352 | CGPoint point = [gesture locationInView:_scrollView]; 353 | 354 | CGFloat nowContentOffset = (_vertical ? point.y : point.x); 355 | 356 | float fractionalPage = nowContentOffset / (_vertical ? self.bounds.size.height : self.bounds.size.width); 357 | 358 | NSInteger didSelectIndex = floorf(fractionalPage); 359 | 360 | if ([_delegate respondsToSelector:@selector(carouselView:didSelectItemAtIndex:)] && didSelectIndex > -1 && didSelectIndex < _numberOfItems) { 361 | [_delegate carouselView:self didSelectItemAtIndex:didSelectIndex]; 362 | } 363 | } 364 | 365 | @end 366 | -------------------------------------------------------------------------------- /CarouselView/PLCarouselView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselView/PLCarouselView.h: -------------------------------------------------------------------------------- 1 | // 2 | // PLCarouselView.h 3 | // OmsaTech 4 | // 5 | // Created by Anil Oruc on 5/4/15. 6 | // Copyright (c) 2015 OmsaTech. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum 12 | { 13 | PLCarouselViewVisibleItemCurrent = 0, 14 | PLCarouselViewVisibleItemNext, 15 | PLCarouselViewVisibleItemPrevious 16 | 17 | } 18 | PLCarouselViewVisibleItem; 19 | 20 | typedef enum 21 | { 22 | PLCarouselViewDirectionStatic = 0, 23 | PLCarouselViewDirectionUp, 24 | PLCarouselViewDirectionDown, 25 | PLCarouselViewDirectionLeft, 26 | PLCarouselViewDirectionRight 27 | } 28 | PLCarouselViewDirection; 29 | 30 | @class PLCarouselView; 31 | 32 | @protocol PLCarouselViewDelegate 33 | 34 | @optional 35 | 36 | -(void)carouselView:(PLCarouselView*)carouselView didSelectItemAtIndex:(NSUInteger)index; 37 | 38 | -(void)carouselCurrentItemIndexDidChange:(PLCarouselView *)carouselView currentIndex:(NSUInteger)currentIndex previousIndex:(NSUInteger)previousIndex; 39 | 40 | -(void)carouselView:(PLCarouselView*)carouselView didScrollDiffrenceRate:(CGFloat)diffRate; 41 | 42 | -(void)carouselView:(PLCarouselView*)carouselView didMoveToView:(UIView*)view; 43 | 44 | -(void)carouselView:(PLCarouselView*)carouselView changedScrollDirection:(PLCarouselViewDirection)direction; 45 | 46 | @end 47 | 48 | @protocol PLCarouselViewDataSource 49 | 50 | @required 51 | 52 | -(UIView*)carouselView:(PLCarouselView *)carouselView viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view; 53 | 54 | -(NSUInteger)numberOfItemsInCarousel:(PLCarouselView *)carouselView; 55 | 56 | @end 57 | 58 | @interface PLCarouselView : UIView 59 | 60 | + (instancetype)init; 61 | 62 | + (instancetype)initWithFrame:(CGRect)frame; 63 | 64 | @property (nonatomic, weak) IBOutlet id dataSource; 65 | 66 | @property (nonatomic, weak) IBOutlet id delegate; 67 | 68 | @property (nonatomic, assign) BOOL vertical; 69 | 70 | @property (nonatomic, assign) BOOL scrollEnabled; 71 | 72 | @property (nonatomic, assign, readonly) NSUInteger currentPageIndex; 73 | 74 | - (void)scrollToItemAtIndex:(NSInteger)index animated:(BOOL)animated; 75 | 76 | - (void)reloadData; 77 | 78 | - (UIView*)visibleItemWithType:(PLCarouselViewVisibleItem)type; 79 | 80 | - (void)resetInset; 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselView/PLCarouselView.m: -------------------------------------------------------------------------------- 1 | // 2 | // PLCarouselView.m 3 | // platinum 4 | // 5 | // Created by Anil Oruc on 5/4/15. 6 | // Copyright (c) 2015 Turkcell. All rights reserved. 7 | // 8 | 9 | #import "PLCarouselView.h" 10 | 11 | #import 12 | #import 13 | 14 | @interface PLCarouselView () 15 | 16 | @property (weak, nonatomic) IBOutlet UIScrollView *scrollView; 17 | @property (nonatomic, assign) CGFloat lastContentOffset; 18 | @property (nonatomic, assign) PLCarouselViewDirection direction; 19 | 20 | @property (nonatomic,strong) UIView *viewPrevious; 21 | @property (nonatomic,strong) UIView *viewCurrent; 22 | @property (nonatomic,strong) UIView *viewNext; 23 | 24 | @property (nonatomic, assign) NSUInteger currentPageIndex; 25 | @property (nonatomic, assign) NSUInteger numberOfItems; 26 | 27 | @property (nonatomic, assign) BOOL isScrolling; 28 | 29 | @end 30 | 31 | @implementation PLCarouselView 32 | 33 | +(instancetype)init 34 | { 35 | PLCarouselView *view = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil].firstObject; 36 | if (self) { 37 | view.vertical = NO; 38 | [view loadView]; 39 | } 40 | return view; 41 | } 42 | 43 | + (instancetype)initWithFrame:(CGRect)frame 44 | { 45 | PLCarouselView *view = [self init]; 46 | if (view) 47 | { 48 | [view setFrame:frame]; 49 | 50 | } 51 | return view; 52 | } 53 | 54 | -(void)setFrame:(CGRect)frame 55 | { 56 | [super setFrame:frame]; 57 | 58 | [self reloadFrame]; 59 | } 60 | 61 | -(void)setScrollEnabled:(BOOL)scrollEnabled 62 | { 63 | _scrollEnabled = scrollEnabled; 64 | 65 | _scrollView.scrollEnabled = scrollEnabled; 66 | } 67 | 68 | - (void)resetInset 69 | { 70 | _scrollView.contentInset = UIEdgeInsetsZero; 71 | } 72 | 73 | -(void)cloneBaseView:(UIView*)viewBase view:(UIView*)cloneView 74 | { 75 | for (UIView *view in viewBase.subviews) 76 | { 77 | [view removeFromSuperview]; 78 | } 79 | if (cloneView) { 80 | 81 | cloneView.center = self.center; 82 | 83 | //[cloneView addShadow]; 84 | 85 | [viewBase addSubview:cloneView]; 86 | } 87 | } 88 | 89 | -(void)setCurrentPageIndex:(NSUInteger)currentPageIndex 90 | { 91 | if (_currentPageIndex - 1 == currentPageIndex) 92 | { 93 | [self cloneBaseView:_viewNext view:_viewCurrent.subviews.firstObject]; 94 | [self cloneBaseView:_viewCurrent view:_viewPrevious.subviews.firstObject]; 95 | 96 | UIView *view = [self viewForItemAtIndex:currentPageIndex - 1 reusingView:_viewPrevious.subviews.firstObject]; 97 | [self cloneBaseView:_viewPrevious view:view]; 98 | 99 | if ([_delegate respondsToSelector:@selector(carouselView:didMoveToView:)]) { 100 | [_delegate carouselView:self didMoveToView:view]; 101 | } 102 | } 103 | else if (_currentPageIndex == currentPageIndex - 1) 104 | { 105 | [self cloneBaseView:_viewPrevious view:_viewCurrent.subviews.firstObject]; 106 | [self cloneBaseView:_viewCurrent view:_viewNext.subviews.firstObject]; 107 | 108 | UIView *view = [self viewForItemAtIndex:currentPageIndex + 1 reusingView:_viewNext.subviews.firstObject]; 109 | [self cloneBaseView:_viewNext view:view]; 110 | 111 | if ([_delegate respondsToSelector:@selector(carouselView:didMoveToView:)]) { 112 | [_delegate carouselView:self didMoveToView:view]; 113 | } 114 | } 115 | else { 116 | UIView *viewNext = [self viewForItemAtIndex:currentPageIndex + 1 reusingView:_viewNext.subviews.firstObject]; 117 | UIView *viewCurrent = [self viewForItemAtIndex:currentPageIndex reusingView:_viewCurrent.subviews.firstObject]; 118 | UIView *viewPrevious = [self viewForItemAtIndex:currentPageIndex - 1 reusingView:_viewPrevious.subviews.firstObject]; 119 | 120 | [self cloneBaseView:_viewNext view:viewNext]; 121 | [self cloneBaseView:_viewCurrent view:viewCurrent]; 122 | [self cloneBaseView:_viewPrevious view:viewPrevious]; 123 | 124 | 125 | if ([_delegate respondsToSelector:@selector(carouselView:didMoveToView:)]) { 126 | [_delegate carouselView:self didMoveToView:viewPrevious]; 127 | [_delegate carouselView:self didMoveToView:viewCurrent]; 128 | [_delegate carouselView:self didMoveToView:viewNext]; 129 | } 130 | } 131 | 132 | if (_currentPageIndex != currentPageIndex) { 133 | 134 | if ([_delegate respondsToSelector:@selector(carouselCurrentItemIndexDidChange:currentIndex:previousIndex:)]) { 135 | [_delegate carouselCurrentItemIndexDidChange:self currentIndex:currentPageIndex previousIndex:_currentPageIndex]; 136 | } 137 | } 138 | 139 | _currentPageIndex = currentPageIndex; 140 | } 141 | 142 | -(UIView*)viewForItemAtIndex:(NSUInteger)index reusingView:(UIView*)reusingView 143 | { 144 | if (_numberOfItems > index) { 145 | return [_dataSource carouselView:self viewForItemAtIndex:index reusingView:reusingView]; 146 | } 147 | return nil; 148 | 149 | } 150 | 151 | -(void)setVertical:(BOOL)vertical 152 | { 153 | _vertical = vertical; 154 | 155 | [self reloadFrame]; 156 | } 157 | 158 | -(void)reloadFrame 159 | { 160 | _scrollView.contentInset = UIEdgeInsetsZero; 161 | 162 | [_scrollView setFrame:self.bounds]; 163 | 164 | [_viewNext setFrame:self.bounds]; 165 | 166 | [_viewCurrent setFrame:self.bounds]; 167 | 168 | [_viewPrevious setFrame:CGRectMake((_vertical ? 0.0f : - self.bounds.size.width), (_vertical ? - self.bounds.size.height : 0.0f ), self.bounds.size.width, self.bounds.size.height)]; 169 | 170 | [_scrollView setContentSize:CGSizeMake((_vertical ? 1 : _numberOfItems) * self.frame.size.width, (_vertical ? _numberOfItems : 1) * self.frame.size.height)]; 171 | 172 | [self setNeedsLayout]; 173 | [self layoutIfNeeded]; 174 | } 175 | 176 | -(void)setDirection:(PLCarouselViewDirection)direction 177 | { 178 | if (_direction != direction) { 179 | _direction = direction; 180 | 181 | _viewNext.hidden = (_direction == PLCarouselViewDirectionStatic); 182 | _viewPrevious.hidden = (_direction == PLCarouselViewDirectionStatic); 183 | _viewCurrent.hidden = NO; 184 | /* 185 | if (_direction == PLCarouselViewDirectionStatic) { 186 | _viewNext.hidden = YES; 187 | _viewPrevious.hidden = YES; 188 | } 189 | else { 190 | _viewNext.hidden = _vertical ? !(_direction == PLCarouselViewDirectionDown) : !(_direction == PLCarouselViewDirectionRight); 191 | _viewPrevious.hidden = _vertical ? (_direction == PLCarouselViewDirectionDown) : (_direction == PLCarouselViewDirectionRight); 192 | } 193 | */ 194 | if ([_delegate respondsToSelector:@selector(carouselView:changedScrollDirection:)]) { 195 | [_delegate carouselView:self changedScrollDirection:_direction]; 196 | } 197 | } 198 | } 199 | 200 | -(UIView *)visibleItemWithType:(PLCarouselViewVisibleItem)type 201 | { 202 | switch (type) { 203 | case PLCarouselViewVisibleItemCurrent: 204 | { 205 | return _viewCurrent.subviews.firstObject; 206 | } 207 | break; 208 | 209 | case PLCarouselViewVisibleItemNext: 210 | { 211 | return _viewNext.subviews.firstObject; 212 | } 213 | break; 214 | case PLCarouselViewVisibleItemPrevious: 215 | { 216 | return _viewPrevious.subviews.firstObject; 217 | } 218 | break; 219 | default: 220 | break; 221 | } 222 | return nil; 223 | } 224 | 225 | -(void)loadView 226 | { 227 | _currentPageIndex = 0; 228 | _scrollEnabled = YES; 229 | 230 | for (UIView *view in _scrollView.subviews) 231 | { 232 | [view removeFromSuperview]; 233 | } 234 | 235 | [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; 236 | 237 | _scrollView.autoresizesSubviews = YES; 238 | 239 | _scrollView.contentInset = UIEdgeInsetsZero; 240 | 241 | [_scrollView setFrame:self.bounds]; 242 | 243 | _viewNext = [[UIView alloc]initWithFrame:self.bounds]; 244 | [_scrollView addSubview:_viewNext]; 245 | 246 | _viewCurrent = [[UIView alloc]initWithFrame:self.bounds]; 247 | [_scrollView addSubview:_viewCurrent]; 248 | 249 | _viewPrevious = [[UIView alloc]initWithFrame:CGRectMake((_vertical ? 0.0f : - self.bounds.size.width), (_vertical ? - self.bounds.size.height : 0.0f ), self.bounds.size.width, self.bounds.size.height)]; 250 | [_scrollView addSubview:_viewPrevious]; 251 | 252 | UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; 253 | singleTap.cancelsTouchesInView = NO; 254 | [_scrollView addGestureRecognizer:singleTap]; 255 | 256 | _viewNext.hidden = YES; 257 | _viewPrevious.hidden = YES; 258 | _direction = PLCarouselViewDirectionStatic; 259 | } 260 | 261 | -(void)reloadData 262 | { 263 | if (!_dataSource) 264 | { 265 | return; 266 | } 267 | NSUInteger newNumberOfItems = [_dataSource numberOfItemsInCarousel:self]; 268 | if (_numberOfItems != newNumberOfItems) { 269 | _numberOfItems = newNumberOfItems; 270 | 271 | [_scrollView setContentSize:CGSizeMake((_vertical ? 1 : _numberOfItems) * self.frame.size.width, (_vertical ? _numberOfItems : 1) * self.frame.size.height)]; 272 | 273 | NSInteger previousPageIndex = _currentPageIndex; 274 | if (previousPageIndex < 0) { 275 | previousPageIndex = 0; 276 | 277 | } 278 | 279 | _currentPageIndex = previousPageIndex; 280 | } 281 | 282 | self.currentPageIndex = _currentPageIndex; 283 | if ([_delegate respondsToSelector:@selector(carouselCurrentItemIndexDidChange:currentIndex:previousIndex:)]) { 284 | [_delegate carouselCurrentItemIndexDidChange:self currentIndex:_currentPageIndex previousIndex:_currentPageIndex]; 285 | } 286 | [self scrollToItemAtIndex:_currentPageIndex animated:NO]; 287 | } 288 | 289 | - (void)scrollToItemAtIndex:(NSInteger)index animated:(BOOL)animated 290 | { 291 | if (_numberOfItems > 0 && index < _numberOfItems) 292 | { 293 | [_scrollView setContentOffset:CGPointMake((_vertical ? 0 : index) * self.frame.size.width, (_vertical ? index : 0) * self.frame.size.height) animated:animated]; 294 | } 295 | } 296 | 297 | #pragma mark - UIScrollViewDelegates 298 | 299 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 300 | { 301 | CGFloat nowContentOffset = (_vertical ? scrollView.contentOffset.y : scrollView.contentOffset.x); 302 | 303 | CGFloat pageSize = (_vertical ? scrollView.frame.size.height : scrollView.frame.size.width); 304 | 305 | CGFloat fractionalPage = nowContentOffset / pageSize; 306 | 307 | NSInteger page = floorf(fractionalPage); 308 | 309 | if (_lastContentOffset > nowContentOffset) 310 | { 311 | self.direction = _vertical ? PLCarouselViewDirectionUp : PLCarouselViewDirectionLeft; 312 | } 313 | else if (_lastContentOffset < nowContentOffset) 314 | { 315 | self.direction = _vertical ? PLCarouselViewDirectionDown : PLCarouselViewDirectionRight; 316 | } 317 | 318 | if (_currentPageIndex * pageSize > nowContentOffset) 319 | { 320 | page = ceilf(fractionalPage); 321 | _viewCurrent.frame = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y, self.bounds.size.width, self.bounds.size.height); 322 | } 323 | 324 | _viewNext.frame = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y, self.bounds.size.width, self.bounds.size.height); 325 | 326 | _lastContentOffset = nowContentOffset; 327 | 328 | if (_currentPageIndex != page && page >= 0) { 329 | 330 | _viewPrevious.frame = CGRectMake((_vertical ? 0.0f : (page - 1) * self.bounds.size.width),(_vertical ? (page - 1) * self.bounds.size.height : 0.0f), self.bounds.size.width, self.bounds.size.height); 331 | _viewNext.frame = CGRectMake((_vertical ? 0.0f : page) * self.bounds.size.width, (_vertical ? page : 0.0f) * self.bounds.size.height, self.bounds.size.width, self.bounds.size.height); 332 | _viewCurrent.frame = _viewNext.frame; 333 | 334 | self.currentPageIndex = page; 335 | 336 | } 337 | 338 | if ([_delegate respondsToSelector:@selector(carouselView:didScrollDiffrenceRate:)]) { 339 | [_delegate carouselView:self didScrollDiffrenceRate:fractionalPage]; 340 | } 341 | } 342 | 343 | -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 344 | { 345 | self.direction = PLCarouselViewDirectionStatic; 346 | } 347 | 348 | #pragma mark - UIGestureRecognizerDelegates 349 | 350 | -(void)handleTap:(UITapGestureRecognizer*)gesture 351 | { 352 | CGPoint point = [gesture locationInView:_scrollView]; 353 | 354 | CGFloat nowContentOffset = (_vertical ? point.y : point.x); 355 | 356 | float fractionalPage = nowContentOffset / (_vertical ? self.bounds.size.height : self.bounds.size.width); 357 | 358 | NSInteger didSelectIndex = floorf(fractionalPage); 359 | 360 | if ([_delegate respondsToSelector:@selector(carouselView:didSelectItemAtIndex:)] && didSelectIndex > -1 && didSelectIndex < _numberOfItems) { 361 | [_delegate carouselView:self didSelectItemAtIndex:didSelectIndex]; 362 | } 363 | } 364 | 365 | @end 366 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselView/PLCarouselView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D3A569B81C917818002A1A8C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D3A569B71C917818002A1A8C /* main.m */; }; 11 | D3A569BB1C917818002A1A8C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D3A569BA1C917818002A1A8C /* AppDelegate.m */; }; 12 | D3A569BE1C917818002A1A8C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D3A569BD1C917818002A1A8C /* ViewController.m */; }; 13 | D3A569C11C917818002A1A8C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D3A569BF1C917818002A1A8C /* Main.storyboard */; }; 14 | D3A569C31C917818002A1A8C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D3A569C21C917818002A1A8C /* Assets.xcassets */; }; 15 | D3A569C61C917818002A1A8C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D3A569C41C917818002A1A8C /* LaunchScreen.storyboard */; }; 16 | D3A569D11C917835002A1A8C /* PLCarouselView.m in Sources */ = {isa = PBXBuildFile; fileRef = D3A569CF1C917835002A1A8C /* PLCarouselView.m */; }; 17 | D3A569D21C917835002A1A8C /* PLCarouselView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D3A569D01C917835002A1A8C /* PLCarouselView.xib */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | D3A569B31C917818002A1A8C /* CarouselViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CarouselViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | D3A569B71C917818002A1A8C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 23 | D3A569B91C917818002A1A8C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 24 | D3A569BA1C917818002A1A8C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 25 | D3A569BC1C917818002A1A8C /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 26 | D3A569BD1C917818002A1A8C /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 27 | D3A569C01C917818002A1A8C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 28 | D3A569C21C917818002A1A8C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 29 | D3A569C51C917818002A1A8C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 30 | D3A569C71C917818002A1A8C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 31 | D3A569CE1C917835002A1A8C /* PLCarouselView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PLCarouselView.h; sourceTree = ""; }; 32 | D3A569CF1C917835002A1A8C /* PLCarouselView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PLCarouselView.m; sourceTree = ""; }; 33 | D3A569D01C917835002A1A8C /* PLCarouselView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PLCarouselView.xib; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | D3A569B01C917818002A1A8C /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | D3A569AA1C917818002A1A8C = { 48 | isa = PBXGroup; 49 | children = ( 50 | D3A569B51C917818002A1A8C /* CarouselViewDemo */, 51 | D3A569B41C917818002A1A8C /* Products */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | D3A569B41C917818002A1A8C /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | D3A569B31C917818002A1A8C /* CarouselViewDemo.app */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | D3A569B51C917818002A1A8C /* CarouselViewDemo */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | D3A569CD1C917835002A1A8C /* CarouselView */, 67 | D3A569B91C917818002A1A8C /* AppDelegate.h */, 68 | D3A569BA1C917818002A1A8C /* AppDelegate.m */, 69 | D3A569BC1C917818002A1A8C /* ViewController.h */, 70 | D3A569BD1C917818002A1A8C /* ViewController.m */, 71 | D3A569BF1C917818002A1A8C /* Main.storyboard */, 72 | D3A569C21C917818002A1A8C /* Assets.xcassets */, 73 | D3A569C41C917818002A1A8C /* LaunchScreen.storyboard */, 74 | D3A569C71C917818002A1A8C /* Info.plist */, 75 | D3A569B61C917818002A1A8C /* Supporting Files */, 76 | ); 77 | path = CarouselViewDemo; 78 | sourceTree = ""; 79 | }; 80 | D3A569B61C917818002A1A8C /* Supporting Files */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | D3A569B71C917818002A1A8C /* main.m */, 84 | ); 85 | name = "Supporting Files"; 86 | sourceTree = ""; 87 | }; 88 | D3A569CD1C917835002A1A8C /* CarouselView */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | D3A569CE1C917835002A1A8C /* PLCarouselView.h */, 92 | D3A569CF1C917835002A1A8C /* PLCarouselView.m */, 93 | D3A569D01C917835002A1A8C /* PLCarouselView.xib */, 94 | ); 95 | path = CarouselView; 96 | sourceTree = ""; 97 | }; 98 | /* End PBXGroup section */ 99 | 100 | /* Begin PBXNativeTarget section */ 101 | D3A569B21C917818002A1A8C /* CarouselViewDemo */ = { 102 | isa = PBXNativeTarget; 103 | buildConfigurationList = D3A569CA1C917818002A1A8C /* Build configuration list for PBXNativeTarget "CarouselViewDemo" */; 104 | buildPhases = ( 105 | D3A569AF1C917818002A1A8C /* Sources */, 106 | D3A569B01C917818002A1A8C /* Frameworks */, 107 | D3A569B11C917818002A1A8C /* Resources */, 108 | ); 109 | buildRules = ( 110 | ); 111 | dependencies = ( 112 | ); 113 | name = CarouselViewDemo; 114 | productName = CarouselViewDemo; 115 | productReference = D3A569B31C917818002A1A8C /* CarouselViewDemo.app */; 116 | productType = "com.apple.product-type.application"; 117 | }; 118 | /* End PBXNativeTarget section */ 119 | 120 | /* Begin PBXProject section */ 121 | D3A569AB1C917818002A1A8C /* Project object */ = { 122 | isa = PBXProject; 123 | attributes = { 124 | LastUpgradeCheck = 0720; 125 | ORGANIZATIONNAME = "Anil Oruc"; 126 | TargetAttributes = { 127 | D3A569B21C917818002A1A8C = { 128 | CreatedOnToolsVersion = 7.2.1; 129 | }; 130 | }; 131 | }; 132 | buildConfigurationList = D3A569AE1C917818002A1A8C /* Build configuration list for PBXProject "CarouselViewDemo" */; 133 | compatibilityVersion = "Xcode 3.2"; 134 | developmentRegion = English; 135 | hasScannedForEncodings = 0; 136 | knownRegions = ( 137 | en, 138 | Base, 139 | ); 140 | mainGroup = D3A569AA1C917818002A1A8C; 141 | productRefGroup = D3A569B41C917818002A1A8C /* Products */; 142 | projectDirPath = ""; 143 | projectRoot = ""; 144 | targets = ( 145 | D3A569B21C917818002A1A8C /* CarouselViewDemo */, 146 | ); 147 | }; 148 | /* End PBXProject section */ 149 | 150 | /* Begin PBXResourcesBuildPhase section */ 151 | D3A569B11C917818002A1A8C /* Resources */ = { 152 | isa = PBXResourcesBuildPhase; 153 | buildActionMask = 2147483647; 154 | files = ( 155 | D3A569D21C917835002A1A8C /* PLCarouselView.xib in Resources */, 156 | D3A569C61C917818002A1A8C /* LaunchScreen.storyboard in Resources */, 157 | D3A569C31C917818002A1A8C /* Assets.xcassets in Resources */, 158 | D3A569C11C917818002A1A8C /* Main.storyboard in Resources */, 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | /* End PBXResourcesBuildPhase section */ 163 | 164 | /* Begin PBXSourcesBuildPhase section */ 165 | D3A569AF1C917818002A1A8C /* Sources */ = { 166 | isa = PBXSourcesBuildPhase; 167 | buildActionMask = 2147483647; 168 | files = ( 169 | D3A569D11C917835002A1A8C /* PLCarouselView.m in Sources */, 170 | D3A569BE1C917818002A1A8C /* ViewController.m in Sources */, 171 | D3A569BB1C917818002A1A8C /* AppDelegate.m in Sources */, 172 | D3A569B81C917818002A1A8C /* main.m in Sources */, 173 | ); 174 | runOnlyForDeploymentPostprocessing = 0; 175 | }; 176 | /* End PBXSourcesBuildPhase section */ 177 | 178 | /* Begin PBXVariantGroup section */ 179 | D3A569BF1C917818002A1A8C /* Main.storyboard */ = { 180 | isa = PBXVariantGroup; 181 | children = ( 182 | D3A569C01C917818002A1A8C /* Base */, 183 | ); 184 | name = Main.storyboard; 185 | sourceTree = ""; 186 | }; 187 | D3A569C41C917818002A1A8C /* LaunchScreen.storyboard */ = { 188 | isa = PBXVariantGroup; 189 | children = ( 190 | D3A569C51C917818002A1A8C /* Base */, 191 | ); 192 | name = LaunchScreen.storyboard; 193 | sourceTree = ""; 194 | }; 195 | /* End PBXVariantGroup section */ 196 | 197 | /* Begin XCBuildConfiguration section */ 198 | D3A569C81C917818002A1A8C /* Debug */ = { 199 | isa = XCBuildConfiguration; 200 | buildSettings = { 201 | ALWAYS_SEARCH_USER_PATHS = NO; 202 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 203 | CLANG_CXX_LIBRARY = "libc++"; 204 | CLANG_ENABLE_MODULES = YES; 205 | CLANG_ENABLE_OBJC_ARC = YES; 206 | CLANG_WARN_BOOL_CONVERSION = YES; 207 | CLANG_WARN_CONSTANT_CONVERSION = YES; 208 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 209 | CLANG_WARN_EMPTY_BODY = YES; 210 | CLANG_WARN_ENUM_CONVERSION = YES; 211 | CLANG_WARN_INT_CONVERSION = YES; 212 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 213 | CLANG_WARN_UNREACHABLE_CODE = YES; 214 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 215 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 216 | COPY_PHASE_STRIP = NO; 217 | DEBUG_INFORMATION_FORMAT = dwarf; 218 | ENABLE_STRICT_OBJC_MSGSEND = YES; 219 | ENABLE_TESTABILITY = YES; 220 | GCC_C_LANGUAGE_STANDARD = gnu99; 221 | GCC_DYNAMIC_NO_PIC = NO; 222 | GCC_NO_COMMON_BLOCKS = YES; 223 | GCC_OPTIMIZATION_LEVEL = 0; 224 | GCC_PREPROCESSOR_DEFINITIONS = ( 225 | "DEBUG=1", 226 | "$(inherited)", 227 | ); 228 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 229 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 230 | GCC_WARN_UNDECLARED_SELECTOR = YES; 231 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 232 | GCC_WARN_UNUSED_FUNCTION = YES; 233 | GCC_WARN_UNUSED_VARIABLE = YES; 234 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 235 | MTL_ENABLE_DEBUG_INFO = YES; 236 | ONLY_ACTIVE_ARCH = YES; 237 | SDKROOT = iphoneos; 238 | }; 239 | name = Debug; 240 | }; 241 | D3A569C91C917818002A1A8C /* Release */ = { 242 | isa = XCBuildConfiguration; 243 | buildSettings = { 244 | ALWAYS_SEARCH_USER_PATHS = NO; 245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 246 | CLANG_CXX_LIBRARY = "libc++"; 247 | CLANG_ENABLE_MODULES = YES; 248 | CLANG_ENABLE_OBJC_ARC = YES; 249 | CLANG_WARN_BOOL_CONVERSION = YES; 250 | CLANG_WARN_CONSTANT_CONVERSION = YES; 251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 252 | CLANG_WARN_EMPTY_BODY = YES; 253 | CLANG_WARN_ENUM_CONVERSION = YES; 254 | CLANG_WARN_INT_CONVERSION = YES; 255 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 256 | CLANG_WARN_UNREACHABLE_CODE = YES; 257 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 258 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 259 | COPY_PHASE_STRIP = NO; 260 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 261 | ENABLE_NS_ASSERTIONS = NO; 262 | ENABLE_STRICT_OBJC_MSGSEND = YES; 263 | GCC_C_LANGUAGE_STANDARD = gnu99; 264 | GCC_NO_COMMON_BLOCKS = YES; 265 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 266 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 267 | GCC_WARN_UNDECLARED_SELECTOR = YES; 268 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 269 | GCC_WARN_UNUSED_FUNCTION = YES; 270 | GCC_WARN_UNUSED_VARIABLE = YES; 271 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 272 | MTL_ENABLE_DEBUG_INFO = NO; 273 | SDKROOT = iphoneos; 274 | VALIDATE_PRODUCT = YES; 275 | }; 276 | name = Release; 277 | }; 278 | D3A569CB1C917818002A1A8C /* Debug */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 282 | INFOPLIST_FILE = CarouselViewDemo/Info.plist; 283 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 284 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 285 | PRODUCT_BUNDLE_IDENTIFIER = annulmobile.CarouselViewDemo; 286 | PRODUCT_NAME = "$(TARGET_NAME)"; 287 | }; 288 | name = Debug; 289 | }; 290 | D3A569CC1C917818002A1A8C /* Release */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 294 | INFOPLIST_FILE = CarouselViewDemo/Info.plist; 295 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 296 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 297 | PRODUCT_BUNDLE_IDENTIFIER = annulmobile.CarouselViewDemo; 298 | PRODUCT_NAME = "$(TARGET_NAME)"; 299 | }; 300 | name = Release; 301 | }; 302 | /* End XCBuildConfiguration section */ 303 | 304 | /* Begin XCConfigurationList section */ 305 | D3A569AE1C917818002A1A8C /* Build configuration list for PBXProject "CarouselViewDemo" */ = { 306 | isa = XCConfigurationList; 307 | buildConfigurations = ( 308 | D3A569C81C917818002A1A8C /* Debug */, 309 | D3A569C91C917818002A1A8C /* Release */, 310 | ); 311 | defaultConfigurationIsVisible = 0; 312 | defaultConfigurationName = Release; 313 | }; 314 | D3A569CA1C917818002A1A8C /* Build configuration list for PBXNativeTarget "CarouselViewDemo" */ = { 315 | isa = XCConfigurationList; 316 | buildConfigurations = ( 317 | D3A569CB1C917818002A1A8C /* Debug */, 318 | D3A569CC1C917818002A1A8C /* Release */, 319 | ); 320 | defaultConfigurationIsVisible = 0; 321 | defaultConfigurationName = Release; 322 | }; 323 | /* End XCConfigurationList section */ 324 | }; 325 | rootObject = D3A569AB1C917818002A1A8C /* Project object */; 326 | } 327 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo.xcodeproj/project.xcworkspace/xcuserdata/aniloruc.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orucanil/CarouselView/c388084ea64ad9df506b6efc819aa1b7dcf06d6f/CarouselViewDemo/CarouselViewDemo.xcodeproj/project.xcworkspace/xcuserdata/aniloruc.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo.xcodeproj/xcuserdata/aniloruc.xcuserdatad/xcschemes/CarouselViewDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo.xcodeproj/xcuserdata/aniloruc.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CarouselViewDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | D3A569B21C917818002A1A8C 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // CarouselViewDemo 4 | // 5 | // Created by Anil Oruc on 3/10/16. 6 | // Copyright © 2016 Anil Oruc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // CarouselViewDemo 4 | // 5 | // Created by Anil Oruc on 3/10/16. 6 | // Copyright © 2016 Anil Oruc. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo/CarouselView/PLCarouselView.h: -------------------------------------------------------------------------------- 1 | // 2 | // PLCarouselView.h 3 | // OmsaTech 4 | // 5 | // Created by Anil Oruc on 5/4/15. 6 | // Copyright (c) 2015 OmsaTech. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum 12 | { 13 | PLCarouselViewVisibleItemCurrent = 0, 14 | PLCarouselViewVisibleItemNext, 15 | PLCarouselViewVisibleItemPrevious 16 | 17 | } 18 | PLCarouselViewVisibleItem; 19 | 20 | typedef enum 21 | { 22 | PLCarouselViewDirectionStatic = 0, 23 | PLCarouselViewDirectionUp, 24 | PLCarouselViewDirectionDown, 25 | PLCarouselViewDirectionLeft, 26 | PLCarouselViewDirectionRight 27 | } 28 | PLCarouselViewDirection; 29 | 30 | @class PLCarouselView; 31 | 32 | @protocol PLCarouselViewDelegate 33 | 34 | @optional 35 | 36 | -(void)carouselView:(PLCarouselView*)carouselView didSelectItemAtIndex:(NSUInteger)index; 37 | 38 | -(void)carouselCurrentItemIndexDidChange:(PLCarouselView *)carouselView currentIndex:(NSUInteger)currentIndex previousIndex:(NSUInteger)previousIndex; 39 | 40 | -(void)carouselView:(PLCarouselView*)carouselView didScrollDiffrenceRate:(CGFloat)diffRate; 41 | 42 | -(void)carouselView:(PLCarouselView*)carouselView didMoveToView:(UIView*)view; 43 | 44 | -(void)carouselView:(PLCarouselView*)carouselView changedScrollDirection:(PLCarouselViewDirection)direction; 45 | 46 | @end 47 | 48 | @protocol PLCarouselViewDataSource 49 | 50 | @required 51 | 52 | -(UIView*)carouselView:(PLCarouselView *)carouselView viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view; 53 | 54 | -(NSUInteger)numberOfItemsInCarousel:(PLCarouselView *)carouselView; 55 | 56 | @end 57 | 58 | @interface PLCarouselView : UIView 59 | 60 | + (instancetype)init; 61 | 62 | + (instancetype)initWithFrame:(CGRect)frame; 63 | 64 | @property (nonatomic, weak) IBOutlet id dataSource; 65 | 66 | @property (nonatomic, weak) IBOutlet id delegate; 67 | 68 | @property (nonatomic, assign) BOOL vertical; 69 | 70 | @property (nonatomic, assign) BOOL scrollEnabled; 71 | 72 | @property (nonatomic, assign, readonly) NSUInteger currentPageIndex; 73 | 74 | - (void)scrollToItemAtIndex:(NSInteger)index animated:(BOOL)animated; 75 | 76 | - (void)reloadData; 77 | 78 | - (UIView*)visibleItemWithType:(PLCarouselViewVisibleItem)type; 79 | 80 | - (void)resetInset; 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo/CarouselView/PLCarouselView.m: -------------------------------------------------------------------------------- 1 | // 2 | // PLCarouselView.m 3 | // platinum 4 | // 5 | // Created by Anil Oruc on 5/4/15. 6 | // Copyright (c) 2015 Turkcell. All rights reserved. 7 | // 8 | 9 | #import "PLCarouselView.h" 10 | 11 | #import 12 | #import 13 | 14 | @interface PLCarouselView () 15 | 16 | @property (weak, nonatomic) IBOutlet UIScrollView *scrollView; 17 | @property (nonatomic, assign) CGFloat lastContentOffset; 18 | @property (nonatomic, assign) PLCarouselViewDirection direction; 19 | 20 | @property (nonatomic,strong) UIView *viewPrevious; 21 | @property (nonatomic,strong) UIView *viewCurrent; 22 | @property (nonatomic,strong) UIView *viewNext; 23 | 24 | @property (nonatomic, assign) NSUInteger currentPageIndex; 25 | @property (nonatomic, assign) NSUInteger numberOfItems; 26 | 27 | @property (nonatomic, assign) BOOL isScrolling; 28 | 29 | @end 30 | 31 | @implementation PLCarouselView 32 | 33 | +(instancetype)init 34 | { 35 | PLCarouselView *view = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil].firstObject; 36 | if (self) { 37 | view.vertical = NO; 38 | [view loadView]; 39 | } 40 | return view; 41 | } 42 | 43 | + (instancetype)initWithFrame:(CGRect)frame 44 | { 45 | PLCarouselView *view = [self init]; 46 | if (view) 47 | { 48 | [view setFrame:frame]; 49 | 50 | } 51 | return view; 52 | } 53 | 54 | -(void)setFrame:(CGRect)frame 55 | { 56 | [super setFrame:frame]; 57 | 58 | [self reloadFrame]; 59 | } 60 | 61 | -(void)setScrollEnabled:(BOOL)scrollEnabled 62 | { 63 | _scrollEnabled = scrollEnabled; 64 | 65 | _scrollView.scrollEnabled = scrollEnabled; 66 | } 67 | 68 | - (void)resetInset 69 | { 70 | _scrollView.contentInset = UIEdgeInsetsZero; 71 | } 72 | 73 | -(void)cloneBaseView:(UIView*)viewBase view:(UIView*)cloneView 74 | { 75 | for (UIView *view in viewBase.subviews) 76 | { 77 | [view removeFromSuperview]; 78 | } 79 | if (cloneView) { 80 | 81 | cloneView.center = self.center; 82 | 83 | //[cloneView addShadow]; 84 | 85 | [viewBase addSubview:cloneView]; 86 | } 87 | } 88 | 89 | -(void)setCurrentPageIndex:(NSUInteger)currentPageIndex 90 | { 91 | if (_currentPageIndex - 1 == currentPageIndex) 92 | { 93 | [self cloneBaseView:_viewNext view:_viewCurrent.subviews.firstObject]; 94 | [self cloneBaseView:_viewCurrent view:_viewPrevious.subviews.firstObject]; 95 | 96 | UIView *view = [self viewForItemAtIndex:currentPageIndex - 1 reusingView:_viewPrevious.subviews.firstObject]; 97 | [self cloneBaseView:_viewPrevious view:view]; 98 | 99 | if ([_delegate respondsToSelector:@selector(carouselView:didMoveToView:)]) { 100 | [_delegate carouselView:self didMoveToView:view]; 101 | } 102 | } 103 | else if (_currentPageIndex == currentPageIndex - 1) 104 | { 105 | [self cloneBaseView:_viewPrevious view:_viewCurrent.subviews.firstObject]; 106 | [self cloneBaseView:_viewCurrent view:_viewNext.subviews.firstObject]; 107 | 108 | UIView *view = [self viewForItemAtIndex:currentPageIndex + 1 reusingView:_viewNext.subviews.firstObject]; 109 | [self cloneBaseView:_viewNext view:view]; 110 | 111 | if ([_delegate respondsToSelector:@selector(carouselView:didMoveToView:)]) { 112 | [_delegate carouselView:self didMoveToView:view]; 113 | } 114 | } 115 | else { 116 | UIView *viewNext = [self viewForItemAtIndex:currentPageIndex + 1 reusingView:_viewNext.subviews.firstObject]; 117 | UIView *viewCurrent = [self viewForItemAtIndex:currentPageIndex reusingView:_viewCurrent.subviews.firstObject]; 118 | UIView *viewPrevious = [self viewForItemAtIndex:currentPageIndex - 1 reusingView:_viewPrevious.subviews.firstObject]; 119 | 120 | [self cloneBaseView:_viewNext view:viewNext]; 121 | [self cloneBaseView:_viewCurrent view:viewCurrent]; 122 | [self cloneBaseView:_viewPrevious view:viewPrevious]; 123 | 124 | 125 | if ([_delegate respondsToSelector:@selector(carouselView:didMoveToView:)]) { 126 | [_delegate carouselView:self didMoveToView:viewPrevious]; 127 | [_delegate carouselView:self didMoveToView:viewCurrent]; 128 | [_delegate carouselView:self didMoveToView:viewNext]; 129 | } 130 | } 131 | 132 | if (_currentPageIndex != currentPageIndex) { 133 | 134 | if ([_delegate respondsToSelector:@selector(carouselCurrentItemIndexDidChange:currentIndex:previousIndex:)]) { 135 | [_delegate carouselCurrentItemIndexDidChange:self currentIndex:currentPageIndex previousIndex:_currentPageIndex]; 136 | } 137 | } 138 | 139 | _currentPageIndex = currentPageIndex; 140 | } 141 | 142 | -(UIView*)viewForItemAtIndex:(NSUInteger)index reusingView:(UIView*)reusingView 143 | { 144 | if (_numberOfItems > index) { 145 | return [_dataSource carouselView:self viewForItemAtIndex:index reusingView:reusingView]; 146 | } 147 | return nil; 148 | 149 | } 150 | 151 | -(void)setVertical:(BOOL)vertical 152 | { 153 | _vertical = vertical; 154 | 155 | [self reloadFrame]; 156 | } 157 | 158 | -(void)reloadFrame 159 | { 160 | _scrollView.contentInset = UIEdgeInsetsZero; 161 | 162 | [_scrollView setFrame:self.bounds]; 163 | 164 | [_viewNext setFrame:self.bounds]; 165 | 166 | [_viewCurrent setFrame:self.bounds]; 167 | 168 | [_viewPrevious setFrame:CGRectMake((_vertical ? 0.0f : - self.bounds.size.width), (_vertical ? - self.bounds.size.height : 0.0f ), self.bounds.size.width, self.bounds.size.height)]; 169 | 170 | [_scrollView setContentSize:CGSizeMake((_vertical ? 1 : _numberOfItems) * self.frame.size.width, (_vertical ? _numberOfItems : 1) * self.frame.size.height)]; 171 | 172 | [self setNeedsLayout]; 173 | [self layoutIfNeeded]; 174 | } 175 | 176 | -(void)setDirection:(PLCarouselViewDirection)direction 177 | { 178 | if (_direction != direction) { 179 | _direction = direction; 180 | 181 | _viewNext.hidden = (_direction == PLCarouselViewDirectionStatic); 182 | _viewPrevious.hidden = (_direction == PLCarouselViewDirectionStatic); 183 | _viewCurrent.hidden = NO; 184 | /* 185 | if (_direction == PLCarouselViewDirectionStatic) { 186 | _viewNext.hidden = YES; 187 | _viewPrevious.hidden = YES; 188 | } 189 | else { 190 | _viewNext.hidden = _vertical ? !(_direction == PLCarouselViewDirectionDown) : !(_direction == PLCarouselViewDirectionRight); 191 | _viewPrevious.hidden = _vertical ? (_direction == PLCarouselViewDirectionDown) : (_direction == PLCarouselViewDirectionRight); 192 | } 193 | */ 194 | if ([_delegate respondsToSelector:@selector(carouselView:changedScrollDirection:)]) { 195 | [_delegate carouselView:self changedScrollDirection:_direction]; 196 | } 197 | } 198 | } 199 | 200 | -(UIView *)visibleItemWithType:(PLCarouselViewVisibleItem)type 201 | { 202 | switch (type) { 203 | case PLCarouselViewVisibleItemCurrent: 204 | { 205 | return _viewCurrent.subviews.firstObject; 206 | } 207 | break; 208 | 209 | case PLCarouselViewVisibleItemNext: 210 | { 211 | return _viewNext.subviews.firstObject; 212 | } 213 | break; 214 | case PLCarouselViewVisibleItemPrevious: 215 | { 216 | return _viewPrevious.subviews.firstObject; 217 | } 218 | break; 219 | default: 220 | break; 221 | } 222 | return nil; 223 | } 224 | 225 | -(void)loadView 226 | { 227 | _currentPageIndex = 0; 228 | _scrollEnabled = YES; 229 | 230 | for (UIView *view in _scrollView.subviews) 231 | { 232 | [view removeFromSuperview]; 233 | } 234 | 235 | [self setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; 236 | 237 | _scrollView.autoresizesSubviews = YES; 238 | 239 | _scrollView.contentInset = UIEdgeInsetsZero; 240 | 241 | [_scrollView setFrame:self.bounds]; 242 | 243 | _viewNext = [[UIView alloc]initWithFrame:self.bounds]; 244 | [_scrollView addSubview:_viewNext]; 245 | 246 | _viewCurrent = [[UIView alloc]initWithFrame:self.bounds]; 247 | [_scrollView addSubview:_viewCurrent]; 248 | 249 | _viewPrevious = [[UIView alloc]initWithFrame:CGRectMake((_vertical ? 0.0f : - self.bounds.size.width), (_vertical ? - self.bounds.size.height : 0.0f ), self.bounds.size.width, self.bounds.size.height)]; 250 | [_scrollView addSubview:_viewPrevious]; 251 | 252 | UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; 253 | singleTap.cancelsTouchesInView = NO; 254 | [_scrollView addGestureRecognizer:singleTap]; 255 | 256 | _viewNext.hidden = YES; 257 | _viewPrevious.hidden = YES; 258 | _direction = PLCarouselViewDirectionStatic; 259 | } 260 | 261 | -(void)reloadData 262 | { 263 | if (!_dataSource) 264 | { 265 | return; 266 | } 267 | NSUInteger newNumberOfItems = [_dataSource numberOfItemsInCarousel:self]; 268 | if (_numberOfItems != newNumberOfItems) { 269 | _numberOfItems = newNumberOfItems; 270 | 271 | [_scrollView setContentSize:CGSizeMake((_vertical ? 1 : _numberOfItems) * self.frame.size.width, (_vertical ? _numberOfItems : 1) * self.frame.size.height)]; 272 | 273 | NSInteger previousPageIndex = _currentPageIndex; 274 | if (previousPageIndex < 0) { 275 | previousPageIndex = 0; 276 | 277 | } 278 | 279 | _currentPageIndex = previousPageIndex; 280 | } 281 | 282 | self.currentPageIndex = _currentPageIndex; 283 | if ([_delegate respondsToSelector:@selector(carouselCurrentItemIndexDidChange:currentIndex:previousIndex:)]) { 284 | [_delegate carouselCurrentItemIndexDidChange:self currentIndex:_currentPageIndex previousIndex:_currentPageIndex]; 285 | } 286 | [self scrollToItemAtIndex:_currentPageIndex animated:NO]; 287 | } 288 | 289 | - (void)scrollToItemAtIndex:(NSInteger)index animated:(BOOL)animated 290 | { 291 | if (_numberOfItems > 0 && index < _numberOfItems) 292 | { 293 | [_scrollView setContentOffset:CGPointMake((_vertical ? 0 : index) * self.frame.size.width, (_vertical ? index : 0) * self.frame.size.height) animated:animated]; 294 | } 295 | } 296 | 297 | #pragma mark - UIScrollViewDelegates 298 | 299 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 300 | { 301 | CGFloat nowContentOffset = (_vertical ? scrollView.contentOffset.y : scrollView.contentOffset.x); 302 | 303 | CGFloat pageSize = (_vertical ? scrollView.frame.size.height : scrollView.frame.size.width); 304 | 305 | CGFloat fractionalPage = nowContentOffset / pageSize; 306 | 307 | NSInteger page = floorf(fractionalPage); 308 | 309 | if (_lastContentOffset > nowContentOffset) 310 | { 311 | self.direction = _vertical ? PLCarouselViewDirectionUp : PLCarouselViewDirectionLeft; 312 | } 313 | else if (_lastContentOffset < nowContentOffset) 314 | { 315 | self.direction = _vertical ? PLCarouselViewDirectionDown : PLCarouselViewDirectionRight; 316 | } 317 | 318 | if (_currentPageIndex * pageSize > nowContentOffset) 319 | { 320 | page = ceilf(fractionalPage); 321 | _viewCurrent.frame = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y, self.bounds.size.width, self.bounds.size.height); 322 | } 323 | 324 | _viewNext.frame = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y, self.bounds.size.width, self.bounds.size.height); 325 | 326 | _lastContentOffset = nowContentOffset; 327 | 328 | if (_currentPageIndex != page && page >= 0) { 329 | 330 | _viewPrevious.frame = CGRectMake((_vertical ? 0.0f : (page - 1) * self.bounds.size.width),(_vertical ? (page - 1) * self.bounds.size.height : 0.0f), self.bounds.size.width, self.bounds.size.height); 331 | _viewNext.frame = CGRectMake((_vertical ? 0.0f : page) * self.bounds.size.width, (_vertical ? page : 0.0f) * self.bounds.size.height, self.bounds.size.width, self.bounds.size.height); 332 | _viewCurrent.frame = _viewNext.frame; 333 | 334 | self.currentPageIndex = page; 335 | 336 | } 337 | 338 | if ([_delegate respondsToSelector:@selector(carouselView:didScrollDiffrenceRate:)]) { 339 | [_delegate carouselView:self didScrollDiffrenceRate:fractionalPage]; 340 | } 341 | } 342 | 343 | -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 344 | { 345 | self.direction = PLCarouselViewDirectionStatic; 346 | } 347 | 348 | #pragma mark - UIGestureRecognizerDelegates 349 | 350 | -(void)handleTap:(UITapGestureRecognizer*)gesture 351 | { 352 | CGPoint point = [gesture locationInView:_scrollView]; 353 | 354 | CGFloat nowContentOffset = (_vertical ? point.y : point.x); 355 | 356 | float fractionalPage = nowContentOffset / (_vertical ? self.bounds.size.height : self.bounds.size.width); 357 | 358 | NSInteger didSelectIndex = floorf(fractionalPage); 359 | 360 | if ([_delegate respondsToSelector:@selector(carouselView:didSelectItemAtIndex:)] && didSelectIndex > -1 && didSelectIndex < _numberOfItems) { 361 | [_delegate carouselView:self didSelectItemAtIndex:didSelectIndex]; 362 | } 363 | } 364 | 365 | @end 366 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo/CarouselView/PLCarouselView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // CarouselViewDemo 4 | // 5 | // Created by Anil Oruc on 3/10/16. 6 | // Copyright © 2016 Anil Oruc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // CarouselViewDemo 4 | // 5 | // Created by Anil Oruc on 3/10/16. 6 | // Copyright © 2016 Anil Oruc. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "PLCarouselView.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (nonatomic,strong) NSArray *colors; 15 | 16 | @property (nonatomic,strong) PLCarouselView *carousel; 17 | 18 | @end 19 | 20 | @implementation ViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | // Do any additional setup after loading the view, typically from a nib. 25 | 26 | _colors = @[@[[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor]],@[[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor]],@[[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor]],@[[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor]],@[[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor]]]; 27 | 28 | _carousel = [PLCarouselView init]; 29 | 30 | [_carousel setFrame:[UIScreen mainScreen].bounds]; 31 | 32 | _carousel.delegate = self; 33 | 34 | _carousel.dataSource = self; 35 | 36 | [self.view addSubview:_carousel]; 37 | 38 | [_carousel reloadData]; 39 | } 40 | 41 | - (void)didReceiveMemoryWarning { 42 | [super didReceiveMemoryWarning]; 43 | // Dispose of any resources that can be recreated. 44 | } 45 | 46 | -(UIColor*)randomColor 47 | { 48 | CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0 49 | CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white 50 | CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black 51 | UIColor *color = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1]; 52 | return color; 53 | } 54 | 55 | #pragma mark - PLCarouselView delegates & datasources 56 | 57 | -(UIView *)carouselView:(PLCarouselView *)carouselView viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view 58 | { 59 | if (carouselView == _carousel) { 60 | 61 | PLCarouselView *carouselCategory = (PLCarouselView*)view; 62 | 63 | if (!view) { 64 | carouselCategory = [PLCarouselView init]; 65 | 66 | carouselCategory.delegate = self; 67 | 68 | carouselCategory.dataSource = self; 69 | 70 | [carouselCategory setFrame:[UIScreen mainScreen].bounds]; 71 | 72 | carouselCategory.vertical = YES; 73 | } 74 | 75 | carouselCategory.tag = index; 76 | 77 | [carouselCategory reloadData]; 78 | 79 | return carouselCategory; 80 | } 81 | else { 82 | 83 | UIView *viewTemp = view; 84 | if(!viewTemp) 85 | { 86 | viewTemp = [UIView new]; 87 | } 88 | viewTemp.layer.masksToBounds = NO; 89 | viewTemp.layer.shadowColor = [[UIColor blackColor] CGColor]; 90 | viewTemp.layer.shadowOffset = CGSizeMake(2.0f, 2.0f); 91 | viewTemp.layer.shadowRadius = 3.0f; 92 | viewTemp.layer.shadowOpacity = 1.0f; 93 | 94 | [viewTemp setFrame:[UIScreen mainScreen].bounds]; 95 | 96 | viewTemp.backgroundColor = _colors[carouselView.tag][index]; 97 | 98 | return viewTemp; 99 | } 100 | } 101 | 102 | -(NSUInteger)numberOfItemsInCarousel:(PLCarouselView *)carouselView 103 | { 104 | if (carouselView == _carousel) { 105 | return _colors.count; 106 | } 107 | 108 | return [_colors[carouselView.tag] count]; 109 | } 110 | 111 | -(void)carouselView:(PLCarouselView *)carouselView didMoveToView:(UIView *)view 112 | { 113 | 114 | } 115 | 116 | -(void)carouselCurrentItemIndexDidChange:(PLCarouselView *)carouselView currentIndex:(NSUInteger)currentIndex previousIndex:(NSUInteger)previousIndex 117 | { 118 | 119 | } 120 | 121 | -(void)carouselView:(PLCarouselView *)carouselView didSelectItemAtIndex:(NSUInteger)index 122 | { 123 | 124 | } 125 | 126 | -(void)carouselView:(PLCarouselView *)carouselView changedScrollDirection:(PLCarouselViewDirection)direction 127 | { 128 | switch (direction) { 129 | case PLCarouselViewDirectionDown: 130 | { 131 | [self.navigationController setNavigationBarHidden:YES animated:YES]; 132 | } 133 | break; 134 | case PLCarouselViewDirectionUp: 135 | { 136 | [self.navigationController setNavigationBarHidden:NO animated:YES]; 137 | } 138 | break; 139 | case PLCarouselViewDirectionLeft: 140 | { 141 | [self.navigationController setNavigationBarHidden:NO animated:YES]; 142 | } 143 | break; 144 | case PLCarouselViewDirectionRight: 145 | { 146 | [self.navigationController setNavigationBarHidden:NO animated:YES]; 147 | } 148 | break; 149 | case PLCarouselViewDirectionStatic: 150 | { 151 | 152 | } 153 | break; 154 | default: 155 | break; 156 | } 157 | } 158 | 159 | -(void)carouselView:(PLCarouselView *)carouselView didScrollDiffrenceRate:(CGFloat)diffRate 160 | { 161 | 162 | } 163 | 164 | @end 165 | -------------------------------------------------------------------------------- /CarouselViewDemo/CarouselViewDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CarouselViewDemo 4 | // 5 | // Created by Anil Oruc on 3/10/16. 6 | // Copyright © 2016 Anil Oruc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Anıl ORUÇ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CarouselView 2 | 3 | The component was used in the platinum application : https://itunes.apple.com/tr/app/turkcell-platinum-size-ozel/id671494224?mt=8 4 | 5 | ## Display Visual Example 6 | 7 | ---- 8 | ![Visual1](http://g.recordit.co/elt6VbPnPF.gif) 9 | 10 | 11 | Installation 12 | -------------- 13 | 14 | To use the PLCarouselView class in an app, just drag the PLCarouselView class files (demo files and assets are not needed) into your project and add the QuartzCore framework. 15 | 16 | 17 | Properties 18 | -------------- 19 | 20 | The PLCarouselView has the following properties (note: for iOS, UIView when using properties): 21 | 22 | @property (nonatomic, weak) IBOutlet id dataSource; 23 | 24 | An object that supports the PLCarouselViewDataSource protocol and can provide views to populate the scroll. 25 | 26 | @property (nonatomic, weak) IBOutlet id delegate; 27 | 28 | An object that supports the PLCarouselViewDelegate protocol and can respond to scroll events and layout requests. 29 | 30 | @property (nonatomic, assign) BOOL vertical; 31 | 32 | This property toggles whether the carousel is displayed horizontally or vertically on screen. All the built-in carousel types work in both orientations. Switching to vertical changes both the layout of the carousel and also the direction of swipe detection on screen. Note that custom carousel transforms are not affected by this property, however the swipe gesture direction will still be affected. Defaulth value is NO. 33 | 34 | @property (nonatomic, assign) BOOL scrollEnabled; 35 | 36 | Enables and disables user scrolling of the carousel. The carousel can still be scrolled programmatically if this property is set to NO. 37 | 38 | @property (nonatomic, assign, readonly) NSUInteger currentPageIndex; 39 | 40 | The index of the currently centered item in the carousel. Setting this property is equivalent to calling `scrollToItemAtIndex:animated:` with the animated argument set to NO. 41 | 42 | 43 | Methods 44 | -------------- 45 | 46 | The PLCarouselView class has the following methods (note: for iOS, UIView in method arguments): 47 | 48 | + (instancetype)init; 49 | 50 | Custom initialize method. 51 | 52 | + (instancetype)initWithFrame:(CGRect)frame; 53 | 54 | Custom initialize method. 55 | 56 | - (UIView*)visibleItemWithType:(PLCarouselViewVisibleItem)type; 57 | 58 | Returns the visible item view with the specified PLCarouselViewVisibleItem (PLCarouselViewVisibleItemCurrent, PLCarouselViewVisibleItemNext or PLCarouselViewVisibleItemPrevious). 59 | 60 | - (void)scrollToItemAtIndex:(NSInteger)index animated:(BOOL)animated; 61 | 62 | This will center the carousel on the specified item, either immediately or with a smooth animation. For wrapped carousels, the carousel will automatically determine the shortest (direct or wraparound) distance to scroll. If you need to control the scroll direction, or want to scroll by more than one revolution, use the scrollByNumberOfItems method instead. 63 | 64 | - (void)reloadData; 65 | 66 | This reloads all carousel views from the dataSource and refreshes the carousel display. 67 | 68 | 69 | Protocols 70 | --------------- 71 | 72 | The PLCarouselView follows the Apple convention for data-driven views by providing two protocol interfaces, PLCarouselViewDataSource and PLCarouselViewDelegate. The PLCarouselViewDataSource protocol has the following required methods (note: for iOS, UIView in method arguments): 73 | 74 | -(NSUInteger)numberOfItemsInCarousel:(PLCarouselView *)carouselView; 75 | 76 | Return the number of items (views) in the carousel. 77 | 78 | -(UIView*)carouselView:(PLCarouselView *)carouselView viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view; 79 | 80 | Return a view to be displayed at the specified index in the carousel. The `reusingView` argument works like a UIPickerView, where views that have previously been displayed in the carousel are passed back to the method to be recycled. If this argument is not nil, you can set its properties and return it instead of creating a new view instance, which will slightly improve performance. Unlike UITableView, there is no reuseIdentifier for distinguishing between different carousel view types, so if your carousel contains multiple different view types then you should just ignore this parameter and return a new view each time the method is called. You should ensure that each time the `carouselView:viewForItemAtIndex:reusingView:` method is called, it either returns the reusingView or a brand new view instance rather than maintaining your own pool of recyclable views, as returning multiple copies of the same view for different carousel item indexes may cause display issues with the carousel. 81 | 82 | The PLCarouselViewDelegate protocol has the following optional methods: 83 | 84 | -(void)carouselCurrentItemIndexDidChange:(PLCarouselView *)carouselView currentIndex:(NSUInteger)currentIndex previousIndex:(NSUInteger)previousIndex; 85 | 86 | This method is called whenever the carousel scrolls far enough for the currentPageIndex property to change. It is called regardless of whether the item index was updated programatically or through user interaction. 87 | 88 | -(void)carouselView:(PLCarouselView*)carouselView didSelectItemAtIndex:(NSUInteger)index; 89 | 90 | This method will fire if the user taps any carousel item view, including the currently selected view. This method will not fire if the user taps a control within the currently selected view (i.e. any view that is a subclass of UIControl). 91 | 92 | -(void)carouselView:(PLCarouselView*)carouselView didScrollDiffrenceRate:(CGFloat)diffRate; 93 | 94 | This method is called whenever the carousel scrolls far enough for the contentOffset property to change. It is called regardless of whether the item contentOffset was updated programatically or through user interaction. 95 | 96 | -(void)carouselView:(PLCarouselView*)carouselView didMoveToView:(UIView*)view; 97 | 98 | This method is called whenever the carousel scrolls far enough for the currentPageIndex property to change and the item move into carousel view. It is called regardless of whether the item view was updated programatically or through user interaction. 99 | 100 | -(void)carouselView:(PLCarouselView*)carouselView changedScrollDirection:(PLCarouselViewDirection)direction; 101 | 102 | This method is called whenever the carousel scrolls far enough for the contentOffset property to change. It is called regardless of whether the carousel contentOffset was updated programatically or through user interaction. (PLCarouselViewDirectionStatic, PLCarouselViewDirectionUp, PLCarouselViewDirectionDown, PLCarouselViewDirectionLeft or PLCarouselViewDirectionRight) 103 | 104 | 105 | How to use ? 106 | ---------- 107 | 108 | ```Objective-C 109 | #import "PLCarouselView.h" 110 | 111 | @interface ViewController () 112 | 113 | ... 114 | 115 | - (void)loadView 116 | { 117 | [super loadView]; 118 | 119 | PLCarouselView *_carousel = [PLCarouselView init]; 120 | 121 | [_carousel setFrame:[UIScreen mainScreen].bounds]; 122 | 123 | _carousel.delegate = self; 124 | 125 | _carousel.dataSource = self; 126 | 127 | _carousel.vertical = YES; 128 | 129 | [self.view addSubview:_carousel]; 130 | 131 | [_carousel reloadData]; 132 | 133 | } 134 | 135 | ... 136 | 137 | #pragma mark - PLCarouselView delegates & datasources 138 | 139 | -(UIView *)carouselView:(PLCarouselView *)carouselView viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view 140 | { 141 | if(view) 142 | { 143 | return view; 144 | } 145 | else 146 | { 147 | return [UIView new]; 148 | } 149 | } 150 | 151 | -(NSUInteger)numberOfItemsInCarousel:(PLCarouselView *)carouselView 152 | { 153 | return 10; 154 | } 155 | 156 | -(void)carouselView:(PLCarouselView *)carouselView didMoveToView:(UIView *)view 157 | { 158 | 159 | } 160 | 161 | -(void)carouselCurrentItemIndexDidChange:(PLCarouselView *)carouselView currentIndex:(NSUInteger)currentIndex previousIndex:(NSUInteger)previousIndex 162 | { 163 | 164 | } 165 | 166 | -(void)carouselView:(PLCarouselView *)carouselView didSelectItemAtIndex:(NSUInteger)index 167 | { 168 | 169 | } 170 | 171 | -(void)carouselView:(PLCarouselView *)carouselView changedScrollDirection:(PLCarouselViewDirection)direction 172 | { 173 | switch (direction) { 174 | case PLCarouselViewDirectionDown: 175 | { 176 | [self.navigationController setNavigationBarHidden:YES animated:YES]; 177 | } 178 | break; 179 | case PLCarouselViewDirectionUp: 180 | { 181 | [self.navigationController setNavigationBarHidden:NO animated:YES]; 182 | } 183 | break; 184 | case PLCarouselViewDirectionLeft: 185 | { 186 | [self.navigationController setNavigationBarHidden:NO animated:YES]; 187 | } 188 | break; 189 | case PLCarouselViewDirectionRight: 190 | { 191 | [self.navigationController setNavigationBarHidden:NO animated:YES]; 192 | } 193 | break; 194 | case PLCarouselViewDirectionStatic: 195 | { 196 | 197 | } 198 | break; 199 | default: 200 | break; 201 | } 202 | } 203 | 204 | -(void)carouselView:(PLCarouselView *)carouselView didScrollDiffrenceRate:(CGFloat)diffRate 205 | { 206 | 207 | } 208 | 209 | 210 | ``` 211 | 212 | Build and run the project files. Enjoy more examples! --------------------------------------------------------------------------------