web-dev-qa-db-fra.com

Trouver la direction du défilement dans un UIScrollView?

J'ai un UIScrollView dans lequel seul le défilement horizontal est autorisé et j'aimerais savoir dans quelle direction (gauche, droite) l'utilisateur fait défiler. Ce que j'ai fait était de sous-classer la UIScrollView et de remplacer la méthode touchesMoved:

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];

    UITouch *touch = [touches anyObject];
    float now = [touch locationInView:self].x;
    float before = [touch previousLocationInView:self].x;
    NSLog(@"%f %f", before, now);
    if (now > before){
        right = NO;
        NSLog(@"LEFT");
    }
    else{
        right = YES;
        NSLog(@"RIGHT");

    }

}

Mais parfois, cette méthode ne s'appelle pas du tout quand je déménage. Qu'est-ce que tu penses?

175
Alex1987

Déterminer la direction est assez simple, mais gardez à l'esprit que la direction peut changer plusieurs fois au cours d'un geste. Par exemple, si vous avez une vue de défilement avec la pagination activée et que l'utilisateur glisse pour aller à la page suivante, la direction initiale peut être orientée vers la droite, mais si vous avez activé le rebond, il ne suivra brièvement aucune direction et puis brièvement aller à gauche.

Pour déterminer la direction, vous devez utiliser le délégué UIScrollView scrollViewDidScroll. Dans cet exemple, j'ai créé une variable nommée lastContentOffset que j'utilise pour comparer l'offset de contenu actuel avec le précédent. Si c'est plus grand, alors le scrollView défile à droite. Si c'est moins que scrollView défile à gauche:

// somewhere in the private class extension
@property (nonatomic, assign) CGFloat lastContentOffset;

// somewhere in the class implementation
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    ScrollDirection scrollDirection;

    if (self.lastContentOffset > scrollView.contentOffset.x) {
        scrollDirection = ScrollDirectionRight;
    } else if (self.lastContentOffset < scrollView.contentOffset.x) {
        scrollDirection = ScrollDirectionLeft;
    }

    self.lastContentOffset = scrollView.contentOffset.x;

    // do whatever you need to with scrollDirection here.    
}

J'utilise l'énumération suivante pour définir la direction. Définir la première valeur sur ScrollDirectionNone présente l’avantage supplémentaire de faire de cette direction la valeur par défaut lors de l’initialisation des variables:

typedef NS_ENUM(NSInteger, ScrollDirection) {
    ScrollDirectionNone,
    ScrollDirectionRight,
    ScrollDirectionLeft,
    ScrollDirectionUp,
    ScrollDirectionDown,
    ScrollDirectionCrazy,
};
385
memmons

... je voudrais savoir dans quelle direction (gauche, droite) l'utilisateur fait défiler.

Dans ce cas, sur iOS 5 et versions ultérieures, utilisez la variable UIScrollViewDelegate pour déterminer la direction du mouvement de panoramique de l'utilisateur:

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{ 
    if ([scrollView.panGestureRecognizer translationInView:scrollView.superview].x > 0) {
        // handle dragging to the right
    } else {
        // handle dragging to the left
    }
}
72
followben

Utiliser scrollViewDidScroll: est un bon moyen de trouver la direction actuelle.

Si vous voulez connaître la direction après l'utilisateur a terminé le défilement, utilisez ce qui suit:

@property (nonatomic) CGFloat lastContentOffset;

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

    self.lastContentOffset = scrollView.contentOffset.x;
}

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

    if (self.lastContentOffset < scrollView.contentOffset.x) {
        // moved right
    } else if (self.lastContentOffset > scrollView.contentOffset.x) {
        // moved left
    } else {
        // didn't move
    }
}
57
Justin Tanner

Pas besoin d'ajouter une variable supplémentaire pour garder une trace de cela. Utilisez simplement la propriété UIScrollView 'panGestureRecognizer comme ceci. Malheureusement, cela ne fonctionne que si la vélocité n'est pas 0:

