2010-12-13 47 views
0

Je tente d'implémenter une vue de défilement horizontale pour une application iPad. La vue de défilement montre une série d'images. Après le défilement, lorsque la vue défile s'arrête, je veux que les images soient alignées le long de points horizontaux spécifiques. Pour ce faire, j'ai essayé 2 approches:Arrêt d'un UIScrollView à une position spécifique

1) Désactiver le défilement pour UIScrollView et implémenter à la place un PanGestureRecogniser qui détecterait la translation et la vélocité du mouvement "glisser/glisser" et définirait le décalage de contenu de la vue déroulante en conséquence pour les points prédéterminés requis. Mais, le problème ici est que si l'utilisateur traîne lentement la vue de défilement, le mouvement de vue de défilement est très saccadé parce que les x récupérés coordonnés de la vue de défilement sautent d'avant en arrière autour du point où le doigt est pressé. Par exemple: les valeurs de x vont de 50 à 70 quand le doigt se déplace le long de 60-70. Comment puis-je rendre le mouvement de la vue de défilement lisse?

2) Activer le défilement pour UIScrollView et vérifier la fin de la décélération, puis régler le décalage de contenu de la vue déroulante pour l'ajuster à la position nécessaire. Mais le problème ici est que la vue de défilement ne s'ajuste à sa position finale qu'après avoir arrêté de décélérer et qu'il y a un retard visible et que cette animation ne semble pas bonne aussi. Y at-il un moyen que je peux détecter lorsque la vue défilement est sur le point d'arrêter de défiler et ensuite ajuster sa position au lieu d'attendre pour finir le défilement. D'après ce que j'ai lu sur certains forums, ce ne sera pas possible.

3) Une scrollview de pagination ne fonctionnera pas dans ce scénario car je ne veux pas que toute la largeur des images soit défilée.

Est-ce que quelqu'un a des idées sur la façon dont je peux mettre en œuvre la fonctionnalité ci-dessus.

Répondre

1

Vous pouvez toujours utiliser une vue de défilement de pagination pour cela. Tout ce que vous devez faire est de définir clipsToBounds à NO (de sorte que le contenu de la vue de défilement est autorisé à rendre en dehors de son cadre), puis définissez le frame aux étapes d'alignement souhaitées.

Par exemple, si vous souhaitez aligner la vue de défilement sur des multiples de 200, vous pouvez le faire:

scrollView.pagingEnabled = YES; 
scrollView.clipsToBounds = NO; 

// horizontally center scroll view with a width of 200 and the parent's height 
scrollView.frame = CGRectMake(
    floor((parentView.bounds.size.width - 200)/2), 
    0, 
    200, 
    parentView.bounds.size.height 
); 

[parentView addSubview:scrollView]; 
+0

vrai.Mais dans ce cas, la zone "scrollable/swipe-able" ne sera-t-elle pas limitée à une largeur de 200? Cela signifie que l'utilisateur ne pourra pas balayer la portion horizontale complète dans laquelle les images sont restituées et sera limité au défilement/balayage uniquement dans ces 200 unités. Je veux que l'utilisateur puisse faire défiler toute la largeur de la vue défilée. – Nathan

+0

Vous pourriez contourner ce problème en sous-classant 'UIScrollView' et en surchargeant' hitTest: withEvent: 'pour retourner' YES' pour n'importe quoi dans la zone que vous voulez faire défiler (la largeur de la vue parent, dans ce cas). –

+0

Ok, je devrais essayer ça. Mais, avec une vue de défilement de pagination vient un autre problème fondamental. Si l'utilisateur fait un "balayage rapide", alors une seule image (ou un ensemble fixe d'images) défilerait chaque fois à droite? Ce ne serait pas comme une vue de défilement libre où le nombre d'images défilant à chaque fois varierait selon la distance et la vitesse du balayage. Désolé si cela ressemble à im jouer le défenseur du diable ici, mais je suis juste en train d'essayer d'obtenir la fonctionnalité correcte. – Nathan

2

J'ai été confronté au même problème avec un scrollview vertical et utilisé l'approche 2. Même Si cette question est ancienne, je n'ai pas trouvé de solution ici et je pense que ce serait formidable de répondre.

La réponse courte est la méthode scrollViewWillEndDragging: withVelocity: targetContentOffset: dans le protocole UIScrollViewDelegate.

Il vous donne le targetContentOffset lorsque l'utilisateur a fini de défiler et que l'animation commence à décélérer. Vous pouvez modifier targetContentOffset dans cette méthode, afin que l'animation se termine exactement là où vous le pouvez. Votre travail consiste uniquement à calculer la position cible.

