Проверьте, достиг ли UIScrollView вершины или дна

#ios #objective-c #iphone #uiscrollview #uiscrollviewdelegate

#iOS #objective-c #iPhone #uiscrollview #uiscrollviewdelegate

Вопрос:

Есть ли способ узнать, достиг ли UIScrollView вершины или дна? Возможно, в методе:

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

Ответ №1:

Реализуйте UIScrollViewDelegate в своем классе, а затем добавьте это:

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

Надеюсь, это то, что вам нужно! В противном случае придется повозиться, добавив дополнительные условия к приведенному выше коду и NSLog значение scrollOffset.

Комментарии:

1. Если вы находитесь в navigationController , вы можете захотеть сделать что-то более похожее на это: scrollOffset <= (0 - self.navigationController.navigationBar.frame.size.height - 20) и (scrollOffset scrollHeight) >= scrollContentSizeHeight

2. Проблема с этим заключается в отказе … все вызывается дважды, как только достигает вершины или дна. Есть ли решение для этого, помимо отключения отображения таблицы или коллекции?

3. @denikov вы нашли какое-либо решение для этого?

4. scrollOffset может быть <0, если ограничение scrollview top!= 0

Ответ №2:

Ну, contentInsets также задействованы, когда вы пытаетесь определить, находится ли ScrollView вверху или внизу. Вас также могут заинтересовать случаи, когда ваш ScrollView находится выше верха и ниже низа. Вот код, который я использую для поиска верхней и нижней позиций:

Swift:

 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
    }

}
  

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

Комментарии:

1. Это хороший ответ, реализация scrollviewdidscroll плохо сказывается на производительности

2. Как мне это вызвать, если не используется scrollViewDidScroll?

3. Из-за причуд математики с плавающей запятой я получал «verticalOffsetForBottom» 1879.05xxx и мой contentOffset. y был 1879 годом, когда оказался внизу в Swift 3. Поэтому я сменил return scrollViewBottomOffset на return scrollViewBottomOffset.rounded()

4. Начиная с iOS 11 safeAreaInsets , это также учитывается. scrollView.contentOffset.y scrollView.bounds.height - scrollView.safeAreaInsets.bottom

5. В iOS 11 и выше вам может потребоваться использовать adjustedContentInset вместо contentInset .

Ответ №3:

Если вам нужен код в swift:

 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 amp;amp; scrollView.contentOffset.y < (scrollView.contentSize.height - scrollView.frame.size.height)){
        //not top and not bottom
    }
}
  

Ответ №4:

Простое и понятное расширение:

 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
    }

}
  

На GitHub:

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

Ответ №5:

Я точно понял, как это сделать:

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

Ответ №6:

Сделайте это так (для 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
        }


    }
}
  

Комментарии:

1. Добро пожаловать в SO! При публикации кода в вашем ответе полезно объяснить, как ваш код решает проблему OP 🙂