2010-08-07 18 views
16

Ich versuche, eine Scrollview mit unendlichen (horizontalen) Scrollen einzurichten.UIScrollView Infinite Scrolling

Scrollen nach vorne ist einfach - ich habe scrollViewDidScroll implementiert, und wenn der contentOffset fast das Ende erreicht, mache ich die scrollview-Inhaltsgröße größer und füge weitere Daten in den Raum ein (ich muss mich mit dem lähmenden Effekt befassen, den das später haben wird) !)

Mein Problem ist das Zurückscrollen - der Plan ist zu sehen, wenn ich in die Nähe des Anfanges der Bildlaufansicht komme, wenn ich die Inhaltsgröße größer mache, den vorhandenen Inhalt mitnehme, die neuen Daten am Anfang hinzufügen und dann - stellen Sie den contentOffset so ein, dass die Daten unter dem View-Port gleich bleiben.

Dies funktioniert perfekt, wenn ich langsam blättern (oder Paging aktivieren), aber wenn ich schnell gehe (nicht einmal sehr schnell!) Wird es verrückt! Heres der Code:

- (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); 
} 

Wie das Scrollen das Protokoll geschieht liest: 1286,500000 1285,500000 1284,500000 1283,500000 1282,500000 1281,500000 1280,500000

Dann, wenn Seitennummer < 4 (wir bekommen in der Nähe der Anfang): 4479.500000 4479.500000

Gr Essen! - aber die Zahlen sollten weiterhin in der 4,000s gehen, aber die nächsten Log-Einträge lesen: 1278,000000 1277,000000 1276,500000 1275,500000 etc ....

Continiuing von wo es aufgehört!

Nur für das Protokoll, wenn gescrollt langsam das Protokoll liest: 1294,500000 1290,000000 1284,500000 1280,500000 4476,000000 4476,000000 4473,000000 4470,000000 4467,500000 4464,000000 4460,500000 4457,500000 etc ....

Irgendwelche Ideen????

Dank

Ben.

Antwort

7

Es kann sein, dass, was auch immer diese Zahlen dort einstellt, nicht sehr beeindruckt ist, indem Sie das contentOffset unter seinen Händen setzen. Es wird also nur weiter gesetzt, was für den nächsten Augenblick das contentOffset sein sollte - ohne zu überprüfen, ob sich contentOffset in der Zwischenzeit geändert hat.

Ich würde UIScrollView Unterklasse und setzen Sie die Magie in die setContentOffset Methode. Meiner Erfahrung nach passiert jede Änderung des Inhaltsversatzes diese Methode, sogar die Veränderung des Inhaltsversatzes, die durch das interne Scrollen verursacht wird. Tun Sie einfach [super setContentOffset:..] an einem Punkt, um die Nachricht an die echte UIScrollView weiterzugeben.

Vielleicht, wenn Sie Ihre Schaltaktion in dort setzen, wird es besser funktionieren.Sie konnten die 3000-off-Einstellung von contentOffset zumindest erkennen und beheben, bevor Sie die Nachricht weiterleiten. Wenn Sie auch die Methode contentOffset überschreiben würden, könnten Sie versuchen, herauszufinden, ob Sie eine virtuelle unendliche Inhaltsgröße erstellen und diese auf "echte" Proportionen "unter der Haube" reduzieren können.

+1

Seien Sie vorsichtig, Scrollen, so scheint es 'setContentOffset: animierte:' aktuelle Scrollen annulliert, aber nicht 'setContentOffset:' ... Aufschalten 'setContentOffset:' in einem Nachkommen von ' UIScrollView' und Tweaking Offset funktioniert (zumindest auf iOS SDK 4.3). –

+0

Ich habe festgestellt, dass sowohl setContentOffset: animated: und setContentOffset: die Scrolling-Animation (iPad2 SDK 4.3) leider abbrechen. –

5

(hoffe ich das richtig bin Entsendung - ich bin neu hier !?)

MVDS war genau richtig - dank - Subklassen UIScrollView funktioniert perfekt - ich bin noch das Verschieben und Laden von neuen Daten zu implementieren , aber ich habe eine UIScrollView, die in einer Endlosschleife läuft!

Heres der Code:

#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 

Hinweise:

