iPhone - wiedzieć, czy UIScrollView osiągnął górę czy dół

86

Czy istnieje sposób, aby dowiedzieć się, czy UIScrollViewdoszło do górnej lub dolnej części w środku

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView 
                  willDecelerate:(BOOL)decelerate

?

dzięki.

kaczka
źródło

Odpowiedzi:

199

Zaimplementuj UIScrollViewDelegatew swojej klasie, a następnie dodaj to:

-(void)scrollViewDidScroll: (UIScrollView*)scrollView
{
    float scrollViewHeight = scrollView.frame.size.height;
    float scrollContentSizeHeight = scrollView.contentSize.height;
    float scrollOffset = scrollView.contentOffset.y;

    if (scrollOffset == 0)
    {
        // then we are at the top
    }
    else if (scrollOffset + scrollViewHeight == scrollContentSizeHeight)
    {
        // then we are at the end
    }
}

Mam nadzieję, że właśnie tego szukasz! W przeciwnym razie majstrować, dodając więcej warunków do powyższego kodu i NSLog wartość scrollOffset.

Łukasz
źródło
4
Jeśli jesteś w a navigationController, możesz chcieć zrobić coś bardziej podobnego: scrollOffset <= (0 - self.navigationController.navigationBar.frame.size.height - 20)i(scrollOffset + scrollHeight) >= scrollContentSizeHeight
Wayne
Problem z tym polega na odbiciu ... wszystko jest wywoływane dwa razy, gdy osiągnie górę lub dół. Czy istnieje na to rozwiązanie, poza wyłączeniem odbijania widoku tabeli lub kolekcji?
denikov
@denikov, czy znalazłeś na to jakieś rozwiązanie?
Priyal
83

Cóż, contentInsetssą również zaangażowane, gdy próbujesz określić, czy scrollView jest na górze, czy na dole. Mogą Cię również zainteresować przypadki, w których scrollView znajduje się powyżej i poniżej dołu. Oto kod, którego używam, aby znaleźć górne i dolne pozycje:

Szybki:

extension UIScrollView {

    var isAtTop: Bool {
        return contentOffset.y <= verticalOffsetForTop
    }

    var isAtBottom: Bool {
        return contentOffset.y >= verticalOffsetForBottom
    }

    var verticalOffsetForTop: CGFloat {
        let topInset = contentInset.top
        return -topInset
    }

    var verticalOffsetForBottom: CGFloat {
        let scrollViewHeight = bounds.height
        let scrollContentSizeHeight = contentSize.height
        let bottomInset = contentInset.bottom
        let scrollViewBottomOffset = scrollContentSizeHeight + bottomInset - scrollViewHeight
        return scrollViewBottomOffset
    }

}

Cel C:

@implementation UIScrollView (Additions)

- (BOOL)isAtTop {
    return (self.contentOffset.y <= [self verticalOffsetForTop]);
}

- (BOOL)isAtBottom {
    return (self.contentOffset.y >= [self verticalOffsetForBottom]);
}

- (CGFloat)verticalOffsetForTop {
    CGFloat topInset = self.contentInset.top;
    return -topInset;
}

- (CGFloat)verticalOffsetForBottom {
    CGFloat scrollViewHeight = self.bounds.size.height;
    CGFloat scrollContentSizeHeight = self.contentSize.height;
    CGFloat bottomInset = self.contentInset.bottom;
    CGFloat scrollViewBottomOffset = scrollContentSizeHeight + bottomInset - scrollViewHeight;
    return scrollViewBottomOffset;
}


@end
Alexander Dvornikov
źródło
To dobra odpowiedź, wdrożenie scrollviewdidscroll jest złe dla występów
Vassily,
3
Jak to nazwać, jeśli nie używam scrollViewDidScroll?
Dave G,
2
Ze względu na kaprysy matematyki zmiennoprzecinkowej otrzymałem „verticalOffsetForBottom” 1879.05xxx, a mój contentOffset.y był 1879, kiedy byłem na dole w Swift 3. Więc zmieniłem return scrollViewBottomOffsetnareturn scrollViewBottomOffset.rounded()
chadbag
1
Od iOS 11 safeAreaInsetsrównież uwzględnij to. scrollView.contentOffset.y + scrollView.bounds.height - scrollView.safeAreaInsets.bottom
InkGolem
36

Jeśli chcesz, aby kod był szybki:

override func scrollViewDidScroll(scrollView: UIScrollView) {

    if (scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height)) {
        //reach bottom
    }

    if (scrollView.contentOffset.y <= 0){
        //reach top
    }

    if (scrollView.contentOffset.y > 0 && scrollView.contentOffset.y < (scrollView.contentSize.height - scrollView.frame.size.height)){
        //not top and not bottom
    }
}
ytbryan
źródło
12

Proste i przejrzyste rozszerzenie:

import UIKit

extension UIScrollView {

    var scrolledToTop: Bool {
        let topEdge = 0 - contentInset.top
        return contentOffset.y <= topEdge
    }

    var scrolledToBottom: Bool {
        let bottomEdge = contentSize.height + contentInset.bottom - bounds.height
        return contentOffset.y >= bottomEdge
    }

}

W serwisie GitHub:

https://github.com/salutis/swift-scroll-view-edges

Rudolf Adamkovič
źródło
4

Dowiedziałem się dokładnie, jak to zrobić:

CGFloat maxPosition = scrollView.contentInset.top + scrollView.contentSize.height + scrollView.contentInset.bottom - scrollView.bounds.size.height;
CGFloat currentPosition = scrollView.contentOffset.y + self.topLayoutGuide.length;

if (currentPosition == maxPosition) {
  // you're at the bottom!
}
JPN
źródło
0

Zrób to w ten sposób (dla Swift 3):

class MyClass: UIScrollViewDelegate {

   func scrollViewDidScroll(_ scrollView: UIScrollView) {

        if (scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height)) {
           //scrolled to top, do smth
        }


    }
}
Radu Ghitescu
źródło
Witamy w SO! Umieszczając kod w swojej odpowiedzi, pomocne jest wyjaśnienie, w jaki sposób Twój kod rozwiązuje problem OP :)
Joel