Bir UIScrollView içinde kaydırma yönünü mü arıyorsunuz?


183

Bir var UIScrollViewizin verilen tek yatay kaydırma ile, ben hangi yöne (sol, sağ) kullanıcı parşömenlerini bilmek istiyorum. Ne yaptım alt sınıf UIScrollViewve touchesMovedyöntemi geçersiz kılmak oldu :

- (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");

    }

}

Ancak bazen hareket ettiğimde bu yöntem çağrılmaz. Ne düşünüyorsun?


Aşağıdaki yanıtıma bakın - bunu yapmak için kaydırma görünümü delegelerini kullanıyor olmalısınız.
memmons

Yanıtlar:


392

Yönü belirlemek oldukça basittir, ancak yönün bir hareket sırasında birkaç kez değişebileceğini unutmayın. Örneğin, disk belleği açık bir kaydırma görünümünüz varsa ve kullanıcı bir sonraki sayfaya gitmek için hızlıca kaydırırsa, ilk yön sağa doğru olabilir, ancak sekme açıksa, kısa bir süre hiçbir yöne gitmeyecektir ve sonra kısaca sola gidiyor.

Yönü belirlemek için UIScrollView scrollViewDidScrolltemsilciyi kullanmanız gerekir . Bu örnekte, lastContentOffsetgeçerli içerik dengesini bir öncekiyle karşılaştırmak için kullandığım adlı bir değişken oluşturdum . Daha büyükse, scrollView sağa kayar. Daha azsa, scrollView sola kaydırıyor:

// 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.    
}

Yön tanımlamak için aşağıdaki numarayı kullanıyorum. İlk değeri ScrollDirectionNone olarak ayarlamak, değişkenleri başlatırken bu yönü varsayılan yapmak gibi bir avantaj sağlar:

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

1
Nasıl lastContentOffset alabilirim
Dev

@JasonZhao Heh - Ben kod intial olarak test ederken bazı garip sonuçlar alıyordum çünkü scrollview sıçrama ..er..bounce kaydırma yönünü neden dikkate almadım. Böylece, beklenmedik sonuçlar bulduğumda bunu sıralamaya ekledim.
12:44

1
@Dev Şifreli varlastContentOffset = scrollView.contentOffset.x;
memmons

@ akivag29 İyi yakalama lastContentOffsettürü. Özellik ve statik değişkenler açısından: her iki çözüm de iyi bir seçimdir. Gerçek bir tercihim yok. İyi bir nokta.
memmons

2
@JosueEspinosa Doğru kaydırma yönünü elde etmek için, lastContentOffset ayarlayıcı çağrısına scrollViewWillBeginDragging: yöntemine koyarsınız.
strawnut

73

... kullanıcının hangi yönde (sol, sağ) kaydırdığını bilmek istiyorum.

Bu durumda, iOS 5 ve sonraki sürümlerde UIScrollViewDelegate, kullanıcının yatay kaydırma hareketinin yönünü belirlemek için şunu kullanın :

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

1
en iyi çözüm olabilir, ancak gerçekten yavaş bir başlangıçla, dx 0'a eşit olacaktır.
trickster77777

Elbette öyle. Bu daha çok “yerel” veya uygun bir yol olduğundan daha fazla seçilmelidir çünkü esas olarak özellikte saklanan değerleri kullanmanız gerekir.
Michi

Bu , kullanıcı kaydırmayı durdurduktan sonra momentum kaydırma başladığında düzgün çalışmayacağı stackoverflow.com/a/26192103/62 ile aynı sorun değil mi?
Liron Yahdav

60

Kullanmak scrollViewDidScroll:, geçerli yönü bulmak için iyi bir yoldur.

Kullanıcı kaydırmayı bitirdikten sonraki yönü bilmek istiyorsanız , aşağıdakileri kullanın:

@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
    }
}

3
Bu Justin harika bir ektir, ancak bir düzenleme eklemek istiyorum. Kaydırma görünümünüzde disk belleği etkinleştirilmişse ve başlangıç ​​konumuna geri dönecek bir sürükleme gerçekleştirirseniz, geçerli "else" koşulu "değişiklik yapılmaması" gerekse bile "sola taşındı" olarak değerlendirir.
Scott Lieberman