Wenn Sie tatsächlich eine Endlosschleife wollen, müssen Sie würden die Zahlen anpassen - diese Codes bemerkt, wenn Sie in der Nähe der Kante erhalten und bewegt Sie nur über die Mitte

My content ist 6720 (21 Seiten)

ich horizontal in Scrollen, nur Interesse bin so nur die x-Werte und hart Co speichern de das y bis 0!

Ben

0

Werfen Sie einen Blick auf diese zu (das Video, das Sie eine kurze Vorstellung davon, was es tut): http://dev.doukasd.com/2011/04/infinite-scrolling-dial-control-for-ios/

Es ist nicht horizontal ist, aber es sollte nützlich sein. Ich habe versucht, was Sie vorgeschlagen haben, aber das Einstellen des Inhalts-Offsets sah nie richtig aus, während die Bildlauf-Animation animierte, die Animation würde immer auseinander gehen.

5

Ich habe ein Beispielprojekt gemacht, um Ihr Problem zu lösen.

können Sie den Code aus

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

diese Rollen in beide Richtungen endlos und lädt Bilder auf dem Sprung herunterladen.

+0

Kannst du das auf Github setzen? – zakdances

+1

https://github.com/centrajat26/IphoneHorizontalScrollView/ –

16

fand ich ein wirklich gutes Beispiel App von Apple Infinite Scrolling mit einer ähnlichen Idee im OP zu implementieren. Sehr einfach und am wichtigsten kein Zerreißen.

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

Sie implementiert, um den "Inhalt Rezentrierung" jedes Mal, layoutSubviews auf der UIScrollView genannt wurde.

Die einzige Anpassung, die ich vorgenommen habe, war, den "Kacheleffekt" zu recyceln, anstatt alte Kacheln wegzuwerfen und neue zuzuteilen.

+0

Wie zentrieren sie den Inhalt, ohne die Beschleunigung zu stoppen? – zakdances

+0

@yourfriendzak Alles, was sie tun ist, den Scroll-Offset neu zu zentrieren, sobald sie eine bestimmte Grenze erreicht, daher ist der Beschleunigungswert dabei unverändert. – rwyland

6

Als ich dieses Problem hatte, hatte ich 3 Bilder, die in der Lage sein mussten, unendlich in beide Richtungen zu scrollen. Die optimale Lösung wäre wahrscheinlich, 3 Bilder zu laden und das Inhaltsfeld zu ändern, wenn der Benutzer sich nach rechts/links bewegt, aber ich habe eine einfachere Lösung gefunden.(Habe einige Bearbeitung auf dem Code, kann Fehler enthalten)

Anstelle von 3 Bildern, ich habe Setup eine Scroll-Ansicht mit 5 Bildern, wie:

3 | 1 | 2 | 3 | 1 

und den Inhalt Ansicht entsprechend berechnet, während der Gehalt Größe ist festgelegt. Hier ist der Code:

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)]; 

Nun, da die Bilder hinzugefügt werden, ist die einzige Sache, die Illusion des unendlichen Scrollens zu erstellen. Um das zu tun, habe ich den Benutzer in die drei Hauptbilder "teleportiert", wann immer er versucht, zu einem der beiden äußeren Bilder zu scrollen. Es ist wichtig, dass nicht-animierte zu tun, so dass der Benutzer es nicht das Gefühl, in der Lage:

- (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

Sie dieses Beispiel sehen kann

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

So ein Problem ist, dass die Content Einstellung während in der ScrollViewDidScroll: delegate-Methode wird die Delegate-Methode erneut ausgelöst, während Sie darin sind. Also was ich tue, ist das Delegieren> setContentOffset> setzen Sie den Delegierten erneut.

Hier ist mein Code:

#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

Für meine Zwecke ist Apples StreetScroller-Demo ein Nein, da ich in der Lage sein muss, die Bildlaufanzeigen zu sehen. Mit dieser Vorgehensweise können Sie sie auch bei wachsender contentSize sehen. –

1

ich eine Unterklasse von UIScrollView gemacht haben, die genau das tut, was Sie wollen. Es scrollt für immer in alle Richtungen, sogar in beide Richtungen gleichzeitig. Es smooth scrolling und Paging unterstützt

https://github.com/vasvf/NPInfiniteScrollView

+0

Es blendet die Unteransicht von scrollView aus und benötigt viel Speicher. – TheTiger

Verwandte Themen