UIScrollView无限滚动

我试图设置无限(水平)滚动滚动视图。

向前滚动很简单 – 我已经实现了scrollViewDidScroll,当contentOffset接近尾声时,我将scrollview的内容做得更大,并将更多的数据添加到空间中(我将必须处理稍后将会出现的残缺效果)。

我的问题是滚动回来 – 计划是看我什么时候接近滚动视图的开始,然后当我做内容大一点,移动现有的内容,添加新的数据到开始,然后 – 重要调整contentOffset使得视图端口下的数据保持不变。

如果我慢慢滚动(或启用分页),但是如果我快速(甚至不是非常快! 这里是代码:

- (void) scrollViewDidScroll:(UIScrollView *)scrollView { float pageNumber = scrollView.contentOffset.x / 320; float pageCount = scrollView.contentSize.width / 320; if (pageNumber > pageCount-4) { //Add 10 new pages to end mainScrollView.contentSize = CGSizeMake(mainScrollView.contentSize.width + 3200, mainScrollView.contentSize.height); //add new data here at (320*pageCount, 0); } //*** the problem is here - I use updatingScrollingContent to make sure its only called once (for accurate testing!) if (pageNumber < 4 && !updatingScrollingContent) { updatingScrollingContent = YES; mainScrollView.contentSize = CGSizeMake(mainScrollView.contentSize.width + 3200, mainScrollView.contentSize.height); mainScrollView.contentOffset = CGPointMake(mainScrollView.contentOffset.x + 3200, 0); for (UIView *view in [mainContainerView subviews]) { view.frame = CGRectMake(view.frame.origin.x+3200, view.frame.origin.y, view.frame.size.width, view.frame.size.height); } //add new data here at (0, 0); } //** MY CHECK! NSLog(@"%f", mainScrollView.contentOffset.x); } 

当滚动发生日志读取:1286.500000 1285.500000 1284.500000 1283.500000 1282.500000 1281.500000 1280.500000

然后,当pageNumber <4(我们接近开始):4479.500000 4479.500000

大! – 但数字应该继续下降在4000年,但接下来的日志条目:1278.000000 1277.000000 1276.500000 1275.500000等….

继续从那里停止!

只是为了logging,如果滚动缓慢日志读取:1294.500000 1290.000000 1284.500000 1280.500000 4476.000000 4476.000000 4473.000000 4470.000000 4467.500000 4464.000000 4460.500000 4457.500000等….

有任何想法吗????

谢谢

本。

这可能是,无论在那里设置这些数字,都不会对你设置contentOffset留下深刻的印象。 所以它只是继续设置它认为应该是下一个时刻的contentOffset – 而不validationcontentOffset是否在此期间发生了变化。

我将inheritanceUIScrollView,并把这个魔术放在setContentOffset方法中。 根据我的经验,所有内容偏移量的变化都会经过这个方法,甚至是由内部滚动引起的内容偏移量的变化。 只要在某个时刻执行[super setContentOffset:..]将消息传递给真正的UIScrollView。

也许如果你把你的转移动作放在那里,它会更好。 您至less可以检测到contentOffset的3000关设置,并在传递消息之前对其进行修复。 如果您还要覆盖contentOffset方法,则可以尝试查看是否可以创build虚拟无限大的内容,并将其缩小为“底层”的实际比例。

我发现了一个非常棒的苹果示例应用程序来实现在OP中使用类似的想法无限滚动。 非常简单,最重要的是不撕裂

http://developer.apple.com/library/ios/#samplecode/StreetScroller/Introduction/Intro.html

每次在UIScrollView上调用layoutSubviews ,他们都实现了“内容重新调度”。

我做的唯一的调整就是回收“平铺效果”,而不是丢弃旧的瓷砖和分配新的瓷砖。

当我遇到这个问题时,我有3张图片,需要能够在任一方向无限滚动。 最佳的解决scheme可能是当用户移动到最右边/最左边的图像时加载3个图像并修改内容面板,但我find了一个更简单的解决scheme。 (对代码进行了一些编辑,可能包含拼写错误)

而不是3个图像,我已经设置了5个图像的滚动视图,如:

 3 | 1 | 2 | 3 | 1 

并相应地计算内容视图,而内容大小是固定的。 这里是代码:

 for ( NSUInteger i = 1; i <= NO_OF_IMAGES_IN_SCROLLVIEW + 2 ; i ++) { UIImage *image; if ( i % 3 == 1){ image = [UIImage imageNamed:[NSString stringWithFormat:@"img1.png"]]; } else if (i % 3 == 2 ) { image = [UIImage imageNamed:[NSString stringWithFormat:@"img2.png"]]; } else { image = [UIImage imageNamed:[NSString stringWithFormat:@"img3.png"]]; } UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake((i-1)*_scrollView.frame.size.width, 0, _scrollView.frame.size.width, _scrollView.frame.size.height)]; imageView.contentMode=UIViewContentModeScaleToFill; [imageView setImage:image]; imageView.tag=i+1; [self.scrollView addSubview:imageView]; } [self.scrollView setContentOffset:CGPointMake(self.scrollView.frame.size.width, 0)]; [scrMain setContentSize:CGSizeMake(self.scrollView.frame.size.width * ( NO_OF_IMAGES_IN_SCROLLVIEW + 2 ), self.scrollView.frame.size.height)]; 

现在图像被添加,唯一的是创造无限滚动的错觉。 要做到这一点,只要他试图滚动到外部的两个图像之一,我已经“传送”了用户到三个主要图像。 这样做是非常重要的,这样用户将无法感觉到:

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView.contentOffset.x < scrollView.frame.size.width ){ [scrollView scrollRectToVisible:CGRectMake(scrollView.contentOffset.x + 3 * scrollView.frame.size.width, 0, scrollView.frame.size.width, scrollView.frame.size.height) animated:NO]; } } else if ( scrollView.contentOffset.x > 4 * scrollView.frame.size.width ){ [scrollView scrollRectToVisible:CGRectMake(scrollView.contentOffset.x - 3 * scrollView.frame.size.width, 0, scrollView.frame.size.width, scrollView.frame.size.height) animated:NO]; } } 

