2010-08-07 9 views
16

Estoy intentando configurar una vista de desplazamiento con desplazamiento infinito (horizontal).UIScrollView Infinite Scrolling

Desplazar hacia adelante es fácil - Implementé scrollViewDidScroll, y cuando el contentOffset se acerca al final, agrandé el contenido de scrollview y agregué más datos al espacio (tendré que lidiar con el efecto paralizante que esto tendrá más adelante !)

Mi problema es el desplazamiento hacia atrás - el plan es ver cuándo estoy cerca del comienzo de la vista de desplazamiento, luego cuando hago que el contenido sea más grande, mueva el contenido existente, agregue los datos nuevos al comienzo y luego - importantemente ajuste el contentOffset para que los datos debajo del puerto de vista se mantengan iguales.

Esto funciona perfectamente si me desplazo lentamente (o habilito la paginación) pero si voy rápido (¡ni tan rápido!) ¡Se vuelve loco! Aquí está el código:

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

A medida que el desplazamiento ocurre el registro lee: 1286,500000 1285,500000 1284,500000 1283,500000 1282,500000 1281,500000 1280,500000

Entonces, cuando numPag < 4 (nos estamos cerca de la comenzando): 4479.500000 4479.500000

Gr ¡comer! - pero los números deben continuar a bajar en los 4,000s pero las entradas de registro siguiente lectura: 1278,000000 1277,000000 1276,500000 1275,500000 etc ....

Continiuing desde donde lo dejó!

Sólo para que conste, si es desplazado lentamente el registro de lecturas: 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 ....

¿¿¿¿Algunas ideas????

Gracias

Ben.

Respuesta

7

Podría ser que cualquiera que sea la configuración de esos números allí, no queda muy impresionado al configurar el contentOffset en sus manos. Por lo tanto, solo sigue configurando lo que cree que debería ser el contentOffset para el próximo instante, sin verificar si el contentOffset ha cambiado mientras tanto.

Subclase UIScrollView y pongo la magia en el método setContentOffset. En mi experiencia, todo el cambio de offset de contenido pasa a través de ese método, incluso el cambio de offset de contenido inducido por el desplazamiento interno. Simplemente haga [super setContentOffset:..] en algún momento para pasar el mensaje al verdadero UIScrollView.

Tal vez si pone su acción de desplazamiento allí, funcionará mejor.Al menos podría detectar la configuración de 3000 off de contentOffset y corregirla antes de pasar el mensaje. Si también anula el método contentOffset, podría intentar y ver si puede hacer un tamaño de contenido virtual infinito y reducirlo a proporciones reales "bajo el capó".

+1

tener cuidado, parece 'setContentOffset: animada:' cancela el desplazamiento actual, pero no 'setContentOffset:' '... Anulación de setContentOffset:' en un descendiente de ' UIScrollView' y ajustes de compensación (al menos en iOS SDK 4.3). –

+0

He notado que setContentOffset: animated: y setContentOffset: cancelan la animación de desplazamiento (iPad2 SDK 4.3) desafortunadamente. –

5

(espero que lo pongo esto correctamente - Soy nuevo aquí !?)

MVDS era perfecto - gracias - la subclasificación UIScrollView funciona perfectamente - estoy aún para implementar el desplazamiento y la carga de nuevos datos , pero tengo un UIScrollView que se desplaza en un ciclo infinito.

Aquí está el código:

#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 

Notas:

Si usted quiere realmente un bucle infinito, que había necesidad de ajustar los números - esto códigos de avisos cuando llegue cerca del borde y lo hace avanzar a poco más allá de la mitad

Mi contentSize es 6720 (21 páginas)

estoy único interés en desplazamiento horizontal, por lo que sólo guardar los valores de x y co duro de la y a 0!

Ben

5

He hecho un proyecto de muestra para resolver su problema.

se puede descargar el código desde

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

esta se desplaza en ambas direcciones sin fin y las cargas de imágenes en movimiento.

+0

¿Puedes poner esto en Github? – zakdances

+1

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

16

he encontrado una muy buena aplicación de ejemplo por Apple para implementar Infinito Desplazamiento usando una idea similar en el PO. Muy simple y lo más importante sin desgarro.

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

implementaron el "contenido recentrado" cada vez layoutSubviews se llamaba en la UIScrollView.

El único ajuste que hice fue reciclar el "efecto de mosaico" en lugar de tirar las fichas antiguas y asignar nuevas.

+0

¿Cómo recentran el contenido sin detener la aceleración? – zakdances

+0

@yourfriendzak Todo lo que hacen es volver a centrar el desplazamiento de desplazamiento una vez que alcanza un límite determinado, por lo que el valor de aceleración no cambia en el proceso. – rwyland

6

Cuando me enfrenté a este problema, tenía 3 imágenes, que debían poder desplazarse infinitamente en cualquier dirección. La solución óptima probablemente sería cargar 3 imágenes y modificar el panel de contenido cuando el usuario se mueva a la imagen más a la derecha/izquierda, pero he encontrado una solución más fácil.(¿Algunos de edición del código, puede contener errores tipográficos)

En lugar de 3 imágenes, He fijado una vista de desplazamiento con 5 imágenes, como:

3 | 1 | 2 | 3 | 1 

y calculado el punto de vista contenido en consecuencia, mientras que el contenido el tamaño es fijo Aquí está el código:

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

Ahora que se añaden las imágenes, lo único es crear la ilusión de movimiento en sentido vertical infinita. Para hacer eso, he "teletransportado" al usuario a la imagen principal de tres, cada vez que intenta desplazarse a una de las dos imágenes externas. Es importante hacer eso no animado, de modo que el usuario no será capaz de sentirlo:

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

Se puede ver este ejemplo

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

Así que un problema es que la fijación de la contentOffset mientras está en scrollViewDidScroll: el método de delegado hará que el método de delegado vuelva a dispararse mientras se encuentre dentro de él. Entonces lo que hago es eliminar el delegado> setContentOffset> configurar el delegado nuevamente.

Aquí está mi código de:

#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

Para mis propósitos, la demostración de StreetScroller de Apple es un no ir ya que necesito poder ver los indicadores de la vista de desplazamiento. Este enfoque te permite verlos incluso mientras crece el tamaño del contenido. –

1

He hecho una subclase de UIScrollView que simplemente hace lo que quiere. Simplemente se desplaza para siempre en cualquier dirección, incluso en ambas direcciones simultáneamente. Es compatible con el desplazamiento suave y paginación Desplazamiento

https://github.com/vasvf/NPInfiniteScrollView

+0

Oculta la subvista de scrollView y requiere mucha memoria. – TheTiger