├── .gitignore ├── EndlessTableView ├── EndlessTableView.h └── EndlessTableView.m ├── EndlessTableViewDemo ├── EndlessTableView │ ├── EndlessTableView.h │ └── EndlessTableView.m ├── EndlessTableViewDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── EndlessTableViewDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── EndlessTableView │ ├── EndlessTableView.h │ └── EndlessTableView.m │ ├── EndlessTableViewCell.h │ ├── EndlessTableViewCell.m │ ├── EndlessTableViewCell.xib │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── LICENSE.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | -------------------------------------------------------------------------------- /EndlessTableView/EndlessTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // EndlessTableView.h 3 | // OmsaTech 4 | // 5 | // Created by Anıl Oruç on 06/05/14. 6 | // Copyright (c) 2014 OmsaTech. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface EndlessTableView : UITableView 12 | 13 | @property(nonatomic)BOOL enableEndlessScrolling; 14 | 15 | @property(nonatomic)BOOL enableAutoScrolling; 16 | 17 | @property(nonatomic)CGFloat autoScrollValue; 18 | 19 | @property(nonatomic)CGFloat differenceRateValue; 20 | 21 | @property(nonatomic,weak) EndlessTableView *attachedTableView; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /EndlessTableView/EndlessTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // EndlessTableView.m 3 | // OmsaTech 4 | // 5 | // Created by Anıl Oruç on 06/05/14. 6 | // Copyright (c) 2014 OmsaTech. All rights reserved. 7 | // 8 | 9 | #import "EndlessTableView.h" 10 | #import 11 | 12 | #define AUTO_SCROLLING_DEFAULT_VALUE 1.0f 13 | #define DIFFERENCE_SCROLL_DEFAULT_VALUE 1.0f 14 | 15 | @interface NSTimer (BlocksSupport) 16 | 17 | + (NSTimer*)autoScrolling_scheduledTimerWithTimeInterval:(NSTimeInterval)interval 18 | block:(void(^)())block 19 | repeats:(BOOL)repeats; 20 | @end 21 | 22 | @implementation NSTimer (BlocksSupport) 23 | 24 | + (NSTimer*)autoScrolling_scheduledTimerWithTimeInterval:(NSTimeInterval)interval 25 | block:(void(^)())block 26 | repeats:(BOOL)repeats 27 | { 28 | 29 | return [self scheduledTimerWithTimeInterval:interval 30 | target:self 31 | selector:@selector(autoScrolling_blockInvoke:) 32 | userInfo:[block copy] 33 | repeats:repeats]; 34 | } 35 | 36 | + (void)autoScrolling_blockInvoke:(NSTimer*)timer { 37 | void (^block)() = timer.userInfo; 38 | if (block) { 39 | block(); 40 | } 41 | } 42 | 43 | @end 44 | 45 | @interface EndlessTableViewDelegate : NSObject 46 | 47 | @property (nonatomic, weak) id receiver; 48 | @property (nonatomic, weak) id middleMan; 49 | 50 | @end 51 | 52 | @implementation EndlessTableViewDelegate 53 | 54 | - (id) forwardingTargetForSelector:(SEL)aSelector { 55 | 56 | if ([_middleMan respondsToSelector:aSelector]) 57 | return _middleMan; 58 | 59 | if ([_receiver respondsToSelector:aSelector]) 60 | return _receiver; 61 | 62 | return [super forwardingTargetForSelector:aSelector]; 63 | 64 | } 65 | 66 | - (BOOL) respondsToSelector:(SEL)aSelector { 67 | 68 | BOOL autoScrollingMethodControl = (sel_isEqual(aSelector, @selector(scrollViewDidEndDragging:willDecelerate:)) || sel_isEqual(aSelector, @selector(scrollViewWillBeginDragging:)) || sel_isEqual(aSelector, @selector(scrollViewDidEndDecelerating:)) || sel_isEqual(aSelector, @selector(scrollViewWillBeginDecelerating:)) || sel_isEqual(aSelector, @selector(scrollViewDidScroll:))); 69 | 70 | if (autoScrollingMethodControl) { 71 | if ([_middleMan respondsToSelector:aSelector]) 72 | return YES; 73 | } 74 | 75 | if ([_receiver respondsToSelector:aSelector]) 76 | return YES; 77 | 78 | return [super respondsToSelector:aSelector]; 79 | 80 | } 81 | 82 | @end 83 | 84 | @interface EndlessTableView () 85 | 86 | @property (nonatomic,strong) EndlessTableViewDelegate *dataSourceEndless,*delegateEndless; 87 | @property (nonatomic) NSInteger totalRows,totalCellsVisible; 88 | @property (nonatomic) CGFloat lastContentOffsetY; 89 | @property (nonatomic,strong) NSTimer* timerAutoScrolling; 90 | @property (nonatomic,readonly) BOOL isScrolling; 91 | 92 | @end 93 | 94 | @implementation EndlessTableView 95 | 96 | #pragma mark Initialization 97 | - (id)initWithCoder:(NSCoder *)aDecoder 98 | { 99 | self = [super initWithCoder:aDecoder]; 100 | if( self ) 101 | { 102 | [self customIntitialization]; 103 | } 104 | return self; 105 | } 106 | 107 | - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style 108 | { 109 | self = [super initWithFrame:frame style:style]; 110 | if( self ) 111 | { 112 | [self customIntitialization]; 113 | } 114 | return self; 115 | } 116 | 117 | - (id)initWithFrame:(CGRect)frame 118 | { 119 | self = [super initWithFrame:frame]; 120 | if (self) 121 | { 122 | [self customIntitialization]; 123 | } 124 | return self; 125 | } 126 | 127 | -(void)dealloc 128 | { 129 | [self stopAutoScrolling]; 130 | } 131 | 132 | - (void)customIntitialization 133 | { 134 | _enableEndlessScrolling = YES; 135 | _autoScrollValue = 0.0f; 136 | _differenceRateValue = DIFFERENCE_SCROLL_DEFAULT_VALUE; 137 | } 138 | 139 | - (NSIndexPath*)editIndexPathForIndexPath:(NSIndexPath*)oldIndexPath totalRows:(NSInteger)totalRows 140 | { 141 | return _enableEndlessScrolling ? [NSIndexPath indexPathForRow:oldIndexPath.row % totalRows inSection:oldIndexPath.section] : oldIndexPath; 142 | } 143 | 144 | - (void)resetContentOffsetIfNeeded 145 | { 146 | if( !_enableEndlessScrolling ) 147 | return; 148 | 149 | NSArray *indexpaths = [self indexPathsForVisibleRows]; 150 | int totalVisibleCells = [indexpaths count]; 151 | if( _totalCellsVisible > totalVisibleCells ) 152 | { 153 | //we dont have enough content to generate scroll 154 | return; 155 | } 156 | CGPoint contentOffset = self.contentOffset; 157 | BOOL control = NO; 158 | //check the top condition 159 | //check if the scroll view reached its top.. if so.. move it to center.. remember center is the start of the data repeating for 2nd time. 160 | if( contentOffset.y <= 0.0 ) 161 | { 162 | control = YES; 163 | contentOffset.y = self.contentSize.height/3.0f; 164 | } 165 | else if( contentOffset.y >= ( self.contentSize.height - self.bounds.size.height) )//scrollview content offset reached bottom minus the height of the tableview 166 | { 167 | control = YES; 168 | //this scenario is same as the data repeating for 2nd time minus the height of the table view 169 | contentOffset.y = self.contentSize.height/3.0f- self.bounds.size.height; 170 | } 171 | 172 | if (control) { 173 | /* 174 | [_attachedTableView stopAutoScrolling]; 175 | [self stopAutoScrolling]; 176 | */ 177 | _lastContentOffsetY = contentOffset.y; 178 | [self setContentOffset:contentOffset]; 179 | /* 180 | if (_attachedTableView.enableAutoScrolling) { 181 | [_attachedTableView startAutoScrollingWithDelay:NO]; 182 | } 183 | else if(self.enableAutoScrolling){ 184 | [self startAutoScrollingWithDelay:NO]; 185 | } 186 | */ 187 | } 188 | } 189 | 190 | -(void)startAutoScrollingWithDelay:(BOOL)isDelay 191 | { 192 | double delayInSeconds = (isDelay ? 0.15f : 0.0f); 193 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 194 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 195 | if (!_timerAutoScrolling) 196 | { 197 | __weak id weakSelf = self; 198 | _timerAutoScrolling = [NSTimer autoScrolling_scheduledTimerWithTimeInterval:0.05 block:^{ 199 | EndlessTableView * strongSelf = weakSelf; 200 | [strongSelf autoScrollingAction]; 201 | } repeats:YES]; 202 | 203 | [[NSRunLoop mainRunLoop] addTimer:_timerAutoScrolling forMode:UITrackingRunLoopMode]; 204 | } 205 | }); 206 | } 207 | 208 | -(void)stopAutoScrolling 209 | { 210 | //[self.layer removeAllAnimations]; 211 | [_timerAutoScrolling invalidate]; 212 | _timerAutoScrolling = nil; 213 | } 214 | 215 | -(void)autoScrollingAction 216 | { 217 | [UIView animateWithDuration:0.05 delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{ 218 | 219 | [self setContentOffset:CGPointMake(self.contentOffset.x, self.contentOffset.y + (_autoScrollValue <= 0.0f ? AUTO_SCROLLING_DEFAULT_VALUE : _autoScrollValue))]; 220 | 221 | } completion:nil]; 222 | 223 | } 224 | 225 | #pragma mark Layout 226 | 227 | - (void)layoutSubviews 228 | { 229 | _totalCellsVisible = self.frame.size.height / self.rowHeight; 230 | [self resetContentOffsetIfNeeded]; 231 | [super layoutSubviews]; 232 | } 233 | 234 | #pragma mark Setter/Getter 235 | 236 | -(void)reloadData{ 237 | [super reloadData]; 238 | if (_enableAutoScrolling) { 239 | [self startAutoScrollingWithDelay:YES]; 240 | } 241 | } 242 | 243 | -(void)setDifferenceRateValue:(CGFloat)differenceRateValue 244 | { 245 | _differenceRateValue = differenceRateValue; 246 | if(_attachedTableView) { 247 | [_attachedTableView setPrivateDifferenceRateValue:1/_differenceRateValue]; 248 | [_attachedTableView reloadData]; 249 | } 250 | } 251 | 252 | 253 | -(void)setPrivateDifferenceRateValue:(CGFloat)differenceRateValue 254 | { 255 | _differenceRateValue = differenceRateValue; 256 | 257 | } 258 | 259 | -(void)setEnableEndlessScrolling:(BOOL)enableEndlessScrolling 260 | { 261 | _enableEndlessScrolling = enableEndlessScrolling; 262 | 263 | [self reloadData]; 264 | } 265 | 266 | -(void)setEnableAutoScrolling:(BOOL)enableAutoScrolling 267 | { 268 | _enableAutoScrolling = enableAutoScrolling; 269 | 270 | if (_enableAutoScrolling) { 271 | [self startAutoScrollingWithDelay:YES]; 272 | } 273 | else { 274 | [self stopAutoScrolling]; 275 | } 276 | } 277 | 278 | -(void)setAttachedTableView:(EndlessTableView *)attachedTableView 279 | { 280 | _attachedTableView = attachedTableView; 281 | [_attachedTableView setPrivateAttachedTableView:self]; 282 | } 283 | 284 | -(void)setPrivateAttachedTableView:(EndlessTableView *)attachedTableView 285 | { 286 | _attachedTableView = attachedTableView; 287 | } 288 | 289 | - (void)setDataSource:(id)dataSource 290 | { 291 | if( !_dataSourceEndless) 292 | { 293 | _dataSourceEndless = [[EndlessTableViewDelegate alloc] init]; 294 | } 295 | 296 | _dataSourceEndless.receiver = dataSource; 297 | _dataSourceEndless.middleMan = self; 298 | 299 | [super setDataSource:(id)_dataSourceEndless]; 300 | } 301 | 302 | -(void)setDelegate:(id)delegate 303 | { 304 | if (delegate == nil) { 305 | [super setDelegate:delegate]; 306 | return; 307 | } 308 | if( !_delegateEndless) 309 | { 310 | _delegateEndless = [[EndlessTableViewDelegate alloc] init]; 311 | } 312 | 313 | _delegateEndless.receiver = delegate; 314 | _delegateEndless.middleMan = self; 315 | 316 | [super setDelegate:(id)_delegateEndless]; 317 | } 318 | 319 | /* 320 | // Only override drawRect: if you perform custom drawing. 321 | // An empty implementation adversely affects performance during animation. 322 | - (void)drawRect:(CGRect)rect 323 | { 324 | // Drawing code 325 | } 326 | */ 327 | 328 | #pragma mark - UIScrollViewDelegates 329 | 330 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView; 331 | { 332 | if ([self numberOfRowsInSection:0] > 0 && [_attachedTableView numberOfRowsInSection:0] > 0 && !_attachedTableView.isScrolling && !_attachedTableView.timerAutoScrolling.isValid){ 333 | 334 | CGFloat differenceRate = (_attachedTableView.contentSize.height / scrollView.contentSize.height); 335 | 336 | CGFloat diffY = scrollView.contentOffset.y - _lastContentOffsetY; 337 | 338 | if (0.8f < differenceRate < 1.2f) { 339 | differenceRate = differenceRate * _differenceRateValue; 340 | } 341 | 342 | CGFloat value = _attachedTableView.contentOffset.y + (diffY * differenceRate); 343 | 344 | if (_attachedTableView.contentSize.height >= value) { 345 | 346 | _lastContentOffsetY = scrollView.contentOffset.y; 347 | 348 | _attachedTableView.lastContentOffsetY = value; 349 | 350 | [_attachedTableView setContentOffset:CGPointMake(_attachedTableView.contentOffset.x, value)]; 351 | } 352 | 353 | } 354 | 355 | if ([_delegateEndless.receiver respondsToSelector:@selector(scrollViewDidScroll:)]) { 356 | return [_delegateEndless.receiver scrollViewDidScroll:scrollView]; 357 | } 358 | } 359 | 360 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 361 | { 362 | if (_enableAutoScrolling && !decelerate) { 363 | [self startAutoScrollingWithDelay:YES]; 364 | } 365 | 366 | _isScrolling = NO; 367 | 368 | if (_attachedTableView.enableAutoScrolling && !decelerate && !_attachedTableView.isDragging) 369 | { 370 | [_attachedTableView startAutoScrollingWithDelay:YES]; 371 | } 372 | 373 | if ([_delegateEndless.receiver respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) { 374 | return [_delegateEndless.receiver scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; 375 | } 376 | } 377 | 378 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 379 | { 380 | if (_enableAutoScrolling) { 381 | [self stopAutoScrolling]; 382 | } 383 | 384 | _isScrolling = YES; 385 | 386 | if (_attachedTableView.enableAutoScrolling) 387 | { 388 | [_attachedTableView stopAutoScrolling]; 389 | } 390 | 391 | if ([_delegateEndless.receiver respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { 392 | return [_delegateEndless.receiver scrollViewWillBeginDragging:scrollView]; 393 | } 394 | } 395 | 396 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 397 | { 398 | if (_enableAutoScrolling) { 399 | [self startAutoScrollingWithDelay:YES]; 400 | } 401 | 402 | _isScrolling = NO; 403 | 404 | if (_attachedTableView.enableAutoScrolling) { 405 | [_attachedTableView startAutoScrollingWithDelay:YES]; 406 | } 407 | 408 | if ([_delegateEndless.receiver respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { 409 | return [_delegateEndless.receiver scrollViewDidEndDecelerating:scrollView]; 410 | } 411 | } 412 | 413 | 414 | 415 | #pragma mark - UITableViewDataSource 416 | - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section 417 | { 418 | _totalRows = [_dataSourceEndless.receiver tableView:tableView numberOfRowsInSection:section]; 419 | 420 | return _totalRows * ( _enableEndlessScrolling ? 3 : 1 ); 421 | } 422 | 423 | - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 424 | { 425 | return [_dataSourceEndless.receiver tableView:tableView cellForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 426 | } 427 | 428 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath 429 | { 430 | return [_dataSourceEndless.receiver tableView:tableView canEditRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 431 | } 432 | 433 | - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath 434 | { 435 | return [_dataSourceEndless.receiver tableView:tableView canMoveRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 436 | } 437 | 438 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 439 | { 440 | return [_dataSourceEndless.receiver tableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 441 | } 442 | 443 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath 444 | { 445 | return [_dataSourceEndless.receiver tableView:tableView moveRowAtIndexPath:[self editIndexPathForIndexPath:sourceIndexPath totalRows:_totalRows] toIndexPath:[self editIndexPathForIndexPath:destinationIndexPath totalRows:_totalRows]]; 446 | } 447 | 448 | #pragma mark - UITableViewDelegate 449 | 450 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath 451 | { 452 | return [_delegateEndless.receiver tableView:tableView willDisplayCell:cell forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 453 | } 454 | 455 | - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath 456 | { 457 | return [_delegateEndless.receiver tableView:tableView didEndDisplayingCell:cell forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 458 | } 459 | 460 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 461 | { 462 | return [_delegateEndless.receiver tableView:tableView heightForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 463 | } 464 | 465 | - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath 466 | { 467 | return [_delegateEndless.receiver tableView:tableView estimatedHeightForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 468 | } 469 | 470 | - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath 471 | { 472 | return [_delegateEndless.receiver tableView:tableView accessoryButtonTappedForRowWithIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 473 | } 474 | 475 | - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath 476 | { 477 | return [_delegateEndless.receiver tableView:tableView shouldHighlightRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 478 | } 479 | 480 | - (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath 481 | { 482 | return [_delegateEndless.receiver tableView:tableView didHighlightRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 483 | } 484 | 485 | - (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath 486 | { 487 | return [_delegateEndless.receiver tableView:tableView didUnhighlightRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 488 | } 489 | 490 | - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath 491 | { 492 | return [_delegateEndless.receiver tableView:tableView willSelectRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 493 | } 494 | 495 | - (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath 496 | { 497 | return [_delegateEndless.receiver tableView:tableView willDeselectRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 498 | } 499 | 500 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 501 | { 502 | return [_delegateEndless.receiver tableView:tableView didSelectRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 503 | } 504 | 505 | - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath 506 | { 507 | return [_delegateEndless.receiver tableView:tableView didDeselectRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 508 | } 509 | 510 | - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath 511 | { 512 | return [_delegateEndless.receiver tableView:tableView editingStyleForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 513 | } 514 | 515 | - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath 516 | { 517 | return [_delegateEndless.receiver tableView:tableView titleForDeleteConfirmationButtonForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 518 | } 519 | 520 | - (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath 521 | { 522 | return [_delegateEndless.receiver tableView:tableView shouldIndentWhileEditingRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 523 | } 524 | 525 | - (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath 526 | { 527 | return [_delegateEndless.receiver tableView:tableView willBeginEditingRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 528 | } 529 | 530 | - (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath 531 | { 532 | return [_delegateEndless.receiver tableView:tableView didEndEditingRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 533 | } 534 | 535 | - (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath 536 | { 537 | return [_delegateEndless.receiver tableView:tableView targetIndexPathForMoveFromRowAtIndexPath:[self editIndexPathForIndexPath:sourceIndexPath totalRows:_totalRows] toProposedIndexPath:[self editIndexPathForIndexPath:proposedDestinationIndexPath totalRows:_totalRows]]; 538 | } 539 | 540 | - (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath 541 | { 542 | return [_delegateEndless.receiver tableView:tableView indentationLevelForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 543 | } 544 | 545 | - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath 546 | { 547 | return [_delegateEndless.receiver tableView:tableView shouldShowMenuForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 548 | } 549 | 550 | - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender 551 | { 552 | return [_delegateEndless.receiver tableView:tableView canPerformAction:action forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows] withSender:sender]; 553 | } 554 | 555 | - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender 556 | { 557 | return [_delegateEndless.receiver tableView:tableView performAction:action forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows] withSender:sender]; 558 | } 559 | 560 | 561 | @end 562 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableView/EndlessTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // EndlessTableView.h 3 | // OmsaTech 4 | // 5 | // Created by Anıl Oruç on 06/05/14. 6 | // Copyright (c) 2014 OmsaTech. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface EndlessTableView : UITableView 12 | 13 | @property(nonatomic)BOOL enableEndlessScrolling; 14 | 15 | @property(nonatomic)BOOL enableAutoScrolling; 16 | 17 | @property(nonatomic)CGFloat autoScrollValue; 18 | 19 | @property(nonatomic)CGFloat differenceRateValue; 20 | 21 | @property(nonatomic,weak) EndlessTableView *attachedTableView; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableView/EndlessTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // EndlessTableView.m 3 | // OmsaTech 4 | // 5 | // Created by Anıl Oruç on 06/05/14. 6 | // Copyright (c) 2014 OmsaTech. All rights reserved. 7 | // 8 | 9 | #import "EndlessTableView.h" 10 | #import 11 | 12 | #define AUTO_SCROLLING_DEFAULT_VALUE 1.0f 13 | #define DIFFERENCE_SCROLL_DEFAULT_VALUE 1.0f 14 | 15 | @interface NSTimer (BlocksSupport) 16 | 17 | + (NSTimer*)autoScrolling_scheduledTimerWithTimeInterval:(NSTimeInterval)interval 18 | block:(void(^)())block 19 | repeats:(BOOL)repeats; 20 | @end 21 | 22 | @implementation NSTimer (BlocksSupport) 23 | 24 | + (NSTimer*)autoScrolling_scheduledTimerWithTimeInterval:(NSTimeInterval)interval 25 | block:(void(^)())block 26 | repeats:(BOOL)repeats 27 | { 28 | 29 | return [self scheduledTimerWithTimeInterval:interval 30 | target:self 31 | selector:@selector(autoScrolling_blockInvoke:) 32 | userInfo:[block copy] 33 | repeats:repeats]; 34 | } 35 | 36 | + (void)autoScrolling_blockInvoke:(NSTimer*)timer { 37 | void (^block)() = timer.userInfo; 38 | if (block) { 39 | block(); 40 | } 41 | } 42 | 43 | @end 44 | 45 | @interface EndlessTableViewDelegate : NSObject 46 | 47 | @property (nonatomic, weak) id receiver; 48 | @property (nonatomic, weak) id middleMan; 49 | 50 | @end 51 | 52 | @implementation EndlessTableViewDelegate 53 | 54 | - (id) forwardingTargetForSelector:(SEL)aSelector { 55 | 56 | if ([_middleMan respondsToSelector:aSelector]) 57 | return _middleMan; 58 | 59 | if ([_receiver respondsToSelector:aSelector]) 60 | return _receiver; 61 | 62 | return [super forwardingTargetForSelector:aSelector]; 63 | 64 | } 65 | 66 | - (BOOL) respondsToSelector:(SEL)aSelector { 67 | 68 | BOOL autoScrollingMethodControl = (sel_isEqual(aSelector, @selector(scrollViewDidEndDragging:willDecelerate:)) || sel_isEqual(aSelector, @selector(scrollViewWillBeginDragging:)) || sel_isEqual(aSelector, @selector(scrollViewDidEndDecelerating:)) || sel_isEqual(aSelector, @selector(scrollViewWillBeginDecelerating:)) || sel_isEqual(aSelector, @selector(scrollViewDidScroll:))); 69 | 70 | if (autoScrollingMethodControl) { 71 | if ([_middleMan respondsToSelector:aSelector]) 72 | return YES; 73 | } 74 | 75 | if ([_receiver respondsToSelector:aSelector]) 76 | return YES; 77 | 78 | return [super respondsToSelector:aSelector]; 79 | 80 | } 81 | 82 | @end 83 | 84 | @interface EndlessTableView () 85 | 86 | @property (nonatomic,strong) EndlessTableViewDelegate *dataSourceEndless,*delegateEndless; 87 | @property (nonatomic) NSInteger totalRows,totalCellsVisible; 88 | @property (nonatomic) CGFloat lastContentOffsetY; 89 | @property (nonatomic,strong) NSTimer* timerAutoScrolling; 90 | @property (nonatomic,readonly) BOOL isScrolling; 91 | 92 | @end 93 | 94 | @implementation EndlessTableView 95 | 96 | #pragma mark Initialization 97 | - (id)initWithCoder:(NSCoder *)aDecoder 98 | { 99 | self = [super initWithCoder:aDecoder]; 100 | if( self ) 101 | { 102 | [self customIntitialization]; 103 | } 104 | return self; 105 | } 106 | 107 | - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style 108 | { 109 | self = [super initWithFrame:frame style:style]; 110 | if( self ) 111 | { 112 | [self customIntitialization]; 113 | } 114 | return self; 115 | } 116 | 117 | - (id)initWithFrame:(CGRect)frame 118 | { 119 | self = [super initWithFrame:frame]; 120 | if (self) 121 | { 122 | [self customIntitialization]; 123 | } 124 | return self; 125 | } 126 | 127 | -(void)dealloc 128 | { 129 | [self stopAutoScrolling]; 130 | } 131 | 132 | - (void)customIntitialization 133 | { 134 | _enableEndlessScrolling = YES; 135 | _autoScrollValue = 0.0f; 136 | _differenceRateValue = DIFFERENCE_SCROLL_DEFAULT_VALUE; 137 | } 138 | 139 | - (NSIndexPath*)editIndexPathForIndexPath:(NSIndexPath*)oldIndexPath totalRows:(NSInteger)totalRows 140 | { 141 | return _enableEndlessScrolling ? [NSIndexPath indexPathForRow:oldIndexPath.row % totalRows inSection:oldIndexPath.section] : oldIndexPath; 142 | } 143 | 144 | - (void)resetContentOffsetIfNeeded 145 | { 146 | if( !_enableEndlessScrolling ) 147 | return; 148 | 149 | NSArray *indexpaths = [self indexPathsForVisibleRows]; 150 | int totalVisibleCells = [indexpaths count]; 151 | if( _totalCellsVisible > totalVisibleCells ) 152 | { 153 | //we dont have enough content to generate scroll 154 | return; 155 | } 156 | CGPoint contentOffset = self.contentOffset; 157 | BOOL control = NO; 158 | //check the top condition 159 | //check if the scroll view reached its top.. if so.. move it to center.. remember center is the start of the data repeating for 2nd time. 160 | if( contentOffset.y <= 0.0 ) 161 | { 162 | control = YES; 163 | contentOffset.y = self.contentSize.height/3.0f; 164 | } 165 | else if( contentOffset.y >= ( self.contentSize.height - self.bounds.size.height) )//scrollview content offset reached bottom minus the height of the tableview 166 | { 167 | control = YES; 168 | //this scenario is same as the data repeating for 2nd time minus the height of the table view 169 | contentOffset.y = self.contentSize.height/3.0f- self.bounds.size.height; 170 | } 171 | 172 | if (control) { 173 | /* 174 | [_attachedTableView stopAutoScrolling]; 175 | [self stopAutoScrolling]; 176 | */ 177 | _lastContentOffsetY = contentOffset.y; 178 | [self setContentOffset:contentOffset]; 179 | /* 180 | if (_attachedTableView.enableAutoScrolling) { 181 | [_attachedTableView startAutoScrollingWithDelay:NO]; 182 | } 183 | else if(self.enableAutoScrolling){ 184 | [self startAutoScrollingWithDelay:NO]; 185 | } 186 | */ 187 | } 188 | } 189 | 190 | -(void)startAutoScrollingWithDelay:(BOOL)isDelay 191 | { 192 | double delayInSeconds = (isDelay ? 0.15f : 0.0f); 193 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 194 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 195 | if (!_timerAutoScrolling) 196 | { 197 | __weak id weakSelf = self; 198 | _timerAutoScrolling = [NSTimer autoScrolling_scheduledTimerWithTimeInterval:0.05 block:^{ 199 | EndlessTableView * strongSelf = weakSelf; 200 | [strongSelf autoScrollingAction]; 201 | } repeats:YES]; 202 | 203 | [[NSRunLoop mainRunLoop] addTimer:_timerAutoScrolling forMode:UITrackingRunLoopMode]; 204 | } 205 | }); 206 | } 207 | 208 | -(void)stopAutoScrolling 209 | { 210 | //[self.layer removeAllAnimations]; 211 | [_timerAutoScrolling invalidate]; 212 | _timerAutoScrolling = nil; 213 | } 214 | 215 | -(void)autoScrollingAction 216 | { 217 | [UIView animateWithDuration:0.05 delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{ 218 | 219 | [self setContentOffset:CGPointMake(self.contentOffset.x, self.contentOffset.y + (_autoScrollValue <= 0.0f ? AUTO_SCROLLING_DEFAULT_VALUE : _autoScrollValue))]; 220 | 221 | } completion:nil]; 222 | 223 | } 224 | 225 | #pragma mark Layout 226 | 227 | - (void)layoutSubviews 228 | { 229 | _totalCellsVisible = self.frame.size.height / self.rowHeight; 230 | [self resetContentOffsetIfNeeded]; 231 | [super layoutSubviews]; 232 | } 233 | 234 | #pragma mark Setter/Getter 235 | 236 | -(void)reloadData{ 237 | [super reloadData]; 238 | if (_enableAutoScrolling) { 239 | [self startAutoScrollingWithDelay:YES]; 240 | } 241 | } 242 | 243 | -(void)setDifferenceRateValue:(CGFloat)differenceRateValue 244 | { 245 | _differenceRateValue = differenceRateValue; 246 | if(_attachedTableView) { 247 | [_attachedTableView setPrivateDifferenceRateValue:1/_differenceRateValue]; 248 | [_attachedTableView reloadData]; 249 | } 250 | } 251 | 252 | 253 | -(void)setPrivateDifferenceRateValue:(CGFloat)differenceRateValue 254 | { 255 | _differenceRateValue = differenceRateValue; 256 | 257 | } 258 | 259 | -(void)setEnableEndlessScrolling:(BOOL)enableEndlessScrolling 260 | { 261 | _enableEndlessScrolling = enableEndlessScrolling; 262 | 263 | [self reloadData]; 264 | } 265 | 266 | -(void)setEnableAutoScrolling:(BOOL)enableAutoScrolling 267 | { 268 | _enableAutoScrolling = enableAutoScrolling; 269 | 270 | if (_enableAutoScrolling) { 271 | [self startAutoScrollingWithDelay:YES]; 272 | } 273 | else { 274 | [self stopAutoScrolling]; 275 | } 276 | } 277 | 278 | -(void)setAttachedTableView:(EndlessTableView *)attachedTableView 279 | { 280 | _attachedTableView = attachedTableView; 281 | [_attachedTableView setPrivateAttachedTableView:self]; 282 | } 283 | 284 | -(void)setPrivateAttachedTableView:(EndlessTableView *)attachedTableView 285 | { 286 | _attachedTableView = attachedTableView; 287 | } 288 | 289 | - (void)setDataSource:(id)dataSource 290 | { 291 | if( !_dataSourceEndless) 292 | { 293 | _dataSourceEndless = [[EndlessTableViewDelegate alloc] init]; 294 | } 295 | 296 | _dataSourceEndless.receiver = dataSource; 297 | _dataSourceEndless.middleMan = self; 298 | 299 | [super setDataSource:(id)_dataSourceEndless]; 300 | } 301 | 302 | -(void)setDelegate:(id)delegate 303 | { 304 | if (delegate == nil) { 305 | [super setDelegate:delegate]; 306 | return; 307 | } 308 | if( !_delegateEndless) 309 | { 310 | _delegateEndless = [[EndlessTableViewDelegate alloc] init]; 311 | } 312 | 313 | _delegateEndless.receiver = delegate; 314 | _delegateEndless.middleMan = self; 315 | 316 | [super setDelegate:(id)_delegateEndless]; 317 | } 318 | 319 | /* 320 | // Only override drawRect: if you perform custom drawing. 321 | // An empty implementation adversely affects performance during animation. 322 | - (void)drawRect:(CGRect)rect 323 | { 324 | // Drawing code 325 | } 326 | */ 327 | 328 | #pragma mark - UIScrollViewDelegates 329 | 330 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView; 331 | { 332 | if ([self numberOfRowsInSection:0] > 0 && [_attachedTableView numberOfRowsInSection:0] > 0 && !_attachedTableView.isScrolling && !_attachedTableView.timerAutoScrolling.isValid){ 333 | 334 | CGFloat differenceRate = (_attachedTableView.contentSize.height / scrollView.contentSize.height); 335 | 336 | CGFloat diffY = scrollView.contentOffset.y - _lastContentOffsetY; 337 | 338 | if (0.8f < differenceRate < 1.2f) { 339 | differenceRate = differenceRate * _differenceRateValue; 340 | } 341 | 342 | CGFloat value = _attachedTableView.contentOffset.y + (diffY * differenceRate); 343 | 344 | if (_attachedTableView.contentSize.height >= value) { 345 | 346 | _lastContentOffsetY = scrollView.contentOffset.y; 347 | 348 | _attachedTableView.lastContentOffsetY = value; 349 | 350 | [_attachedTableView setContentOffset:CGPointMake(_attachedTableView.contentOffset.x, value)]; 351 | } 352 | 353 | } 354 | 355 | if ([_delegateEndless.receiver respondsToSelector:@selector(scrollViewDidScroll:)]) { 356 | return [_delegateEndless.receiver scrollViewDidScroll:scrollView]; 357 | } 358 | } 359 | 360 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 361 | { 362 | if (_enableAutoScrolling && !decelerate) { 363 | [self startAutoScrollingWithDelay:YES]; 364 | } 365 | 366 | _isScrolling = NO; 367 | 368 | if (_attachedTableView.enableAutoScrolling && !decelerate && !_attachedTableView.isDragging) 369 | { 370 | [_attachedTableView startAutoScrollingWithDelay:YES]; 371 | } 372 | 373 | if ([_delegateEndless.receiver respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) { 374 | return [_delegateEndless.receiver scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; 375 | } 376 | } 377 | 378 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 379 | { 380 | if (_enableAutoScrolling) { 381 | [self stopAutoScrolling]; 382 | } 383 | 384 | _isScrolling = YES; 385 | 386 | if (_attachedTableView.enableAutoScrolling) 387 | { 388 | [_attachedTableView stopAutoScrolling]; 389 | } 390 | 391 | if ([_delegateEndless.receiver respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { 392 | return [_delegateEndless.receiver scrollViewWillBeginDragging:scrollView]; 393 | } 394 | } 395 | 396 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 397 | { 398 | if (_enableAutoScrolling) { 399 | [self startAutoScrollingWithDelay:YES]; 400 | } 401 | 402 | _isScrolling = NO; 403 | 404 | if (_attachedTableView.enableAutoScrolling) { 405 | [_attachedTableView startAutoScrollingWithDelay:YES]; 406 | } 407 | 408 | if ([_delegateEndless.receiver respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { 409 | return [_delegateEndless.receiver scrollViewDidEndDecelerating:scrollView]; 410 | } 411 | } 412 | 413 | 414 | 415 | #pragma mark - UITableViewDataSource 416 | - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section 417 | { 418 | _totalRows = [_dataSourceEndless.receiver tableView:tableView numberOfRowsInSection:section]; 419 | 420 | return _totalRows * ( _enableEndlessScrolling ? 3 : 1 ); 421 | } 422 | 423 | - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 424 | { 425 | return [_dataSourceEndless.receiver tableView:tableView cellForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 426 | } 427 | 428 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath 429 | { 430 | return [_dataSourceEndless.receiver tableView:tableView canEditRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 431 | } 432 | 433 | - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath 434 | { 435 | return [_dataSourceEndless.receiver tableView:tableView canMoveRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 436 | } 437 | 438 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 439 | { 440 | return [_dataSourceEndless.receiver tableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 441 | } 442 | 443 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath 444 | { 445 | return [_dataSourceEndless.receiver tableView:tableView moveRowAtIndexPath:[self editIndexPathForIndexPath:sourceIndexPath totalRows:_totalRows] toIndexPath:[self editIndexPathForIndexPath:destinationIndexPath totalRows:_totalRows]]; 446 | } 447 | 448 | #pragma mark - UITableViewDelegate 449 | 450 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath 451 | { 452 | return [_delegateEndless.receiver tableView:tableView willDisplayCell:cell forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 453 | } 454 | 455 | - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath 456 | { 457 | return [_delegateEndless.receiver tableView:tableView didEndDisplayingCell:cell forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 458 | } 459 | 460 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 461 | { 462 | return [_delegateEndless.receiver tableView:tableView heightForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 463 | } 464 | 465 | - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath 466 | { 467 | return [_delegateEndless.receiver tableView:tableView estimatedHeightForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 468 | } 469 | 470 | - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath 471 | { 472 | return [_delegateEndless.receiver tableView:tableView accessoryButtonTappedForRowWithIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 473 | } 474 | 475 | - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath 476 | { 477 | return [_delegateEndless.receiver tableView:tableView shouldHighlightRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 478 | } 479 | 480 | - (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath 481 | { 482 | return [_delegateEndless.receiver tableView:tableView didHighlightRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 483 | } 484 | 485 | - (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath 486 | { 487 | return [_delegateEndless.receiver tableView:tableView didUnhighlightRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 488 | } 489 | 490 | - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath 491 | { 492 | return [_delegateEndless.receiver tableView:tableView willSelectRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 493 | } 494 | 495 | - (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath 496 | { 497 | return [_delegateEndless.receiver tableView:tableView willDeselectRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 498 | } 499 | 500 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 501 | { 502 | return [_delegateEndless.receiver tableView:tableView didSelectRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 503 | } 504 | 505 | - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath 506 | { 507 | return [_delegateEndless.receiver tableView:tableView didDeselectRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 508 | } 509 | 510 | - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath 511 | { 512 | return [_delegateEndless.receiver tableView:tableView editingStyleForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 513 | } 514 | 515 | - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath 516 | { 517 | return [_delegateEndless.receiver tableView:tableView titleForDeleteConfirmationButtonForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 518 | } 519 | 520 | - (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath 521 | { 522 | return [_delegateEndless.receiver tableView:tableView shouldIndentWhileEditingRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 523 | } 524 | 525 | - (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath 526 | { 527 | return [_delegateEndless.receiver tableView:tableView willBeginEditingRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 528 | } 529 | 530 | - (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath 531 | { 532 | return [_delegateEndless.receiver tableView:tableView didEndEditingRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 533 | } 534 | 535 | - (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath 536 | { 537 | return [_delegateEndless.receiver tableView:tableView targetIndexPathForMoveFromRowAtIndexPath:[self editIndexPathForIndexPath:sourceIndexPath totalRows:_totalRows] toProposedIndexPath:[self editIndexPathForIndexPath:proposedDestinationIndexPath totalRows:_totalRows]]; 538 | } 539 | 540 | - (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath 541 | { 542 | return [_delegateEndless.receiver tableView:tableView indentationLevelForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 543 | } 544 | 545 | - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath 546 | { 547 | return [_delegateEndless.receiver tableView:tableView shouldShowMenuForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 548 | } 549 | 550 | - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender 551 | { 552 | return [_delegateEndless.receiver tableView:tableView canPerformAction:action forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows] withSender:sender]; 553 | } 554 | 555 | - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender 556 | { 557 | return [_delegateEndless.receiver tableView:tableView performAction:action forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows] withSender:sender]; 558 | } 559 | 560 | 561 | @end 562 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D31C14CC1CB24280000376B5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D31C14CB1CB24280000376B5 /* main.m */; }; 11 | D31C14CF1CB24280000376B5 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D31C14CE1CB24280000376B5 /* AppDelegate.m */; }; 12 | D31C14D21CB24280000376B5 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D31C14D11CB24280000376B5 /* ViewController.m */; }; 13 | D31C14D51CB24280000376B5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D31C14D31CB24280000376B5 /* Main.storyboard */; }; 14 | D31C14D71CB24280000376B5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D31C14D61CB24280000376B5 /* Assets.xcassets */; }; 15 | D31C14DA1CB24280000376B5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D31C14D81CB24280000376B5 /* LaunchScreen.storyboard */; }; 16 | D31C14E41CB242C9000376B5 /* EndlessTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = D31C14E31CB242C9000376B5 /* EndlessTableView.m */; }; 17 | D31C14E81CB2478D000376B5 /* EndlessTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D31C14E61CB2478D000376B5 /* EndlessTableViewCell.m */; }; 18 | D31C14E91CB2478D000376B5 /* EndlessTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D31C14E71CB2478D000376B5 /* EndlessTableViewCell.xib */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | D31C14C71CB24280000376B5 /* EndlessTableViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EndlessTableViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | D31C14CB1CB24280000376B5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 24 | D31C14CD1CB24280000376B5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 25 | D31C14CE1CB24280000376B5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 26 | D31C14D01CB24280000376B5 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 27 | D31C14D11CB24280000376B5 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 28 | D31C14D41CB24280000376B5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 29 | D31C14D61CB24280000376B5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 30 | D31C14D91CB24280000376B5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 31 | D31C14DB1CB24280000376B5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 32 | D31C14E21CB242C9000376B5 /* EndlessTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EndlessTableView.h; sourceTree = ""; }; 33 | D31C14E31CB242C9000376B5 /* EndlessTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EndlessTableView.m; sourceTree = ""; }; 34 | D31C14E51CB2478D000376B5 /* EndlessTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EndlessTableViewCell.h; sourceTree = ""; }; 35 | D31C14E61CB2478D000376B5 /* EndlessTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EndlessTableViewCell.m; sourceTree = ""; }; 36 | D31C14E71CB2478D000376B5 /* EndlessTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = EndlessTableViewCell.xib; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | D31C14C41CB24280000376B5 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | D31C14BE1CB24280000376B5 = { 51 | isa = PBXGroup; 52 | children = ( 53 | D31C14C91CB24280000376B5 /* EndlessTableViewDemo */, 54 | D31C14C81CB24280000376B5 /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | D31C14C81CB24280000376B5 /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | D31C14C71CB24280000376B5 /* EndlessTableViewDemo.app */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | D31C14C91CB24280000376B5 /* EndlessTableViewDemo */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | D31C14E11CB242C9000376B5 /* EndlessTableView */, 70 | D31C14CD1CB24280000376B5 /* AppDelegate.h */, 71 | D31C14CE1CB24280000376B5 /* AppDelegate.m */, 72 | D31C14D01CB24280000376B5 /* ViewController.h */, 73 | D31C14D11CB24280000376B5 /* ViewController.m */, 74 | D31C14D31CB24280000376B5 /* Main.storyboard */, 75 | D31C14D61CB24280000376B5 /* Assets.xcassets */, 76 | D31C14D81CB24280000376B5 /* LaunchScreen.storyboard */, 77 | D31C14E51CB2478D000376B5 /* EndlessTableViewCell.h */, 78 | D31C14E61CB2478D000376B5 /* EndlessTableViewCell.m */, 79 | D31C14E71CB2478D000376B5 /* EndlessTableViewCell.xib */, 80 | D31C14DB1CB24280000376B5 /* Info.plist */, 81 | D31C14CA1CB24280000376B5 /* Supporting Files */, 82 | ); 83 | path = EndlessTableViewDemo; 84 | sourceTree = ""; 85 | }; 86 | D31C14CA1CB24280000376B5 /* Supporting Files */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | D31C14CB1CB24280000376B5 /* main.m */, 90 | ); 91 | name = "Supporting Files"; 92 | sourceTree = ""; 93 | }; 94 | D31C14E11CB242C9000376B5 /* EndlessTableView */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | D31C14E21CB242C9000376B5 /* EndlessTableView.h */, 98 | D31C14E31CB242C9000376B5 /* EndlessTableView.m */, 99 | ); 100 | path = EndlessTableView; 101 | sourceTree = ""; 102 | }; 103 | /* End PBXGroup section */ 104 | 105 | /* Begin PBXNativeTarget section */ 106 | D31C14C61CB24280000376B5 /* EndlessTableViewDemo */ = { 107 | isa = PBXNativeTarget; 108 | buildConfigurationList = D31C14DE1CB24280000376B5 /* Build configuration list for PBXNativeTarget "EndlessTableViewDemo" */; 109 | buildPhases = ( 110 | D31C14C31CB24280000376B5 /* Sources */, 111 | D31C14C41CB24280000376B5 /* Frameworks */, 112 | D31C14C51CB24280000376B5 /* Resources */, 113 | ); 114 | buildRules = ( 115 | ); 116 | dependencies = ( 117 | ); 118 | name = EndlessTableViewDemo; 119 | productName = EndlessTableViewDemo; 120 | productReference = D31C14C71CB24280000376B5 /* EndlessTableViewDemo.app */; 121 | productType = "com.apple.product-type.application"; 122 | }; 123 | /* End PBXNativeTarget section */ 124 | 125 | /* Begin PBXProject section */ 126 | D31C14BF1CB24280000376B5 /* Project object */ = { 127 | isa = PBXProject; 128 | attributes = { 129 | LastUpgradeCheck = 0730; 130 | ORGANIZATIONNAME = "Anil Oruc"; 131 | TargetAttributes = { 132 | D31C14C61CB24280000376B5 = { 133 | CreatedOnToolsVersion = 7.3; 134 | }; 135 | }; 136 | }; 137 | buildConfigurationList = D31C14C21CB24280000376B5 /* Build configuration list for PBXProject "EndlessTableViewDemo" */; 138 | compatibilityVersion = "Xcode 3.2"; 139 | developmentRegion = English; 140 | hasScannedForEncodings = 0; 141 | knownRegions = ( 142 | en, 143 | Base, 144 | ); 145 | mainGroup = D31C14BE1CB24280000376B5; 146 | productRefGroup = D31C14C81CB24280000376B5 /* Products */; 147 | projectDirPath = ""; 148 | projectRoot = ""; 149 | targets = ( 150 | D31C14C61CB24280000376B5 /* EndlessTableViewDemo */, 151 | ); 152 | }; 153 | /* End PBXProject section */ 154 | 155 | /* Begin PBXResourcesBuildPhase section */ 156 | D31C14C51CB24280000376B5 /* Resources */ = { 157 | isa = PBXResourcesBuildPhase; 158 | buildActionMask = 2147483647; 159 | files = ( 160 | D31C14DA1CB24280000376B5 /* LaunchScreen.storyboard in Resources */, 161 | D31C14D71CB24280000376B5 /* Assets.xcassets in Resources */, 162 | D31C14E91CB2478D000376B5 /* EndlessTableViewCell.xib in Resources */, 163 | D31C14D51CB24280000376B5 /* Main.storyboard in Resources */, 164 | ); 165 | runOnlyForDeploymentPostprocessing = 0; 166 | }; 167 | /* End PBXResourcesBuildPhase section */ 168 | 169 | /* Begin PBXSourcesBuildPhase section */ 170 | D31C14C31CB24280000376B5 /* Sources */ = { 171 | isa = PBXSourcesBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | D31C14D21CB24280000376B5 /* ViewController.m in Sources */, 175 | D31C14CF1CB24280000376B5 /* AppDelegate.m in Sources */, 176 | D31C14E41CB242C9000376B5 /* EndlessTableView.m in Sources */, 177 | D31C14E81CB2478D000376B5 /* EndlessTableViewCell.m in Sources */, 178 | D31C14CC1CB24280000376B5 /* main.m in Sources */, 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | }; 182 | /* End PBXSourcesBuildPhase section */ 183 | 184 | /* Begin PBXVariantGroup section */ 185 | D31C14D31CB24280000376B5 /* Main.storyboard */ = { 186 | isa = PBXVariantGroup; 187 | children = ( 188 | D31C14D41CB24280000376B5 /* Base */, 189 | ); 190 | name = Main.storyboard; 191 | sourceTree = ""; 192 | }; 193 | D31C14D81CB24280000376B5 /* LaunchScreen.storyboard */ = { 194 | isa = PBXVariantGroup; 195 | children = ( 196 | D31C14D91CB24280000376B5 /* Base */, 197 | ); 198 | name = LaunchScreen.storyboard; 199 | sourceTree = ""; 200 | }; 201 | /* End PBXVariantGroup section */ 202 | 203 | /* Begin XCBuildConfiguration section */ 204 | D31C14DC1CB24280000376B5 /* Debug */ = { 205 | isa = XCBuildConfiguration; 206 | buildSettings = { 207 | ALWAYS_SEARCH_USER_PATHS = NO; 208 | CLANG_ANALYZER_NONNULL = YES; 209 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 210 | CLANG_CXX_LIBRARY = "libc++"; 211 | CLANG_ENABLE_MODULES = YES; 212 | CLANG_ENABLE_OBJC_ARC = YES; 213 | CLANG_WARN_BOOL_CONVERSION = YES; 214 | CLANG_WARN_CONSTANT_CONVERSION = YES; 215 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 216 | CLANG_WARN_EMPTY_BODY = YES; 217 | CLANG_WARN_ENUM_CONVERSION = YES; 218 | CLANG_WARN_INT_CONVERSION = YES; 219 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 220 | CLANG_WARN_UNREACHABLE_CODE = YES; 221 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 222 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 223 | COPY_PHASE_STRIP = NO; 224 | DEBUG_INFORMATION_FORMAT = dwarf; 225 | ENABLE_STRICT_OBJC_MSGSEND = YES; 226 | ENABLE_TESTABILITY = YES; 227 | GCC_C_LANGUAGE_STANDARD = gnu99; 228 | GCC_DYNAMIC_NO_PIC = NO; 229 | GCC_NO_COMMON_BLOCKS = YES; 230 | GCC_OPTIMIZATION_LEVEL = 0; 231 | GCC_PREPROCESSOR_DEFINITIONS = ( 232 | "DEBUG=1", 233 | "$(inherited)", 234 | ); 235 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 236 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 237 | GCC_WARN_UNDECLARED_SELECTOR = YES; 238 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 239 | GCC_WARN_UNUSED_FUNCTION = YES; 240 | GCC_WARN_UNUSED_VARIABLE = YES; 241 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 242 | MTL_ENABLE_DEBUG_INFO = YES; 243 | ONLY_ACTIVE_ARCH = YES; 244 | SDKROOT = iphoneos; 245 | }; 246 | name = Debug; 247 | }; 248 | D31C14DD1CB24280000376B5 /* Release */ = { 249 | isa = XCBuildConfiguration; 250 | buildSettings = { 251 | ALWAYS_SEARCH_USER_PATHS = NO; 252 | CLANG_ANALYZER_NONNULL = YES; 253 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 254 | CLANG_CXX_LIBRARY = "libc++"; 255 | CLANG_ENABLE_MODULES = YES; 256 | CLANG_ENABLE_OBJC_ARC = YES; 257 | CLANG_WARN_BOOL_CONVERSION = YES; 258 | CLANG_WARN_CONSTANT_CONVERSION = YES; 259 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 260 | CLANG_WARN_EMPTY_BODY = YES; 261 | CLANG_WARN_ENUM_CONVERSION = YES; 262 | CLANG_WARN_INT_CONVERSION = YES; 263 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 264 | CLANG_WARN_UNREACHABLE_CODE = YES; 265 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 266 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 267 | COPY_PHASE_STRIP = NO; 268 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 269 | ENABLE_NS_ASSERTIONS = NO; 270 | ENABLE_STRICT_OBJC_MSGSEND = YES; 271 | GCC_C_LANGUAGE_STANDARD = gnu99; 272 | GCC_NO_COMMON_BLOCKS = YES; 273 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 274 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 275 | GCC_WARN_UNDECLARED_SELECTOR = YES; 276 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 277 | GCC_WARN_UNUSED_FUNCTION = YES; 278 | GCC_WARN_UNUSED_VARIABLE = YES; 279 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 280 | MTL_ENABLE_DEBUG_INFO = NO; 281 | SDKROOT = iphoneos; 282 | VALIDATE_PRODUCT = YES; 283 | }; 284 | name = Release; 285 | }; 286 | D31C14DF1CB24280000376B5 /* Debug */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 290 | INFOPLIST_FILE = EndlessTableViewDemo/Info.plist; 291 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 292 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 293 | PRODUCT_BUNDLE_IDENTIFIER = annulmobile.EndlessTableViewDemo; 294 | PRODUCT_NAME = "$(TARGET_NAME)"; 295 | }; 296 | name = Debug; 297 | }; 298 | D31C14E01CB24280000376B5 /* Release */ = { 299 | isa = XCBuildConfiguration; 300 | buildSettings = { 301 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 302 | INFOPLIST_FILE = EndlessTableViewDemo/Info.plist; 303 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 304 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 305 | PRODUCT_BUNDLE_IDENTIFIER = annulmobile.EndlessTableViewDemo; 306 | PRODUCT_NAME = "$(TARGET_NAME)"; 307 | }; 308 | name = Release; 309 | }; 310 | /* End XCBuildConfiguration section */ 311 | 312 | /* Begin XCConfigurationList section */ 313 | D31C14C21CB24280000376B5 /* Build configuration list for PBXProject "EndlessTableViewDemo" */ = { 314 | isa = XCConfigurationList; 315 | buildConfigurations = ( 316 | D31C14DC1CB24280000376B5 /* Debug */, 317 | D31C14DD1CB24280000376B5 /* Release */, 318 | ); 319 | defaultConfigurationIsVisible = 0; 320 | defaultConfigurationName = Release; 321 | }; 322 | D31C14DE1CB24280000376B5 /* Build configuration list for PBXNativeTarget "EndlessTableViewDemo" */ = { 323 | isa = XCConfigurationList; 324 | buildConfigurations = ( 325 | D31C14DF1CB24280000376B5 /* Debug */, 326 | D31C14E01CB24280000376B5 /* Release */, 327 | ); 328 | defaultConfigurationIsVisible = 0; 329 | }; 330 | /* End XCConfigurationList section */ 331 | }; 332 | rootObject = D31C14BF1CB24280000376B5 /* Project object */; 333 | } 334 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // EndlessTableViewDemo 4 | // 5 | // Created by Anil Oruc on 4/4/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 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // EndlessTableViewDemo 4 | // 5 | // Created by Anil Oruc on 4/4/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 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/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 | } -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/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 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/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 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/EndlessTableView/EndlessTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // EndlessTableView.h 3 | // OmsaTech 4 | // 5 | // Created by Anıl Oruç on 06/05/14. 6 | // Copyright (c) 2014 OmsaTech. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface EndlessTableView : UITableView 12 | 13 | @property(nonatomic)BOOL enableEndlessScrolling; 14 | 15 | @property(nonatomic)BOOL enableAutoScrolling; 16 | 17 | @property(nonatomic)CGFloat autoScrollValue; 18 | 19 | @property(nonatomic)CGFloat differenceRateValue; 20 | 21 | @property(nonatomic,weak) EndlessTableView *attachedTableView; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/EndlessTableView/EndlessTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // EndlessTableView.m 3 | // OmsaTech 4 | // 5 | // Created by Anıl Oruç on 06/05/14. 6 | // Copyright (c) 2014 OmsaTech. All rights reserved. 7 | // 8 | 9 | #import "EndlessTableView.h" 10 | #import 11 | 12 | #define AUTO_SCROLLING_DEFAULT_VALUE 1.0f 13 | #define DIFFERENCE_SCROLL_DEFAULT_VALUE 1.0f 14 | 15 | @interface NSTimer (BlocksSupport) 16 | 17 | + (NSTimer*)autoScrolling_scheduledTimerWithTimeInterval:(NSTimeInterval)interval 18 | block:(void(^)())block 19 | repeats:(BOOL)repeats; 20 | @end 21 | 22 | @implementation NSTimer (BlocksSupport) 23 | 24 | + (NSTimer*)autoScrolling_scheduledTimerWithTimeInterval:(NSTimeInterval)interval 25 | block:(void(^)())block 26 | repeats:(BOOL)repeats 27 | { 28 | 29 | return [self scheduledTimerWithTimeInterval:interval 30 | target:self 31 | selector:@selector(autoScrolling_blockInvoke:) 32 | userInfo:[block copy] 33 | repeats:repeats]; 34 | } 35 | 36 | + (void)autoScrolling_blockInvoke:(NSTimer*)timer { 37 | void (^block)() = timer.userInfo; 38 | if (block) { 39 | block(); 40 | } 41 | } 42 | 43 | @end 44 | 45 | @interface EndlessTableViewDelegate : NSObject 46 | 47 | @property (nonatomic, weak) id receiver; 48 | @property (nonatomic, weak) id middleMan; 49 | 50 | @end 51 | 52 | @implementation EndlessTableViewDelegate 53 | 54 | - (id) forwardingTargetForSelector:(SEL)aSelector { 55 | 56 | if ([_middleMan respondsToSelector:aSelector]) 57 | return _middleMan; 58 | 59 | if ([_receiver respondsToSelector:aSelector]) 60 | return _receiver; 61 | 62 | return [super forwardingTargetForSelector:aSelector]; 63 | 64 | } 65 | 66 | - (BOOL) respondsToSelector:(SEL)aSelector { 67 | 68 | BOOL autoScrollingMethodControl = (sel_isEqual(aSelector, @selector(scrollViewDidEndDragging:willDecelerate:)) || sel_isEqual(aSelector, @selector(scrollViewWillBeginDragging:)) || sel_isEqual(aSelector, @selector(scrollViewDidEndDecelerating:)) || sel_isEqual(aSelector, @selector(scrollViewWillBeginDecelerating:)) || sel_isEqual(aSelector, @selector(scrollViewDidScroll:))); 69 | 70 | if (autoScrollingMethodControl) { 71 | if ([_middleMan respondsToSelector:aSelector]) 72 | return YES; 73 | } 74 | 75 | if ([_receiver respondsToSelector:aSelector]) 76 | return YES; 77 | 78 | return [super respondsToSelector:aSelector]; 79 | 80 | } 81 | 82 | @end 83 | 84 | @interface EndlessTableView () 85 | 86 | @property (nonatomic,strong) EndlessTableViewDelegate *dataSourceEndless,*delegateEndless; 87 | @property (nonatomic) NSInteger totalRows,totalCellsVisible; 88 | @property (nonatomic) CGFloat lastContentOffsetY; 89 | @property (nonatomic,strong) NSTimer* timerAutoScrolling; 90 | @property (nonatomic,readonly) BOOL isScrolling; 91 | 92 | @end 93 | 94 | @implementation EndlessTableView 95 | 96 | #pragma mark Initialization 97 | - (id)initWithCoder:(NSCoder *)aDecoder 98 | { 99 | self = [super initWithCoder:aDecoder]; 100 | if( self ) 101 | { 102 | [self customIntitialization]; 103 | } 104 | return self; 105 | } 106 | 107 | - (id)initWithFrame:(CGRect)frame style:(UITableViewStyle)style 108 | { 109 | self = [super initWithFrame:frame style:style]; 110 | if( self ) 111 | { 112 | [self customIntitialization]; 113 | } 114 | return self; 115 | } 116 | 117 | - (id)initWithFrame:(CGRect)frame 118 | { 119 | self = [super initWithFrame:frame]; 120 | if (self) 121 | { 122 | [self customIntitialization]; 123 | } 124 | return self; 125 | } 126 | 127 | -(void)dealloc 128 | { 129 | [self stopAutoScrolling]; 130 | } 131 | 132 | - (void)customIntitialization 133 | { 134 | _enableEndlessScrolling = YES; 135 | _autoScrollValue = 0.0f; 136 | _differenceRateValue = DIFFERENCE_SCROLL_DEFAULT_VALUE; 137 | } 138 | 139 | - (NSIndexPath*)editIndexPathForIndexPath:(NSIndexPath*)oldIndexPath totalRows:(NSInteger)totalRows 140 | { 141 | return _enableEndlessScrolling ? [NSIndexPath indexPathForRow:oldIndexPath.row % totalRows inSection:oldIndexPath.section] : oldIndexPath; 142 | } 143 | 144 | - (void)resetContentOffsetIfNeeded 145 | { 146 | if( !_enableEndlessScrolling ) 147 | return; 148 | 149 | NSArray *indexpaths = [self indexPathsForVisibleRows]; 150 | int totalVisibleCells = [indexpaths count]; 151 | if( _totalCellsVisible > totalVisibleCells ) 152 | { 153 | //we dont have enough content to generate scroll 154 | return; 155 | } 156 | CGPoint contentOffset = self.contentOffset; 157 | BOOL control = NO; 158 | //check the top condition 159 | //check if the scroll view reached its top.. if so.. move it to center.. remember center is the start of the data repeating for 2nd time. 160 | if( contentOffset.y <= 0.0 ) 161 | { 162 | control = YES; 163 | contentOffset.y = self.contentSize.height/3.0f; 164 | } 165 | else if( contentOffset.y >= ( self.contentSize.height - self.bounds.size.height) )//scrollview content offset reached bottom minus the height of the tableview 166 | { 167 | control = YES; 168 | //this scenario is same as the data repeating for 2nd time minus the height of the table view 169 | contentOffset.y = self.contentSize.height/3.0f- self.bounds.size.height; 170 | } 171 | 172 | if (control) { 173 | /* 174 | [_attachedTableView stopAutoScrolling]; 175 | [self stopAutoScrolling]; 176 | */ 177 | _lastContentOffsetY = contentOffset.y; 178 | [self setContentOffset:contentOffset]; 179 | /* 180 | if (_attachedTableView.enableAutoScrolling) { 181 | [_attachedTableView startAutoScrollingWithDelay:NO]; 182 | } 183 | else if(self.enableAutoScrolling){ 184 | [self startAutoScrollingWithDelay:NO]; 185 | } 186 | */ 187 | } 188 | } 189 | 190 | -(void)startAutoScrollingWithDelay:(BOOL)isDelay 191 | { 192 | double delayInSeconds = (isDelay ? 0.15f : 0.0f); 193 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 194 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 195 | if (!_timerAutoScrolling) 196 | { 197 | __weak id weakSelf = self; 198 | _timerAutoScrolling = [NSTimer autoScrolling_scheduledTimerWithTimeInterval:0.05 block:^{ 199 | EndlessTableView * strongSelf = weakSelf; 200 | [strongSelf autoScrollingAction]; 201 | } repeats:YES]; 202 | 203 | [[NSRunLoop mainRunLoop] addTimer:_timerAutoScrolling forMode:UITrackingRunLoopMode]; 204 | } 205 | }); 206 | } 207 | 208 | -(void)stopAutoScrolling 209 | { 210 | //[self.layer removeAllAnimations]; 211 | [_timerAutoScrolling invalidate]; 212 | _timerAutoScrolling = nil; 213 | } 214 | 215 | -(void)autoScrollingAction 216 | { 217 | [UIView animateWithDuration:0.05 delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^{ 218 | 219 | [self setContentOffset:CGPointMake(self.contentOffset.x, self.contentOffset.y + (_autoScrollValue <= 0.0f ? AUTO_SCROLLING_DEFAULT_VALUE : _autoScrollValue))]; 220 | 221 | } completion:nil]; 222 | 223 | } 224 | 225 | #pragma mark Layout 226 | 227 | - (void)layoutSubviews 228 | { 229 | _totalCellsVisible = self.frame.size.height / self.rowHeight; 230 | [self resetContentOffsetIfNeeded]; 231 | [super layoutSubviews]; 232 | } 233 | 234 | #pragma mark Setter/Getter 235 | 236 | -(void)reloadData{ 237 | [super reloadData]; 238 | if (_enableAutoScrolling) { 239 | [self startAutoScrollingWithDelay:YES]; 240 | } 241 | } 242 | 243 | -(void)setDifferenceRateValue:(CGFloat)differenceRateValue 244 | { 245 | _differenceRateValue = differenceRateValue; 246 | if(_attachedTableView) { 247 | [_attachedTableView setPrivateDifferenceRateValue:1/_differenceRateValue]; 248 | [_attachedTableView reloadData]; 249 | } 250 | } 251 | 252 | 253 | -(void)setPrivateDifferenceRateValue:(CGFloat)differenceRateValue 254 | { 255 | _differenceRateValue = differenceRateValue; 256 | 257 | } 258 | 259 | -(void)setEnableEndlessScrolling:(BOOL)enableEndlessScrolling 260 | { 261 | _enableEndlessScrolling = enableEndlessScrolling; 262 | 263 | [self reloadData]; 264 | } 265 | 266 | -(void)setEnableAutoScrolling:(BOOL)enableAutoScrolling 267 | { 268 | _enableAutoScrolling = enableAutoScrolling; 269 | 270 | if (_enableAutoScrolling) { 271 | [self startAutoScrollingWithDelay:YES]; 272 | } 273 | else { 274 | [self stopAutoScrolling]; 275 | } 276 | } 277 | 278 | -(void)setAttachedTableView:(EndlessTableView *)attachedTableView 279 | { 280 | _attachedTableView = attachedTableView; 281 | [_attachedTableView setPrivateAttachedTableView:self]; 282 | } 283 | 284 | -(void)setPrivateAttachedTableView:(EndlessTableView *)attachedTableView 285 | { 286 | _attachedTableView = attachedTableView; 287 | } 288 | 289 | - (void)setDataSource:(id)dataSource 290 | { 291 | if( !_dataSourceEndless) 292 | { 293 | _dataSourceEndless = [[EndlessTableViewDelegate alloc] init]; 294 | } 295 | 296 | _dataSourceEndless.receiver = dataSource; 297 | _dataSourceEndless.middleMan = self; 298 | 299 | [super setDataSource:(id)_dataSourceEndless]; 300 | } 301 | 302 | -(void)setDelegate:(id)delegate 303 | { 304 | if (delegate == nil) { 305 | [super setDelegate:delegate]; 306 | return; 307 | } 308 | if( !_delegateEndless) 309 | { 310 | _delegateEndless = [[EndlessTableViewDelegate alloc] init]; 311 | } 312 | 313 | _delegateEndless.receiver = delegate; 314 | _delegateEndless.middleMan = self; 315 | 316 | [super setDelegate:(id)_delegateEndless]; 317 | } 318 | 319 | /* 320 | // Only override drawRect: if you perform custom drawing. 321 | // An empty implementation adversely affects performance during animation. 322 | - (void)drawRect:(CGRect)rect 323 | { 324 | // Drawing code 325 | } 326 | */ 327 | 328 | #pragma mark - UIScrollViewDelegates 329 | 330 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView; 331 | { 332 | if ([self numberOfRowsInSection:0] > 0 && [_attachedTableView numberOfRowsInSection:0] > 0 && !_attachedTableView.isScrolling && !_attachedTableView.timerAutoScrolling.isValid){ 333 | 334 | CGFloat differenceRate = (_attachedTableView.contentSize.height / scrollView.contentSize.height); 335 | 336 | CGFloat diffY = scrollView.contentOffset.y - _lastContentOffsetY; 337 | 338 | if (0.8f < differenceRate < 1.2f) { 339 | differenceRate = differenceRate * _differenceRateValue; 340 | } 341 | 342 | CGFloat value = _attachedTableView.contentOffset.y + (diffY * differenceRate); 343 | 344 | if (_attachedTableView.contentSize.height >= value) { 345 | 346 | _lastContentOffsetY = scrollView.contentOffset.y; 347 | 348 | _attachedTableView.lastContentOffsetY = value; 349 | 350 | [_attachedTableView setContentOffset:CGPointMake(_attachedTableView.contentOffset.x, value)]; 351 | } 352 | 353 | } 354 | 355 | if ([_delegateEndless.receiver respondsToSelector:@selector(scrollViewDidScroll:)]) { 356 | return [_delegateEndless.receiver scrollViewDidScroll:scrollView]; 357 | } 358 | } 359 | 360 | - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate 361 | { 362 | if (_enableAutoScrolling && !decelerate) { 363 | [self startAutoScrollingWithDelay:YES]; 364 | } 365 | 366 | _isScrolling = NO; 367 | 368 | if (_attachedTableView.enableAutoScrolling && !decelerate && !_attachedTableView.isDragging) 369 | { 370 | [_attachedTableView startAutoScrollingWithDelay:YES]; 371 | } 372 | 373 | if ([_delegateEndless.receiver respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) { 374 | return [_delegateEndless.receiver scrollViewDidEndDragging:scrollView willDecelerate:decelerate]; 375 | } 376 | } 377 | 378 | - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 379 | { 380 | if (_enableAutoScrolling) { 381 | [self stopAutoScrolling]; 382 | } 383 | 384 | _isScrolling = YES; 385 | 386 | if (_attachedTableView.enableAutoScrolling) 387 | { 388 | [_attachedTableView stopAutoScrolling]; 389 | } 390 | 391 | if ([_delegateEndless.receiver respondsToSelector:@selector(scrollViewWillBeginDragging:)]) { 392 | return [_delegateEndless.receiver scrollViewWillBeginDragging:scrollView]; 393 | } 394 | } 395 | 396 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 397 | { 398 | if (_enableAutoScrolling) { 399 | [self startAutoScrollingWithDelay:YES]; 400 | } 401 | 402 | _isScrolling = NO; 403 | 404 | if (_attachedTableView.enableAutoScrolling) { 405 | [_attachedTableView startAutoScrollingWithDelay:YES]; 406 | } 407 | 408 | if ([_delegateEndless.receiver respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) { 409 | return [_delegateEndless.receiver scrollViewDidEndDecelerating:scrollView]; 410 | } 411 | } 412 | 413 | 414 | 415 | #pragma mark - UITableViewDataSource 416 | - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section 417 | { 418 | _totalRows = [_dataSourceEndless.receiver tableView:tableView numberOfRowsInSection:section]; 419 | 420 | return _totalRows * ( _enableEndlessScrolling ? 3 : 1 ); 421 | } 422 | 423 | - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 424 | { 425 | return [_dataSourceEndless.receiver tableView:tableView cellForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 426 | } 427 | 428 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath 429 | { 430 | return [_dataSourceEndless.receiver tableView:tableView canEditRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 431 | } 432 | 433 | - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath 434 | { 435 | return [_dataSourceEndless.receiver tableView:tableView canMoveRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 436 | } 437 | 438 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 439 | { 440 | return [_dataSourceEndless.receiver tableView:tableView commitEditingStyle:editingStyle forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 441 | } 442 | 443 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath 444 | { 445 | return [_dataSourceEndless.receiver tableView:tableView moveRowAtIndexPath:[self editIndexPathForIndexPath:sourceIndexPath totalRows:_totalRows] toIndexPath:[self editIndexPathForIndexPath:destinationIndexPath totalRows:_totalRows]]; 446 | } 447 | 448 | #pragma mark - UITableViewDelegate 449 | 450 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath 451 | { 452 | return [_delegateEndless.receiver tableView:tableView willDisplayCell:cell forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 453 | } 454 | 455 | - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath 456 | { 457 | return [_delegateEndless.receiver tableView:tableView didEndDisplayingCell:cell forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 458 | } 459 | 460 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 461 | { 462 | return [_delegateEndless.receiver tableView:tableView heightForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 463 | } 464 | 465 | - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath 466 | { 467 | return [_delegateEndless.receiver tableView:tableView estimatedHeightForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 468 | } 469 | 470 | - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath 471 | { 472 | return [_delegateEndless.receiver tableView:tableView accessoryButtonTappedForRowWithIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 473 | } 474 | 475 | - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath 476 | { 477 | return [_delegateEndless.receiver tableView:tableView shouldHighlightRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 478 | } 479 | 480 | - (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath 481 | { 482 | return [_delegateEndless.receiver tableView:tableView didHighlightRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 483 | } 484 | 485 | - (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath 486 | { 487 | return [_delegateEndless.receiver tableView:tableView didUnhighlightRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 488 | } 489 | 490 | - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath 491 | { 492 | return [_delegateEndless.receiver tableView:tableView willSelectRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 493 | } 494 | 495 | - (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath 496 | { 497 | return [_delegateEndless.receiver tableView:tableView willDeselectRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 498 | } 499 | 500 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 501 | { 502 | return [_delegateEndless.receiver tableView:tableView didSelectRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 503 | } 504 | 505 | - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath 506 | { 507 | return [_delegateEndless.receiver tableView:tableView didDeselectRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 508 | } 509 | 510 | - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath 511 | { 512 | return [_delegateEndless.receiver tableView:tableView editingStyleForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 513 | } 514 | 515 | - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath 516 | { 517 | return [_delegateEndless.receiver tableView:tableView titleForDeleteConfirmationButtonForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 518 | } 519 | 520 | - (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath 521 | { 522 | return [_delegateEndless.receiver tableView:tableView shouldIndentWhileEditingRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 523 | } 524 | 525 | - (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath 526 | { 527 | return [_delegateEndless.receiver tableView:tableView willBeginEditingRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 528 | } 529 | 530 | - (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath 531 | { 532 | return [_delegateEndless.receiver tableView:tableView didEndEditingRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 533 | } 534 | 535 | - (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath 536 | { 537 | return [_delegateEndless.receiver tableView:tableView targetIndexPathForMoveFromRowAtIndexPath:[self editIndexPathForIndexPath:sourceIndexPath totalRows:_totalRows] toProposedIndexPath:[self editIndexPathForIndexPath:proposedDestinationIndexPath totalRows:_totalRows]]; 538 | } 539 | 540 | - (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath 541 | { 542 | return [_delegateEndless.receiver tableView:tableView indentationLevelForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 543 | } 544 | 545 | - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath 546 | { 547 | return [_delegateEndless.receiver tableView:tableView shouldShowMenuForRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows]]; 548 | } 549 | 550 | - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender 551 | { 552 | return [_delegateEndless.receiver tableView:tableView canPerformAction:action forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows] withSender:sender]; 553 | } 554 | 555 | - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender 556 | { 557 | return [_delegateEndless.receiver tableView:tableView performAction:action forRowAtIndexPath:[self editIndexPathForIndexPath:indexPath totalRows:_totalRows] withSender:sender]; 558 | } 559 | 560 | 561 | @end 562 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/EndlessTableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // EndlessTableViewCell.h 3 | // EndlessTableViewDemo 4 | // 5 | // Created by Anil Oruc on 4/4/16. 6 | // Copyright © 2016 Anil Oruc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface EndlessTableViewCell : UITableViewCell 12 | 13 | @property (nonatomic, strong) UIColor *bgColor; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/EndlessTableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // EndlessTableViewCell.m 3 | // EndlessTableViewDemo 4 | // 5 | // Created by Anil Oruc on 4/4/16. 6 | // Copyright © 2016 Anil Oruc. All rights reserved. 7 | // 8 | 9 | #import "EndlessTableViewCell.h" 10 | 11 | @interface EndlessTableViewCell () 12 | 13 | @property (weak, nonatomic) IBOutlet UIView *view; 14 | 15 | @end 16 | 17 | @implementation EndlessTableViewCell 18 | 19 | - (void)awakeFromNib { 20 | [super awakeFromNib]; 21 | // Initialization code 22 | } 23 | 24 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 25 | [super setSelected:selected animated:animated]; 26 | 27 | // Configure the view for the selected state 28 | } 29 | 30 | -(void)setBgColor:(UIColor *)bgColor 31 | { 32 | _bgColor = bgColor; 33 | 34 | _view.backgroundColor = bgColor; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/EndlessTableViewCell.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 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/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 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // EndlessTableViewDemo 4 | // 5 | // Created by Anil Oruc on 4/4/16. 6 | // Copyright © 2016 Anil Oruc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // EndlessTableViewDemo 4 | // 5 | // Created by Anil Oruc on 4/4/16. 6 | // Copyright © 2016 Anil Oruc. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "EndlessTableView.h" 11 | #import "EndlessTableViewCell.h" 12 | 13 | @interface ViewController () 14 | 15 | @property (weak, nonatomic) IBOutlet EndlessTableView *tableViewProduct; 16 | @property (weak, nonatomic) IBOutlet EndlessTableView *tableViewCampaign; 17 | 18 | @property (nonatomic, strong) NSArray *arrayCampaigns; 19 | @property (nonatomic, strong) NSArray *arrayProducts; 20 | 21 | @end 22 | 23 | @implementation ViewController 24 | 25 | - (void)viewDidLoad { 26 | [super viewDidLoad]; 27 | // Do any additional setup after loading the view, typically from a nib. 28 | 29 | [self.tableViewProduct registerNib:[UINib nibWithNibName:@"EndlessTableViewCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"EndlessTableViewCell"]; 30 | [self.tableViewCampaign registerNib:[UINib nibWithNibName:@"EndlessTableViewCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"EndlessTableViewCell"]; 31 | 32 | self.arrayCampaigns = @[[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor]]; 33 | self.arrayProducts = @[[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor],[self randomColor]]; 34 | 35 | if (self.arrayCampaigns.count > 2 && self.arrayProducts.count > 2) { 36 | self.tableViewProduct.attachedTableView = self.tableViewCampaign; 37 | self.tableViewProduct.enableAutoScrolling = YES; 38 | self.tableViewProduct.differenceRateValue = 1.3f; 39 | } 40 | } 41 | 42 | - (void)didReceiveMemoryWarning { 43 | [super didReceiveMemoryWarning]; 44 | // Dispose of any resources that can be recreated. 45 | } 46 | 47 | -(UIColor*)randomColor 48 | { 49 | CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0 50 | CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white 51 | CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black 52 | UIColor *color = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1]; 53 | return color; 54 | } 55 | 56 | #pragma mark - Setters 57 | 58 | -(void)setArrayProducts:(NSArray *)arrayProducts 59 | { 60 | _arrayProducts = [arrayProducts copy]; 61 | 62 | [_tableViewProduct reloadData]; 63 | } 64 | 65 | -(void)setArrayCampaigns:(NSArray *)arrayCampaigns 66 | { 67 | _arrayCampaigns = [arrayCampaigns copy]; 68 | 69 | [_tableViewCampaign reloadData]; 70 | } 71 | 72 | #pragma mark - TableView Datasource 73 | 74 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 75 | { 76 | NSInteger numberOfRows = 0; 77 | if (tableView == _tableViewCampaign) 78 | numberOfRows = _arrayCampaigns.count; 79 | else if (tableView == _tableViewProduct) 80 | numberOfRows = _arrayProducts.count; 81 | return numberOfRows; 82 | } 83 | 84 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 85 | { 86 | EndlessTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"EndlessTableViewCell"]; 87 | 88 | if(cell == nil) 89 | { 90 | cell = [[EndlessTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"EndlessTableViewCell"]; 91 | } 92 | 93 | cell.bgColor = (tableView == _tableViewCampaign ? _arrayCampaigns[indexPath.row] : _arrayProducts[indexPath.row]); 94 | 95 | 96 | return cell; 97 | } 98 | 99 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 100 | { 101 | if (tableView == _tableViewCampaign) 102 | { 103 | 104 | } 105 | else 106 | { 107 | 108 | } 109 | } 110 | 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /EndlessTableViewDemo/EndlessTableViewDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // EndlessTableViewDemo 4 | // 5 | // Created by Anil Oruc on 4/4/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 | # EndlessTableView 2 | 3 | Endless - Infinite & Double Table Together Parallax Animation & Auto Scrolling 4 | 5 | 6 | ## Display Visual Examples 7 | 8 | ---- 9 | ![Visual1](http://g.recordit.co/KXg4X6QNYN.gif) 10 | ---- 11 | 12 | 13 | Installation 14 | -------------- 15 | 16 | To use the EndlessTableView class in an app, just drag the EndlessTableView class files (demo files and assets are not needed) into your project. 17 | 18 | Properties 19 | -------------- 20 | 21 | The EndlessTableView has the following properties (note: for iOS, UITableView when using properties): 22 | 23 | @property (nonatomic) BOOL enableEndlessScrolling 24 | 25 | The default value is YES. 26 | 27 | @property (nonatomic) BOOL enableAutoScrolling; 28 | 29 | The default value is NO. 30 | 31 | @property (nonatomic) CGFloat autoScrollValue; 32 | 33 | This property can be used to set the table scrolling at a constant speed. A value of 1.0 would scroll the carousel forwards at a rate of one item per second. The autoscroll value can be positive or negative and defaults to 0.0 (stationary). Autoscrolling will stop if the user interacts with the table, and will resume when they stop. The default value is 0.0f. 34 | 35 | @property (nonatomic) CGFloat differenceRateValue; 36 | 37 | If attachedTableView and contentSize of this event of an equal, differenceRateValue processing are taken. The default value is 1.0f. 38 | 39 | @property (nonatomic,weak) EndlessTableView *attachedTableView; 40 | 41 | The default value is nil. 42 | 43 | 44 | How to use ? 45 | ---------- 46 | 47 | ![Visual4](http://g.recordit.co/ykx1SbnAmZ.gif) 48 | ---- 49 | 50 | ```Objective-C 51 | #import "EndlessTableView.h" 52 | 53 | @interface ViewController () 54 | 55 | @property (weak, nonatomic) IBOutlet EndlessTableView *tableViewProduct; 56 | @property (weak, nonatomic) IBOutlet EndlessTableView *tableViewCampaign; 57 | 58 | ... 59 | 60 | - (void)loadView 61 | { 62 | [super loadView]; 63 | 64 | self.tableViewProduct.attachedTableView = self.tableViewCampaign; 65 | self.tableViewProduct.enableAutoScrolling = YES; 66 | self.tableViewProduct.differenceRateValue = 1.3f; 67 | 68 | [_tableViewProduct reloadData]; 69 | 70 | [_tableViewCampaign reloadData]; 71 | 72 | } 73 | 74 | ... 75 | 76 | #pragma mark - TableView Datasource & Delegate 77 | 78 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 79 | { 80 | 81 | NSInteger numberOfRows = 0; 82 | 83 | if (tableView == _tableViewCampaign) 84 | numberOfRows = 10; 85 | else if (tableView == _tableViewProduct) 86 | numberOfRows = 8; 87 | 88 | return numberOfRows; 89 | 90 | } 91 | 92 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 93 | { 94 | 95 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"EndlessTableViewCell"]; 96 | 97 | if (tableView == _tableViewCampaign) 98 | { 99 | 100 | } 101 | else if (tableView == _tableViewProduct) 102 | { 103 | 104 | } 105 | return cell; 106 | 107 | } 108 | 109 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 110 | { 111 | 112 | } 113 | 114 | ``` 115 | 116 | OR 117 | 118 | ```Objective-C 119 | #import "EndlessTableView.h" 120 | 121 | @interface ViewController () 122 | 123 | ... 124 | 125 | - (void)loadView 126 | { 127 | [super loadView]; 128 | 129 | EndlessTableView *tableViewCampaign = [[EndlessTableView alloc]initWithFrame:CGRectMake([UIScreen mainScreen].bounds.size.width / 2, 0.0f, [UIScreen mainScreen].bounds.size.width / 2, [UIScreen mainScreen].bounds.size.height)]; 130 | tableViewCampaign.tag = 0; 131 | tableViewCampaign.dataSource = self; 132 | tableViewCampaign.delegate = self; 133 | 134 | EndlessTableView *tableViewProduct = [[EndlessTableView alloc]initWithFrame:CGRectMake(0.0f, 0.0f, [UIScreen mainScreen].bounds.size.width / 2, [UIScreen mainScreen].bounds.size.height)]; 135 | tableViewProduct.tag = 1; 136 | tableViewProduct.dataSource = self; 137 | tableViewProduct.delegate = self; 138 | 139 | tableViewProduct.attachedTableView = tableViewCampaign; 140 | tableViewProduct.enableAutoScrolling = YES; 141 | tableViewProduct.differenceRateValue = 1.3f; 142 | 143 | } 144 | 145 | ... 146 | 147 | #pragma mark - TableView Datasource & Delegate 148 | 149 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 150 | { 151 | 152 | NSInteger numberOfRows = 0; 153 | 154 | if (tableView.tag == 0) // Campaign TableView 155 | numberOfRows = 10; 156 | else if (tableView.tag == 1) // Product TableView 157 | numberOfRows = 8; 158 | 159 | return numberOfRows; 160 | 161 | } 162 | 163 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 164 | { 165 | 166 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"EndlessTableViewCell"]; 167 | 168 | if (tableView.tag == 0) // Campaign TableView 169 | { 170 | 171 | } 172 | else if (tableView.tag == 1) // Product TableView 173 | { 174 | 175 | } 176 | return cell; 177 | 178 | } 179 | 180 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 181 | { 182 | 183 | } 184 | 185 | ``` 186 | 187 | Build and run the project files. Enjoy more examples! --------------------------------------------------------------------------------