CGFloat yVelocity = [scrollView.panGestureRecognizer velocityInView:scrollView].y;
if (yVelocity < 0) {
    NSLog(@"Up");
} else if (yVelocity > 0) {
    NSLog(@"Down");
} else {
    NSLog(@"Can't determine direction as velocity is 0");
}

Vous pouvez utiliser une combinaison de composants x et y pour détecter vers le haut, le bas, la gauche et la droite.

49
rounak

La solution

func scrollViewDidScroll(scrollView: UIScrollView) {
     if(scrollView.panGestureRecognizer.translationInView(scrollView.superview).y > 0)
     {
         print("up")
     }
    else
    {
         print("down")
    } 
}
36
davidrelgr

Dans iOS8 Swift j'ai utilisé cette méthode:

override func scrollViewDidScroll(scrollView: UIScrollView){

    var frame: CGRect = self.photoButton.frame
    var currentLocation = scrollView.contentOffset.y

    if frame.Origin.y > currentLocation{
        println("Going up!")
    }else if frame.Origin.y < currentLocation{
        println("Going down!")
    }

    frame.Origin.y = scrollView.contentOffset.y + scrollHeight
    photoButton.frame = frame
    view.bringSubviewToFront(photoButton)

}

J'ai une vue dynamique qui change d'emplacement au fur et à mesure que l'utilisateur défile afin que la vue puisse sembler rester au même endroit à l'écran. Je suis également en train de suivre quand l'utilisateur monte ou descend.

Voici également un autre moyen:

func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    if targetContentOffset.memory.y < scrollView.contentOffset.y {
        println("Going up!")
    } else {
        println("Going down!")
    }
}
14
Esqarrouth

Swift 4:

Pour le défilement horizontal, vous pouvez simplement faire:

if scrollView.panGestureRecognizer.translation(in: scrollView.superview).x > 0 {
   print("left")
} else {
   print("right")
}

Pour le défilement vertical, changez .x avec .y

14
Alessandro Ornano

Voici ce que cela a fonctionné pour moi (en Objective-C):

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

        NSString *direction = ([scrollView.panGestureRecognizer translationInView:scrollView.superview].y >0)?@"up":@"down";
        NSLog(@"%@",direction);
    }
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {

    CGPoint targetPoint = *targetContentOffset;
    CGPoint currentPoint = scrollView.contentOffset;

    if (targetPoint.y > currentPoint.y) {
        NSLog(@"up");
    }
    else {
        NSLog(@"down");
    }
}
7
Oded Regev

Alternativement, il est possible d'observer le chemin de clé "contentOffset". Ceci est utile lorsqu'il vous est impossible de définir/modifier le délégué de la vue de défilement.

[yourScrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

Après avoir ajouté l'observateur, vous pouvez maintenant:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    CGFloat newOffset = [[change objectForKey:@"new"] CGPointValue].y;
    CGFloat oldOffset = [[change objectForKey:@"old"] CGPointValue].y;
    CGFloat diff = newOffset - oldOffset;
    if (diff < 0 ) { //scrolling down
        // do something
    }
}

N'oubliez pas de retirer l'observateur en cas de besoin. par exemple. vous pouvez ajouter l'observateur dans viewWillAppear et le supprimer dans viewWillDisappear

5
xu huanze

Voici ma solution pour un comportement comme dans la réponse @followben, mais sans perte avec un démarrage lent (lorsque dy est égal à 0)

@property (assign, nonatomic) BOOL isFinding;
@property (assign, nonatomic) CGFloat previousOffset;

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    self.isFinding = YES;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (self.isFinding) {
        if (self.previousOffset == 0) {
            self.previousOffset = self.tableView.contentOffset.y;

        } else {
            CGFloat diff = self.tableView.contentOffset.y - self.previousOffset;
            if (diff != 0) {
                self.previousOffset = 0;
                self.isFinding = NO;

                if (diff > 0) {
                    // moved up
                } else {
                    // moved down
                }
            }
        }
    }
}
4
Andrey Zhukov

