2010-08-07 130 views
16

我試圖設置無限(水平)滾動滾動視圖。滾動向前很容易 - 我已經實現了scrollViewDidScroll,並且當contentOffset接近結束時,我將scrollview內容做得更大,並將更多數據添加到空間中(我將不得不處理稍後會有的殘缺效果) !)UIScrollView無限滾動

我的問題是滾動回來 - 計劃是看我什麼時候接近滾動視圖的開始,然後當我確定內容更大時,將現有內容一起移動,將新數據添加到開始處然後 - 重要的是調整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

Gr吃! - 但數字應該繼續往下走的4,000秒,但接下來的日誌條目閱讀: 1278.000000 1277.000000 1276.500000 1275.500000 等....

從那裏離開Continiuing!

只是爲了記錄在案,如果慢慢地滾動日誌上寫着: 1294.500000 1290.000000 1284.500000 1280.500000 4476.000000 4476.000000 4473.000000 4470.000000 4467.500000 4464.000000 4460.500000 4457.500000 等....

有任何想法嗎????

謝謝

本。

回答

7

可能是因爲無論在那裏設置這些數字,都不會在您將contentOffset設置在其手中時留下深刻的印象。因此,它只是繼續設置它認爲應該是下一個時刻的contentOffset,而不驗證contentOffset是否在此期間發生了變化。

我會繼承UIScrollView並將魔法放在setContentOffset方法中。根據我的經驗,所有內容偏移量變化都會通過該方法,即使內部滾動引起的內容偏移量變化也是如此。在某些時候只需執行[super setContentOffset:..]即可將消息傳遞給真正的UIScrollView。

也許如果你把你的移動動作放在那裏,它會更好。您至少可以檢測contentOffset的3000關設置,並在傳遞消息之前對其進行修復。如果您還要覆蓋contentOffset方法,則可以嘗試查看是否可以製作虛擬無限大的內容,並將其縮小爲「底層」的實際比例。

+1

小心,似乎'setContentOffset:動畫:'取消當前的滾動,而不是'setContentOffset:'...'重寫setContentOffset:在''的後裔UIScrollView'並調整偏移量(至少在iOS SDK 4.3上)。 – 2011-06-30 08:33:36

+0

我注意到setContentOffset:animated:和setContentOffset:不幸地取消了滾動動畫(iPad2 SDK 4.3)。 – 2011-10-03 12:28:23

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!

5

我做了一個示例項目來解決您的問題。

你可以從

https://code.google.com/p/iphone-infinite-horizontal-scroller/

在旅途中不斷地雙向和裝載圖像下載代碼這個卷軸。

+0

你可以把它放在Github上嗎? – zakdances 2013-03-23 01:50:18

+1

https://github.com/cuterajat26/IphoneHorizo​​ntalScrollView/ – 2013-04-20 11:08:38

16

檢查項目中,我發現了一個真正偉大的示例應用程序被蘋果使用在OP類似的想法來實現無限滾動。非常簡單,最重要的是不撕裂

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

他們實施了 「內容recentering」 每layoutSubviews被稱爲在UIScrollView的時間。

我做的唯一調整是回收「平鋪效果」,而不是丟棄舊瓷磚和分配新瓷磚。

+0

他們如何在不停止加速的情況下更新內容? – zakdances 2013-03-23 01:49:44

+0

@yourfriendzak他們所做的一切是在它到達特定邊界時重新調整滾動偏移量,因此加速度值在此過程中保持不變。 – rwyland 2013-03-23 16:51:21

6

當我遇到這個問題時,我有3張圖片,需要能夠在任一方向無限滾動。最佳解決方案可能是在用戶移動到最右/最左側圖像時加載3張圖像並修改內容面板,但我找到了更簡單的解決方案。(是否在代碼一些編輯,可能包含錯別字)

代替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]; 
    } 
} 
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)]; 
} 

} 
0

所以一個問題是,設置contentOffset而在scrollViewDidScroll:委託方法將導致委託方法,當你在裏面時再次觸發。所以我所做的是刪除委託> 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 
+0

爲了我的目的,Apple的StreetScroller演示是不行的,因爲我需要能夠看到滾動視圖指示器。即使contentSize增長,這種方法也可以讓你看到它們。 – 2013-11-15 19:00:25

1

我做的UIScrollView的子類,只是你想要做什麼。它只是在任何方向永遠滾動,即使在兩個方向同時。 它支持平滑滾動和翻頁滾動

https://github.com/vasvf/NPInfiniteScrollView

+0

它隱藏了scrollView的子視圖並佔用大量內存。 – TheTiger 2016-10-02 03:26:08