I\'m attempting to setup a scrollview with infinite (horizontal) scrolling.
Scrolling forward is easy - I have implemented scrollViewDidScroll, and when the contentO
So one issue is that setting the contentOffset while in the scrollViewDidScroll: delegate method will cause the delegate method to fire again while you are inside it. So what I do is remove the delegate > setContentOffset > set the delegate again.
Here's my code:
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (strong, nonatomic) NSMutableArray *labels;
@property (weak, nonatomic) UILabel *originLabel;
- (void)addScrollViewLabels;
- (void)adjustOrigins:(float)deltaX;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// just some starting size
CGSize size = self.scrollView.frame.size;
CGSize contentSize = CGSizeMake(size.width * 4, size.height);
[self.scrollView setContentSize:contentSize];
// just some starting offset
CGPoint contentOffset = CGPointMake((contentSize.width / 2), 0);
[self.scrollView setContentOffset:contentOffset];
[self addScrollViewLabels];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGSize contentSize = scrollView.contentSize;
CGSize size = scrollView.frame.size;
CGPoint contentOffset = scrollView.contentOffset;
const float kContentOffsetBuffer = 100;
const float kContentSizeGrowth = (4 * size.width);
BOOL shouldGrowContentSize = (contentOffset.x > (contentSize.width - size.width - kContentOffsetBuffer))
|| (contentOffset.x < (kContentOffsetBuffer));
if (shouldGrowContentSize) {
// the contentOffset has reached a point where we need to grow the contentSize
CGSize adjustedContentSize = CGSizeMake(contentSize.width + kContentSizeGrowth, contentSize.height);
[self.scrollView setContentSize:adjustedContentSize];
if(contentOffset.x < (kContentOffsetBuffer)) {
// the growth needs to happen on the left side which means that we need to adjust the contentOffset to compensate for the growth.
// this is not necessary when growth happens to the right since the contentOffset is the same.
CGPoint adjustedContentOffset = CGPointMake(contentOffset.x + kContentSizeGrowth, contentOffset.y);
[self.scrollView setDelegate:nil];
[self.scrollView setContentOffset:adjustedContentOffset];
[self.scrollView setDelegate:self];
[self adjustOrigins:kContentSizeGrowth];
}
[self addScrollViewLabels];
}
}
- (void)addScrollViewLabels {
const float kOriginY = 100;
if (!self.labels) {
self.labels = [NSMutableArray array];
float originX = [self.scrollView contentOffset].x;
UILabel *label0 = [[UILabel alloc] initWithFrame:CGRectMake(originX, kOriginY, 100, 30)];
label0.text = @"0";
[self.scrollView addSubview:label0];
[self.labels addObject:label0];
self.originLabel = label0;
}
CGSize contentSize = [self.scrollView contentSize];
const float kIncrementAmount = 75;
NSInteger indexOfOriginLabel = [self.labels indexOfObject:self.originLabel];
// add labels to the right
UILabel *lastLabel = [self.labels lastObject];
float lastOriginX = lastLabel.frame.origin.x;
for (float x = (lastOriginX + kIncrementAmount); (x < (contentSize.width)) ; x += kIncrementAmount) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x, kOriginY, 100, 30)];
NSInteger indexFromOrigin = ([self.labels count] - indexOfOriginLabel);
label.text = [@(indexFromOrigin) description];
[self.scrollView addSubview:label];
[self.labels addObject:label];
[label setNeedsDisplay];
}
// add labels to the left
UILabel *firstLabel = [self.labels firstObject];
float firstOriginX = firstLabel.frame.origin.x;
for (float x = (firstOriginX - kIncrementAmount); (x >= 0) ; x -= kIncrementAmount) {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x, kOriginY, 100, 30)];
NSInteger indexFromOrigin = -(indexOfOriginLabel + 1);
label.text = [@(indexFromOrigin) description];
[self.scrollView addSubview:label];
[self.labels insertObject:label atIndex:0];
indexOfOriginLabel++;
[label setNeedsDisplay];
}
}
- (void)adjustOrigins:(float)deltaX {
for (UILabel *label in self.labels) {
CGRect frame = label.frame;
frame.origin.x += deltaX;
[label setFrame:frame];
[label setNeedsDisplay];
}
}
@end