En rapide:

    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0 {
        print("down")
    } else {
        print("up")
    }
}

Vous pouvez le faire aussi dans scrollViewDidScroll.

Je préfère effectuer un filtrage en fonction de la réponse de @ memmons

En Objective-C:

// in the private class extension
@property (nonatomic, assign) CGFloat lastContentOffset;

// in the class implementation
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    if (fabs(self.lastContentOffset - scrollView.contentOffset.x) > 20 ) {
        self.lastContentOffset = scrollView.contentOffset.x;
    }

    if (self.lastContentOffset > scrollView.contentOffset.x) {
        //  Scroll Direction Left
        //  do what you need to with scrollDirection here.
    } else {
        //  omitted 
        //  if (self.lastContentOffset < scrollView.contentOffset.x)

        //  do what you need to with scrollDirection here.
        //  Scroll Direction Right
    } 
}

Lorsque testé dans - (void)scrollViewDidScroll:(UIScrollView *)scrollView:

NSLog(@"lastContentOffset: --- %f,   scrollView.contentOffset.x : --- %f", self.lastContentOffset, scrollView.contentOffset.x);

img

self.lastContentOffset change très rapidement, l'écart de valeur est proche de 0.5f.

Ce n'est pas nécessaire.

Et de temps en temps, lorsque vous êtes manipulé dans des conditions précises, votre orientation peut être perdue. (déclarations de mise en œuvre parfois ignorées)

tel que :

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

    CGFloat viewWidth = scrollView.frame.size.width;

    self.lastContentOffset = scrollView.contentOffset.x;
    // Bad example , needs value filtering

    NSInteger page = scrollView.contentOffset.x / viewWidth;

    if (page == self.images.count + 1 && self.lastContentOffset < scrollView.contentOffset.x ){
          //  Scroll Direction Right
          //  do what you need to with scrollDirection here.
    }
   ....

Dans Swift 4:

var lastContentOffset: CGFloat = 0

func scrollViewDidScroll(_ scrollView: UIScrollView) {

     if (abs(lastContentOffset - scrollView.contentOffset.x) > 20 ) {
         lastContentOffset = scrollView.contentOffset.x;
     }

     if (lastContentOffset > scrollView.contentOffset.x) {
          //  Scroll Direction Left
          //  do what you need to with scrollDirection here.
     } else {
         //  omitted
         //  if (self.lastContentOffset < scrollView.contentOffset.x)

         //  do what you need to with scrollDirection here.
         //  Scroll Direction Right
    }
}
1
black_pearl

J'ai vérifié une partie de la réponse et élaboré sur AnswerBot en encapsulant tout dans une chute dans la catégorie UIScrollView. Le "lastContentOffset" est enregistré dans uiscrollview à la place, puis il suffit d'appeler:

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
  [scrollView setLastContentOffset:scrollView.contentOffset];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
  if (scrollView.scrollDirectionX == ScrollDirectionRight) {
    //Do something with your views etc
  }
  if (scrollView.scrollDirectionY == ScrollDirectionUp) {
    //Do something with your views etc
  }
}

Code source sur https://github.com/tehjord/UIScrollViewScrollingDirection

1
Bjergsen

Si vous travaillez avec UIScrollView et UIPageControl, cette méthode modifiera également la vue de page de PageControl.

  func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    let targetOffset = targetContentOffset.memory.x
    let widthPerPage = scrollView.contentSize.width / CGFloat(pageControl.numberOfPages)

    let currentPage = targetOffset / widthPerPage
    pageControl.currentPage = Int(currentPage)
}

Merci le code Swift de _Esq.

0
charles.cc.hsu