1
@ScottLieberman hakkınız, kodu buna göre güncelledim.
Justin Tanner

50

Bunu takip etmek için fazladan bir değişken eklemenize gerek yoktur. Sadece kullanmak UIScrollViewbireyin panGestureRecognizerböyle özelliği. Ne yazık ki, bu sadece hız 0 değilse çalışır:

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");
}

Yukarı, aşağı, sola ve sağa algılamak için x ve y bileşenlerinin bir kombinasyonunu kullanabilirsiniz.


9
ne yazık ki bu sadece sürüklerken işe yarıyor. hala kaydırma sırasında bile dokunuşlar çıkarıldığında hız 0'dır.
heiko

Güzel +1. else part Velocity 0 çünkü kullanıcı artık sürüklemiyor, bu nedenle hareket hızı 0
Pavan

Yönü değişken olarak saklayabilirsiniz. Yalnızca hız! = 0. Benim için çalışıyor
Leszek Zarna


40

Çözüm

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

1
velocityInViewBunun yerine ile bir deneyin translationInView. Bu, aynı hareketteki yönü değiştirirken ortaya çıkan sorunu çözer.
enreas

2
Bu çözüm mükemmel çalışıyor. Bir sonraki ekrana gidip geri döndüğümde en çok kabul edilen cevap birkaç kez çalışmıyor. Yukarı ve aşağı kaydırma işlemleri gerçekleştirin.
Anjaneyulu Battula

En iyisi thnx!
Booharin

18

Hızlı 4:

Yatay kaydırma için şunları yapabilirsiniz:

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

İle dikey kaydırma değişikliği .xiçin.y


14

İOS8 Swift'te bu yöntemi kullandım:

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)

}

Kullanıcı kaydırdığı sırada yerleri değiştiren dinamik bir görünüm var, böylece görünüm ekranda aynı yerde kalmış gibi görünebilir. Kullanıcı yukarı veya aşağı gittiğinde de izliyorum.

İşte alternatif bir yol:

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

1
Y yerine x kullanılsın mı?
Esqarrouth

Aslında UiCollectionView için tespit etmek istiyorum. Yatay kaydırmam var. Yani crollViewDidEndDeceleratingdoğru tespit mi edeceğim ?
Umair Afzal

8

Benim için çalıştığı şey (Objective-C'de):

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

        NSString *direction = ([scrollView.panGestureRecognizer translationInView:scrollView.superview].y >0)?@"up":@"down";
        NSLog(@"%@",direction);
    }

7
- (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");
    }
}

2
Kaydırma görünümünün pagingEnabled özelliğinin değeri YES olduğunda bu yöntem çağrılmaz.
Ako

5

Alternatif olarak, "contentOffset" anahtar yolunu gözlemlemek de mümkündür. Bu, kaydırma görünümü temsilcisini ayarlamanız / değiştirmeniz mümkün olmadığında kullanışlıdır.

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

Gözlemciyi ekledikten sonra, artık şunları yapabilirsiniz:

- (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
    }
}

Gerektiğinde gözlemciyi çıkarmayı unutmayın. mesela sen gözlemciyi ekleyebilirsiniz viewWillAppear ve bunu kaldırmak viewWillDisappear


5

Hızlı:

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

Bunu scrollViewDidScroll içinde de yapabilirsiniz.


4

İşte @followben cevabı gibi davranış için benim çözümüm, ancak yavaş başlatma ile kayıp olmadan (dy 0 olduğunda)

@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
                }
            }
        }
    }
}

1

Cevabın bir kısmını kontrol ettim ve her şeyi UIScrollView kategorisinde bir damla olarak sararak AnswerBot cevabını detaylandırdım. "LastContentOffset" bunun yerine uiscrollview içine kaydedilir ve daha sonra sadece arama meselesidir:

- (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
  }
}

Https://github.com/tehjord/UIScrollViewScrollingDirection adresindeki kaynak kodu


1

