我正在尝试设置无限(水平)滚动的滚动视图。
向前滚动很简单 - 我已经实现了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&lt; 4(我们接近开头)时: 4479.500000 4479.500000
大! - 但数字应继续在4,000s下降,但下一个日志条目如下: 1278.000000 1277.000000 1276.500000 1275.500000 等....
从它停止的地方继续!
只是为了记录,如果慢慢滚动,日志会显示: 1294.500000 1290.000000 1284.500000 1280.500000 4476.000000 4476.000000 4473.000000 4470.000000 4467.500000 4464.000000 4460.500000 4457.500000 等....
任何想法????
由于
本。
答案 0 :(得分:17)
我找到了Apple的一个非常好的示例应用程序,用于在OP中使用类似的想法实现Infinite Scrolling。非常简单,最重要的是没有撕裂。
http://developer.apple.com/library/ios/#samplecode/StreetScroller/Introduction/Intro.html
他们实施了&#34;内容重新定位&#34;每次在UIScrollView上调用layoutSubviews
。
我做的唯一调整是回收&#34;平铺效果&#34;而不是扔掉旧瓷砖并分配新瓷砖。
答案 1 :(得分:7)
可能无论在那里设置这些数字,你都不会将contentOffset置于其手中。所以它只是继续设置它认为应该是下一个瞬间的contentOffset - 而不验证contentOffset是否在此期间发生了变化。
我会将UIScrollView子类化,并将魔法置于setContentOffset
方法中。根据我的经验,所有内容偏移更改都会通过该方法,甚至是内部滚动引起的内容偏移更改。只需在某个时刻执行[super setContentOffset:..]
即可将消息传递给真正的UIScrollView。
也许如果你把你的换挡动作放在那里它会更好。您至少可以检测到contentOffset的3000关设置,并在传递消息之前修复它。如果您还要覆盖contentOffset
方法,则可以尝试查看是否可以制作虚拟无限内容大小,并将其缩小到“真空比例”。
答案 2 :(得分:6)
当我遇到这个问题时,我有3个图像,需要能够向任一方向无限滚动。最佳解决方案可能是在用户移动到最右/最左边的图像时加载3个图像并修改内容面板,但我找到了一个更简单的解决方案。 (对代码进行了一些编辑,可能包含拼写错误)
我设置了一个包含5个图像的滚动视图,而不是3个图像,如:
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];
}
}
答案 3 :(得分:5)
(希望我发布的信息正确 - 我是新来的!?)
mvds是现货 - 谢谢 - 子类化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值并将y硬编码为0!
本
答案 4 :(得分:5)
我已经制作了一个示例项目来解决您的问题。
您可以从
下载代码https://code.google.com/p/iphone-infinite-horizontal-scroller/
这两个方向无休止地滚动并在旅途中加载图像。
答案 5 :(得分:1)
我开发了这种uiscrollview,因此您可以在Infinite UIScrollView in both direction检查我的项目
答案 6 :(得分:1)
我已经创建了UIScrollView的子类,它只是做你想要的。它只是在任何方向永远滚动,即使在两个方向同时。 它支持平滑滚动和分页滚动
答案 7 :(得分:0)
也看看这个(视频会让你快速了解它的作用):http://dev.doukasd.com/2011/04/infinite-scrolling-dial-control-for-ios/
它不是水平的,但它应该是有用的。我尝试了你的建议,但是当滚动视图动画时,设置内容偏移永远不会正确,动画总是会分解。
答案 8 :(得分:0)
您可以看到此示例
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)];
}
}
答案 9 :(得分:0)
因此,一个问题是在scrollViewDidScroll:delegate方法中设置contentOffset会导致委托方法在您进入时再次触发。所以我要做的是删除委托&gt; setContentOffset&gt;再次设置代理。
这是我的代码:
#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