(希望我正确发布这个 – 我是新来的!?)

感谢 – 子类UIScrollView完美的作品 – 我还没有实现新的数据的转移和加载,但我有一个无限循环滚动的UIScrollView!

这里是代码:

 #import "BRScrollView.h" @implementation BRScrollView - (id)awakeFromNib:(NSCoder *)decoder { offsetAdjustment = 0; [super initWithCoder:decoder]; return self; } - (void)setContentOffset:(CGPoint)contentOffset { float realOffset = contentOffset.x + offsetAdjustment; //This happens when contentOffset has updated correctly - there is no need for the adjustment any more if (realOffset < expectedContentOffset-2000 || realOffset > expectedContentOffset+2000) { offsetAdjustment = 0; realOffset = contentOffset.x; } float pageNumber = realOffset / 320; float pageCount = self.contentSize.width / 320; if (pageNumber > pageCount-4) { offsetAdjustment -= 3200; realOffset -= 3200; } if (pageNumber < 4) { offsetAdjustment += 3200; realOffset += 3200; } //Save expected offset for next call, and pass the real offset on expectedContentOffset = realOffset; [super setContentOffset:CGPointMake(realOffset, 0)]; } - (void)dealloc { [super dealloc]; } @end 

笔记:

如果你真的想要一个无限循环,你需要调整数字 – 这个代码通知你,当你靠近边缘,并把你移动到超越中间

我的contentSize是6720(21页)

我只是在横向滚动的兴趣,所以只保存x值和硬编码为0!

我做了一个样例项目来解决你的问题。

你可以从下载代码

https://code.google.com/p/iphone-infinite-horizo​​ntal-scroller/

这两个方向无休止地滚动,并在旅途中加载图像。

我已经开发了这种uiscrollview,所以你可以检查我的项目在无限UIScrollView在两个方向

我做了一个UIScrollView的子类,只是做你想要的。 它只是在任何方向永远滚动,即使在两个方向同时。 它支持平滑的滚动和分页滚动

https://github.com/vasvf/NPInfiniteScrollView

也看看这个(video会给你一个快速的想法): http : //dev.doukasd.com/2011/04/infinite-scrolling-dial-control-for-ios/

这不是水平的,但应该是有用的。 我尝试了你所build议的内容,但是在滚动视图animation的时候,设置内容偏移从来没有看上去是正确的,animation总是会崩溃的。

你可以看到这个例子

 numbercolors=[[NSMutableArray alloc] init]; //total count of array is 49 numbercolors = [NSMutableArray arrayWithObjects:@"25",@"26",@"27",@"28",@"29",@"31",@"32",@"33",@"34",@"35","0",@"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", @"0",@"1",@"2",@"3",nil]; int x=2500; for (NSInteger index = 0; index < [numbercolors count]; index++) { UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; button.frame = CGRectMake(x ,0,29.0,77.0); button.tag = index; [button setTitle:[numbercolors objectAtIndex:index] forState:UIControlStateNormal]; [button addTarget:self action:@selector(didTapButton:) forControlEvents:UIControlEventTouchUpInside]; [coloringScroll addSubview:button]; x=x+70+29; } [coloringScroll setContentSize:CGSizeMake(5000+ (29+70)*[numbercolors count], 1)]; [coloringScroll setContentOffset:CGPointMake(2500+(29+70)*11, 0)]; - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView.contentOffset.x > 2500+(29+70)*4 + ((29+70)*36)) { [scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x-((29+70)*36), 0)]; } if (scrollView.contentOffset.x < 2500+(29+70)*4) { [scrollView setContentOffset:CGPointMake(scrollView.contentOffset.x+((29+70)*36), 0)]; } } 

所以一个问题是,在scrollViewDidScroll:delegate方法中设置contentOffset会导致委托方法在内部时再次触发。 所以我做的是删除委托> setContentOffset>再次设置委托。

这是我的代码:

 #import "ViewController.h" @interface ViewController () <UIScrollViewDelegate> @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