iphone - doesn - UIScrollView Infinite Scrolling
uiscrollview swift 4 (10)
Estoy intentando configurar una vista de desplazamiento con desplazamiento infinito (horizontal).
Desplazarse hacia adelante es fácil: he implementado scrollViewDidScroll, y cuando contentOffset se acerca al final, hago que el tamaño de la vista de scroll sea más grande y añada más datos al espacio (¡tendré que lidiar con el efecto paralizante que esto tendrá más adelante!)
Mi problema es desplazarse hacia atrás: el plan es ver cuándo me acerco al comienzo de la vista de desplazamiento, luego, cuando amplío el tamaño del contenido, muevo el contenido existente, agregue los nuevos datos al principio y luego, ajuste de manera importante contentOffset para que los datos bajo el puerto de vista permanezcan iguales.
Esto funciona perfectamente si me desplazo lentamente (o habilito la paginación) pero si voy rápido (¡ni siquiera muy 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 ocurre el desplazamiento, el registro dice: 1286.500000 1285.500000 1284.500000 1283.500000 1282.500000 1281.500000 1280.500000
Luego, cuando pageNumber <4 (nos estamos acercando al principio): 4479.500000 4479.500000
¡Genial! - pero los números deberían continuar disminuyendo en los 4.000, pero las siguientes entradas de registro dicen: 1278.000000 1277.000000 1276.500000 1275.500000, etc.
Continuando desde donde lo dejó!
Solo para el registro, si se desplaza lentamente, el registro dice: 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
(Espero que esté publicando esto correctamente, ¿soy nuevo aquí?)
mvds fue perfecto - gracias - la subclasificación de UIScrollView funciona a la perfección - todavía tengo que implementar el desplazamiento y la carga de nuevos datos, pero tengo un UIScrollView que se desplaza en un bucle sin fin.
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 realmente quieres un bucle infinito, deberías ajustar los números, esto indica los códigos cuando te acercas al borde y te mueven justo más allá del medio
Mi contentSize es 6720 (21 páginas)
¡Solo me interesa desplazarme horizontalmente, así que solo guarde los valores de x y codifique la y en 0!
Ben
Cuando me enfrenté a este problema, tenía 3 imágenes, que necesitaban 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 de la derecha / izquierda, pero he encontrado una solución más fácil. (Hice alguna edición en el código, podría contener errores tipográficos)
En lugar de 3 imágenes, he configurado una vista de desplazamiento con 5 imágenes, como:
3 | 1 | 2 | 3 | 1
y calculó la vista de contenido en consecuencia, mientras que el tamaño del contenido 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 han agregado las imágenes, lo único es crear la ilusión de desplazamiento infinito. Para hacer eso, he "teletransportado" al usuario a la imagen principal tres, siempre que intenta desplazarse a una de las dos imágenes exteriores. Es importante hacer eso no animado, para que el usuario no pueda 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];
}
}
Eche un vistazo a esto también (el video le dará una idea rápida de lo que hace): http://dev.doukasd.com/2011/04/infinite-scrolling-dial-control-for-ios/
No es horizontal pero debería ser útil. Intenté lo que estaba sugiriendo, pero la configuración del desplazamiento del contenido nunca se vio bien mientras la vista de desplazamiento estaba animando, la animación siempre se rompía.
Encontré una gran aplicación de ejemplo de Apple para implementar Infinite Scrolling usando una idea similar en el OP. Muy simple y lo más importante, sin desgarros .
http://developer.apple.com/library/ios/#samplecode/StreetScroller/Introduction/Intro.html
Implementaron la "reclasificación de contenido" cada vez que se layoutSubviews
en UIScrollView.
El único ajuste que hice fue reciclar el "efecto mosaico" en lugar de tirar las baldosas viejas y asignar otras nuevas.
He desarrollado este tipo de uiscrollview, así que puedes ver mi proyecto en Infinite UIScrollView en ambas direcciones.
He hecho un proyecto de muestra para resolver su problema.
puedes descargar el codigo desde
https://code.google.com/p/iphone-infinite-horizontal-scroller/
esto se desplaza en ambas direcciones sin fin y carga las imágenes sobre la marcha.
He hecho una subclase de UIScrollView que solo hace lo que quieres. Simplemente se desplaza para siempre en cualquier dirección, incluso en ambas direcciones simultáneamente. Es compatible con desplazamiento suave y desplazamiento de paginación
Podría ser que lo que sea que esté configurando esos números allí, no quede muy impresionado si configura el ContentOffset bajo sus manos. Por lo tanto, simplemente establece lo que cree que debería ser contentOffset para el próximo instante, sin verificar si el contentOffset ha cambiado mientras tanto.
Yo subclasificaría UIScrollView y pondría la magia en el método setContentOffset
. En mi experiencia, todo cambio de desplazamiento de contenido pasa a través de ese método, incluso el cambio de desplazamiento de contenido inducido por el desplazamiento interno. Simplemente haga [super setContentOffset:..]
en algún momento para pasar el mensaje al UIScrollView real.
Tal vez si pones tu acción de cambio allí funcionará mejor. Al menos podría detectar la configuración de desactivación 3000 de contentOffset y corregirla antes de pasar el mensaje. Si también anulara el método contentOffset
, podría intentar ver si puede crear un tamaño de contenido infinito virtual y reducirlo a proporciones reales "bajo el capó".
Por lo tanto, un problema es que la configuración de contentOffset en el método scrollViewDidScroll: delegate hará que el método delegate se vuelva a disparar mientras estás dentro. Entonces, lo que hago es eliminar el delegado> setContentOffset> establecer el delegado nuevamente.
Aquí está mi código:
#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
Puedes 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)];
}
}