web-dev-qa-db-fra.com

Comment obtenir la direction verticale d'UIScrollView dans Swift?

Comment puis-je obtenir la direction de défilement/balayage pour monter/descendre dans un VC?

Je veux ajouter un UIScrollView ou quelque chose d'autre dans mon VC qui peut voir si l'utilisateur glisse/fait défiler vers le haut ou le bas, puis cache/affiche une UIView en fonction d'un geste haut/bas.

19
user2722667

Si vous utilisez une UIScrollView, vous pouvez tirer parti de la fonction scrollViewDidScroll: . Vous devez sauvegarder la dernière position (la contentOffset) et la mettre à jour de la manière suivante:

// variable to save the last position visited, default to zero
private var lastContentOffset: CGFloat = 0

func scrollViewDidScroll(scrollView: UIScrollView!) {
    if (self.lastContentOffset > scrollView.contentOffset.y) {
        // move up
    }
    else if (self.lastContentOffset < scrollView.contentOffset.y) { 
       // move down
    }

    // update the new position acquired
    self.lastContentOffset = scrollView.contentOffset.y
}

Il y a d'autres façons de le faire, bien sûr, cela leur est propre.

J'espère que cela vous aidera.

69
Victor Sigler

La réponse de Victor est excellente, mais elle coûte assez cher, car vous comparez et stockez toujours des valeurs. Si votre objectif est d’identifier instantanément la direction de défilement sans calcul coûteux, essayez ceci avec Swift :

func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
    let translation = scrollView.panGestureRecognizer.translation(in: scrollView.superview)
    if translation.y > 0 {
        // swipes from top to bottom of screen -> down
    } else {
        // swipes from bottom to top of screen -> up
    }
}

Et voilà. Encore une fois, si vous devez suivre en permanence, utilisez Victors answer, sinon je préfère cette solution. ???? 

29
GLS

J'ai utilisé la réponse de Victor avec une légère amélioration. Lorsque vous faites défiler au-delà de la fin ou du début du défilement, puis que vous obtenez l'effet de rebond. J'ai ajouté la contrainte en calculant scrollView.contentSize.height - scrollView.frame.height, puis en limitant la plage scrollView.contentOffset.y à une valeur supérieure à 0 ou inférieure à scrollView.contentSize.height - scrollView.frame.height. Aucune modification n'est apportée lors du retour.

func scrollViewDidScroll(_ scrollView: UIScrollView) {

    if lastContentOffset > scrollView.contentOffset.y && lastContentOffset < scrollView.contentSize.height - scrollView.frame.height {
        // move up
    } else if lastContentOffset < scrollView.contentOffset.y && scrollView.contentOffset.y > 0 {
        // move down
    }

    // update the new position acquired
    lastContentOffset = scrollView.contentOffset.y
}
7
gbotha

Swift 2-4 avec UISwipeGestureRecognizer

Une autre option consiste à utiliser la variable UISwipeGestureRecognizer qui reconnaîtra le glissement dans la direction demandée (cela fonctionnera sur toutes les vues et pas seulement sur UIScrollView

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let upGs = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.handleSwipes(sender:)))
        let downGs = UISwipeGestureRecognizer(target: self, action: #selector(ViewController.handleSwipes(sender:)))

        upGs.direction = .up
        downGs.direction = .down

        self.view.addGestureRecognizer(upGs)
        self.view.addGestureRecognizer(downGs)
    }

    @objc func handleSwipes(sender:UISwipeGestureRecognizer) {

        if (sender.direction == .up) {
            print("Up")
        }

        if (sender.direction == .down) {
            print("Down")
        }
    }
}
2
Daniel Krom

Pour Swift4

public func scrollViewDidScroll(_ scrollView: UIScrollView) {

        if(scrollView.panGestureRecognizer.translation(in: scrollView.superview).y > 0) {
            print("up")
        }
        else {
            print("down")
        }
    }
1
Hardik Thakkar

J'ai trouvé qu'il s'agit de l'option la plus simple et la plus flexible (elle fonctionne également pour UICollectionView et UITableView).

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

    switch velocity {
    case _ where velocity.y < 0:
        // swipes from top to bottom of screen -> down
        trackingDirection = .down
    case _ where velocity.y > 0:
        // swipes from bottom to top of screen -> up
        trackingDirection = .up
    default: trackingDirection = .none
    }
}

Là où cela ne fonctionne pas, c'est s'il y a une vélocité nulle. Dans ce cas, vous n'aurez d'autre choix que d'utiliser la solution de propriété stockée de la réponse acceptée.

1
brandonscript

Pour Swift Je pense que le plus simple et le plus puissant est de faire comme ci-dessous. Il vous permet de suivre le changement de direction et de réagir une seule fois lorsqu'il a changé . De plus, vous pouvez toujours accéder à la propriété de défilement .lastDirection si vous avez besoin de le consulter dans votre code à tout moment.

enum WMScrollDirection {
    case Up, Down, None
}

class WMScrollView: UIScrollView {
    var lastDirection: WMScrollDirection = .None {
        didSet {
            if oldValue != lastDirection {
                // direction has changed, call your func here
            }
        }
    }

    override var contentOffset: CGPoint {
        willSet {
            if contentOffset.y > newValue.y {
                lastDirection = .Down
            }
            else {
                lastDirection = .Up
            }
        }
    }
}

