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?
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,
};
... 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
}
}
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
}
}
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.
La solution
func scrollViewDidScroll(scrollView: UIScrollView) {
if(scrollView.panGestureRecognizer.translationInView(scrollView.superview).y > 0)
{
print("up")
}
else
{
print("down")
}
}
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!")
}
}
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
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");
}
}
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
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
}
}
}
}
}
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
// 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);
self.lastContentOffset
change très rapidement, l'écart de valeur est proche de 0.5f.
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.
}
....
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
}
}
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
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.
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
}
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");
}
}
- (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.
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
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
}
}
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;
}