Voici la sous-classe UIView que j'ai créée. Je voulais créer une classe semblable à UIPickerView, mais je voulais qu'elle s'arrête sur des positions spécifiques (comme le sélecteur de date)

J'ai créé une sous-classe UIView conforme à UIScrollViewDelegate. Dans cet UIView, j'ai ajouté un scrollview. Cette scrollview contiendra des instances UILabel. Attention, j'utilise ARC, donc je ne libère rien.

#import <UIKit/UIKit.h> 

@protocol MyPickerViewDataSource <NSObject> 

-(NSUInteger)numberOfRowsForSender:(id)sender; 
-(NSString *)titleForRowAtIndex:(NSUInteger)rowIndex sender:(id)sender; 

@end 

@interface MyPickerView : UIView <UIScrollViewDelegate> 

@property (weak, nonatomic) id<MyPickerViewDataSource> dataSource; 

@end 

La mise en œuvre:

#import "MyPickerView.h" 

#define CONTENT_ROW_HEIGHT 30 

typedef enum { 
    ScrollDirectionUp, 
    ScrollDirectionDown 
} ScrollDirection; 

@interface MyPickerView() { 

    int topOffset; 
    int bottomOffset; 
    int lastContentOffset; 
    int targetLabelIndex; 
    ScrollDirection scrollDirection; 
} 

@implementation PickerView 

- (void)initSetup { 
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(self.frame.origin.x,self.frame.origin.y, self.frame.size.width, self.frame.size.height)]; 
self.scrollView.delegate = self; 
self.scrollView.showsHorizontalScrollIndicator = NO; 
self.scrollView.showsVerticalScrollIndicator = NO; 
self.scrollView.userInteractionEnabled = YES; 
[self addSubview:self.scrollView]; 

targetLabelIndex = 0; 

topOffset = (self.scrollView.frame.size.height/2)-CONTENT_ROW_HEIGHT/2; 
bottomOffset = topOffset; 
} 

- (id)initWithCoder:(NSCoder *)aDecoder { 
    self = [super initWithCoder:aDecoder]; 
    if(self) { 
     [self initSetup]; 
    } 
    return self; 
} 

- (id)initWithFrame:(CGRect)frame { 
    self = [super initWithFrame:frame]; 
    if(self) { 
     [self initSetup]; 
    } 
    return self; 
} 

- (void)layoutSubviews { 

    [super layoutSubviews]; 

    NSUInteger numberOfRows = [self.dataSource numberOfRowsForSender:self]; 

    self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width, topOffset+numberOfRows*CONTENT_ROW_HEIGHT+bottomOffset); 

    for (int index = 0; index < numberOfRows; index++) 
    { 
     UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(self.scrollView.frame.origin.x, 
                    topOffset+index*CONTENT_ROW_HEIGHT, 
                    self.scrollView.frame.size.width, 
                    CONTENT_ROW_HEIGHT)]; 

     label.backgroundColor = [UIColor clearColor]; 
     label.textColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0]; 
     label.text = [self.dataSource titleForRowAtIndex:index sender:self]; 

     [self.scrollView addSubview:label]; 
    } 

    [(UILabel *)[self.scrollView.subviews objectAtIndex:targetLabelIndex] setTextColor:[UIColor blueColor]]; 
} 

- (void)reloadData { 

    NSUInteger numberOfRows = [self.dataSource numberOfRowsForSender:self]; 

    self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width, numberOfRows*CONTENT_ROW_HEIGHT); 

    [self.scrollView setContentOffset:CGPointMake(0.0, 0.0) animated:NO]; 

    [self layoutSubviews]; 
} 

#pragma mark - UIScrollViewDelegate 

- (void)scrollViewDidScroll:(UIScrollView *)scrollView { 

    if(lastContentOffset>self.scrollView.contentOffset.y) { 

     scrollDirection = ScrollDirectionUp; 
    } 
    else { 

     scrollDirection = ScrollDirectionDown; 
    } 

    lastContentOffset = self.scrollView.contentOffset.y; 
} 

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { 

    targetLabelIndex = (targetContentOffset->y/CONTENT_ROW_HEIGHT)+1; 

    if(scrollDirection==ScrollDirectionUp && targetLabelIndex>0) { 

     targetLabelIndex--; 
    } 

    targetContentOffset->y = targetLabelIndex*CONTENT_ROW_HEIGHT; 

    [(UILabel *)[self.scrollView.subviews objectAtIndex:targetLabelIndex] setTextColor:[UIColor blueColor]]; 
} 

@end 
+0

Merci de l'avoir posté; J'ai rencontré le même problème avec un UICollectionView. Je note que la méthode de documentation suggère cette approche: "Votre application peut changer la valeur du paramètre targetContentOffset pour ajuster où le scrollview termine son animation de défilement." – JLundell