Swift 2.2 Solution simple qui suit une ou plusieurs directions sans aucune perte.

  // Keep last location with parameter
  var lastLocation:CGPoint = CGPointZero

  // We are using only this function so, we can
  // track each scroll without lose anyone
  override func scrollViewWillBeginDragging(scrollView: UIScrollView) {
    let currentLocation = scrollView.contentOffset

    // Add each direction string
    var directionList:[String] = []

    if lastLocation.x < currentLocation.x {
      //print("right")
      directionList.append("Right")
    } else if lastLocation.x > currentLocation.x {
      //print("left")
      directionList.append("Left")
    }

    // there is no "else if" to track both vertical
    // and horizontal direction
    if lastLocation.y < currentLocation.y {
      //print("up")
      directionList.append("Up")
    } else if lastLocation.y > currentLocation.y {
      //print("down")
      directionList.append("Down")
    }

    // scrolled to single direction
    if directionList.count == 1 {
      print("scrolled to \(directionList[0]) direction.")
    } else if directionList.count > 0  { // scrolled to multiple direction
      print("scrolled to \(directionList[0])-\(directionList[1]) direction.")
    }

    // Update last location after check current otherwise,
    // values will be same
    lastLocation = scrollView.contentOffset
  }
0
fatihyildizhan

Lorsque la pagination est activée, vous pouvez utiliser ce code.

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    self.lastPage = self.currentPage;
    CGFloat pageWidth = _mainScrollView.frame.size.width;
    self.currentPage = floor((_mainScrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
    if (self.lastPage < self.currentPage) {
        //go right
        NSLog(@"right");
    }else if(self.lastPage > self.currentPage){
        //go left
        NSLog(@"left");
    }else if (self.lastPage == self.currentPage){
        //same page
        NSLog(@"same page");
    }
}
0
Perry
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
NSLog(@"px %f py %f",velocity.x,velocity.y);}

Utilisez cette méthode déléguée de scrollview.

Si la coordonnée de vélocité est + ve, la vue défilement défile vers le bas et si elle est - scrollview défile vers le haut. De même, les défilements gauche et droit peuvent être détectés en utilisant la coordonnée X.

0
Nilesh Tupe

Ok, donc pour moi cette implémentation fonctionne vraiment bien:

@property (nonatomic, assign) CGPoint lastContentOffset;


- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    _lastContentOffset.x = scrollView.contentOffset.x;
    _lastContentOffset.y = scrollView.contentOffset.y;

}


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

    if (_lastContentOffset.x < (int)scrollView.contentOffset.x) {
        // moved right
        NSLog(@"right");
    }
    else if (_lastContentOffset.x > (int)scrollView.contentOffset.x) {
        // moved left
        NSLog(@"left");

    }else if (_lastContentOffset.y<(int)scrollView.contentOffset.y){
        NSLog(@"up");

    }else if (_lastContentOffset.y>(int)scrollView.contentOffset.y){
        NSLog(@"down");
        [self.txtText resignFirstResponder];

    }
}

Donc, cela va déclencher textView pour ignorer après la fin du glisser

0
user2021505

Short & Easy serait, il suffit de vérifier la valeur de vélocité, si sa valeur est supérieure à zéro, puis son défilement à gauche et à droite:

func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    var targetOffset = Float(targetContentOffset.memory.x)
    println("TargetOffset: \(targetOffset)")
    println(velocity)

    if velocity.x < 0 {
        scrollDirection = -1 //scrolling left
    } else {
        scrollDirection = 1 //scrolling right
    }
}
0
iDilip

Les codes s’expliquent je suppose. CGFloat difference1 et difference2 déclarés dans une interface privée de même classe. Bon si contentSize reste identique.

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

        CGFloat contentOffSet = scrollView.contentOffset.y;
        CGFloat contentHeight = scrollView.contentSize.height;

        difference1 = contentHeight - contentOffSet;

        if (difference1 > difference2) {
            NSLog(@"Up");
        }else{
            NSLog(@"Down");
        }

        difference2 = contentHeight - contentOffSet;

       }
0
user2511630