@ Memmons'ın cevabına göre bazı filtreleme yapmayı tercih ederim

Hedef-C'de:

// 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
    } 
}

Test edildiğinde - (void)scrollViewDidScroll:(UIScrollView *)scrollView:

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

img

self.lastContentOffset çok hızlı değişir, değer boşluğu yaklaşık 0,5f'dir.

Bu gerekli değil.

Ve bazen, doğru durumda kullanıldığında, yönünüz belki kaybolabilir. (uygulama ifadeleri bazen atlandı)

gibi :

- (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.
    }
   ....

Swift 4'te:

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
    }
}

0

Disk belleği açıldığında, bu kodu kullanabilirsiniz.

- (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

Kodlar sanırım kendini açıklıyor. CGFloat fark1 ve fark2 aynı sınıf özel arayüzünde bildirildi. ContentSize aynı kalırsa iyi olur.

- (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

Tamam benim için bu uygulama gerçekten iyi çalışıyor:

@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];

    }
}

Bu, sürükle bittikten sonra kapatmak için textView'i tetikleyecektir


0
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
NSLog(@"px %f py %f",velocity.x,velocity.y);}

Bu temsilci scrollview yöntemini kullanın.

Eğer y koordinat hızı + ve ise kaydırma görünümü aşağıya, -ve kaydırma görünümü ise yukarı doğru kayar. Benzer şekilde sol ve sağ kaydırma x koordinatı kullanılarak tespit edilebilir.


0

Kısa ve Kolay olurdu, sadece hız değerini kontrol edin, eğer sıfırdan büyükse sağa sola kaydırma:

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

UIScrollView ve UIPageControl ile çalışırsanız, bu yöntem de PageControl'ün sayfa görünümünü değiştirir.

  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)
}

Teşekkürler @Esq 'in Swift kodu.


0

Swift 2.2 Tek ve çoklu yönleri kayıpsız takip eden basit bir çözüm .

  // 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

Tüm üst cevaplarda problemi çözmenin iki ana yolu panGestureRecognizerveya kullanmaktır contentOffset. Her iki yöntemin de eksileri ve artıları vardır.

Yöntem 1: panGestureRecognizer

panGestureRecognizer@Followben'in önerdiği gibi kullandığınızda , kaydırma görünümünüzü programlı olarak kaydırmak istemiyorsanız, düzgün çalışır.

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

Eksileri

Ancak kaydırma görünümünü aşağıdaki kodla taşırsanız, üst kod onu tanıyamaz:

setContentOffset(CGPoint(x: 100, y: 0), animation: false)

Yöntem 2: ContentOffset

var lastContentOffset: CGPoint = CGPoint.zero

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if (self.lastContentOffset.x > scrollView.contentOffset.x) {
        // scroll to right
    } else if self.lastContentOffset.x < scrollView.contentOffset.x {
        // scroll to left
    }
    self.lastContentOffset = self.scrollView.contentOffset
}

Eksileri

ContentOffset'i programlı olarak değiştirmek isterseniz, kaydırma sırasında (sonsuz kaydırma oluşturmak istediğinizde olduğu gibi), bu yöntem sorun yaratır, çünkü contentOffsetiçerik görünümleri yerlerini değiştirirken değişebilir ve bu sırada sağa kaydırdığınız üst kod atlama veya ayrıldı.


0
//Vertical detection
    var lastVelocityYSign = 0
            func scrollViewDidScroll(_ scrollView: UIScrollView) {
                let currentVelocityY =  scrollView.panGestureRecognizer.velocity(in: scrollView.superview).y
                let currentVelocityYSign = Int(currentVelocityY).signum()
                if currentVelocityYSign != lastVelocityYSign &&
                    currentVelocityYSign != 0 {
                    lastVelocityYSign = currentVelocityYSign
                }
                if lastVelocityYSign < 0 {
                    print("SCROLLING DOWN")
                } else if lastVelocityYSign > 0 {
                    print("SCOLLING UP")
                }
            }

Mos6y tarafından cevap https://medium.com/@Mos6yCanSwift/swift-ios-determine-scroll-direction-d48a2327a004

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.