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.
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.
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. ????
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
}
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")
}
}
}
Pour Swift4
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
if(scrollView.panGestureRecognizer.translation(in: scrollView.superview).y > 0) {
print("up")
}
else {
print("down")
}
}
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.
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é
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
}
J'ai créé un protocole pour réutiliser les instructions de défilement.
Déclarez ces enum
et protocol
s.
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.
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
}
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)
}
}