Ce qui précède suppose que vous ne faites que suivre le défilement haut/bas . Il est personnalisable via l’enum. Vous pouvez ajouter/modifier .left et .right pour suivre n'importe quelle direction.

J'espère que ça aidera quelqu'un.

À votre santé

1
Sasho

vous pourriez faire quelque chose comme ça:

fileprivate var lastContentOffset: CGPoint = .zero

func checkScrollDirection(_ scrollView: UIScrollView) -> UIScrollViewDirection {
    return lastContentOffset.y > scrollView.contentOffset.y ? .up : .down
}

et avec scrollViewDelegate:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    switch checkScrollDirection(scrollView) {
    case .up:
        // move up
    case .down:
        // move down
    default:
        break
    }

    lastContentOffset = scrollView.contentOffset
}
1
Felipe Rolvar

J'ai créé un protocole pour réutiliser les instructions de défilement.

Déclarez ces enum et protocols.

enum ScrollDirection {
    case up, left, down, right, none
}

protocol ScrollDirectionDetectable {
    associatedtype ScrollViewType: UIScrollView
    var scrollView: ScrollViewType { get }
    var scrollDirection: ScrollDirection { get set }
    var lastContentOffset: CGPoint { get set }
}

extension ScrollDirectionDetectable {
    var scrollView: ScrollViewType {
        return self.scrollView
    }
}

Utilisation De ViewController

// Set ScrollDirectionDetectable which has UIScrollViewDelegate
class YourViewController: UIViewController, ScrollDirectionDetectable {
    // any types that inherit UIScrollView can be ScrollViewType
    typealias ScrollViewType = UIScrollView
    var lastContentOffset: CGPoint = .zero
    var scrollDirection: ScrollDirection = .none

}
    extension YourViewController {
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            // Update ScrollView direction
            if self.lastContentOffset.x > scrollView.contentOffset.x {
                scrollDirection = .left
            } else if self.lastContentOffset.x > scrollView.contentOffset.x {
                scrollDirection = .right
            }

            if self.lastContentOffset.y > scrollView.contentOffset.y {
                scrollDirection = .up
            } else if self.lastContentOffset.y < scrollView.contentOffset.y {
                scrollDirection = .down
            }
            self.lastContentOffset.x = scrollView.contentOffset.x
            self.lastContentOffset.y = scrollView.contentOffset.y
        }
    }

Si vous souhaitez utiliser une direction spécifique, mettez simplement à jour la contentOffset que vous souhaitez.

0
Changnam Hong

J'ai essayé chaque réponse dans ce fil de discussion, mais aucune d’entre elles n’a pu fournir un solution pour un tableau de visualisation avec rebond activéapproprié. J'ai donc juste utilisé des parties de solutions avec une solution classique de drapeau booléen classique.

1) Donc, tout d’abord, vous pouvez utiliser une énumération pour le scrollDirection:

enum ScrollDirection {
    case up, down
}

2) Définissez 3 nouveaux vars privés pour nous aider à stocker lastOffset, scrollDirection et un drapeau pour activer/désactiver le calcul de la direction de défilement (nous aide à ignorer l'effet de rebond de tableView) que vous utiliserez plus tard:

private var shouldCalculateScrollDirection = false
private var lastContentOffset: CGFloat = 0
private var scrollDirection: ScrollDirection = .up

3) Dans le scrollViewDidScroll, ajoutez ce qui suit:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    // The current offset
    let offset = scrollView.contentOffset.y

    // Determine the scolling direction
    if lastContentOffset > offset && shouldCalculateScrollDirection {
        scrollDirection = .down
    }
    else if lastContentOffset < offset && shouldCalculateScrollDirection {
        scrollDirection = .up
    }

    // This needs to be in the last line
    lastContentOffset = offset
}

4) Si vous n'avez pas implémenté scrollViewDidEndDragging, implémentez-le et ajoutez les lignes de code qu'il contient:

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    guard !decelerate else { return }
    shouldCalculateScrollDirection = false
}

5) Si vous n'avez pas implémenté scrollViewWillBeginDecelerating, implémentez-le et ajoutez cette ligne de code à l'intérieur:

func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
    shouldCalculateScrollDirection = false
}

6) Enfin, si vous n'avez pas implémenté scrollViewWillBeginDragging, implémentez-le et ajoutez cette ligne de code à l'intérieur:

func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {    
    shouldCalculateScrollDirection = true
}

Et si vous avez suivi toutes les étapes ci-dessus, vous êtes prêt à partir!

Vous pouvez aller où vous voulez utiliser la direction et écrire simplement:

switch scrollDirection {
case .up:
    // Do something for scollDirection up
case .down:
    // Do something for scollDirection down
}
0
Petros Mitakos

Ajoutez simplement cette méthode à votre vue contrôleur :

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if (scrollView.contentOffset.y < 0) {
        // Move UP - Show Navigation Bar
        self.navigationController?.setNavigationBarHidden(false, animated: true)
    } else if (scrollView.contentOffset.y > 0) {
        // Move DOWN - Hide Navigation Bar
        self.navigationController?.setNavigationBarHidden(true, animated: true)
    }
}
0
Archi Stepanov