जब UIScrollView ने स्क्रॉल करना समाप्त कर लिया है तो कैसे पता लगाया जाए


145

UIScrollViewDelegate को दो डेलिगेट तरीके मिले हैं scrollViewDidScroll:और scrollViewDidEndScrollingAnimation:इनमें से कोई भी आपको स्क्रॉलिंग पूरा होने पर नहीं बताता है। scrollViewDidScrollकेवल आपको सूचित करता है कि स्क्रॉल दृश्य ने स्क्रॉल नहीं किया है कि उसने स्क्रॉल करना समाप्त कर दिया है।

दूसरी विधि scrollViewDidEndScrollingAnimationकेवल तब लगती है जब आप उपयोगकर्ता के स्क्रॉल करने पर क्रमिक रूप से स्क्रॉल दृश्य को स्थानांतरित नहीं करते हैं।

क्या किसी स्कीम का पता लगाने के लिए कोई स्क्रॉल व्यू पूरा होने पर पता चलता है?


यह भी देखें, यदि आप क्रमिक रूप से स्क्रॉल करने के बाद समाप्त स्क्रॉलिंग का पता लगाना चाहते हैं: stackoverflow.com/questions/2358046/…
ट्रेवर

जवाबों:


157
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    [self stoppedScrolling];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (!decelerate) {
        [self stoppedScrolling];
    }
}

- (void)stoppedScrolling {
    // ...
}

13
ऐसा लगता है कि यह केवल तभी काम करता है जब यह कम हो रहा हो। यदि आप धीरे-धीरे पैन करते हैं, तो रिलीज़ करें, यह didEndDecelerating नहीं कहता है।
सीन क्लार्क हेस

4
हालांकि यह आधिकारिक एपीआई है। यह वास्तव में हमेशा काम नहीं करता है जैसा कि हम उम्मीद करते हैं। @ एशले स्मार्ट ने अधिक व्यावहारिक समाधान दिया।
दी वू

पूरी तरह से काम करता है और स्पष्टीकरण समझ में आता है
स्टीवन इलियट

यह केवल स्क्रॉल के लिए काम करता है जो इंटरेक्शन खींचने के कारण होता है। यदि आपकी स्क्रॉल कुछ और (जैसे कीबोर्ड खोलने या कीबोर्ड बंद करने) के कारण है, तो ऐसा लगता है कि आपको किसी हैक के साथ घटना का पता लगाना होगा, और स्क्रॉलव्यूडीडइंडक्रॉलिंगअनिमेशन भी उपयोगी नहीं है।
औरेलिन पोर्टे

176

320 कार्यान्वयन इतना बेहतर कर रहे हैं - यहाँ एक पैच पुस्तक के अनुरूप प्रारंभ / समाप्त होता है पाने के लिए है।

-(void)scrollViewDidScroll:(UIScrollView *)sender 
{   
[NSObject cancelPreviousPerformRequestsWithTarget:self];
    //ensure that the end of scroll is fired.
    [self performSelector:@selector(scrollViewDidEndScrollingAnimation:) withObject:sender afterDelay:0.3]; 

...
}

-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
...
}

मेरी स्क्रॉल समस्या के लिए SO पर एकमात्र उपयोगी उत्तर। धन्यवाद!
दी वू

4
[0.3: @selector (scrollViewDidEndScrollingAnimation :) withObject: इस afterDelay स्वयं performSelector] होना चाहिए
ब्रेनवेयर

1
2015 में, यह अभी भी सबसे अच्छा समाधान है।
लुकास

2
मैं ईमानदारी से विश्वास नहीं कर सकता कि मैं फरवरी 2016, आईओएस 9.3 में हूं और यह अभी भी इस समस्या का सबसे अच्छा समाधान है। धन्यवाद, एक आकर्षण की तरह काम किया
gmogames

1
तुमने मुझे बचाया। सबसे अच्छा उपाय।
बारां इम्रे

21

मुझे लगता है कि scrollViewDidEndDecelerating वह है जो आप चाहते हैं। इसका UIScrollViewDelegates वैकल्पिक विधि:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

प्रतिनिधि को बताता है कि स्क्रॉल दृश्य स्क्रॉल आंदोलन को कम कर रहा है।

UIScrollViewDelegate प्रलेखन


एर, मैंने वही जवाब दिया। :)
सुवेश प्रताप

रवींद्र। कुछ हद तक क्रिप्टोकरंसी है, लेकिन इसका ठीक वैसा ही जैसा मैं देख रहा था। प्रलेखन को बेहतर ढंग से पढ़ना चाहिए था। इसका लंबा दिन रहा ...
माइकल गेलॉर्ड

इस एक लाइनर ने मेरी समस्या हल कर दी। मेरे लिए जल्दी ठीक करो! धन्यवाद।
डोडी रचमत विसाकोसनो

20

ड्रैगिंग इंटरैक्शन से संबंधित सभी स्क्रॉल के लिए, यह पर्याप्त होगा:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    _isScrolling = NO;
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (!decelerate) {
        _isScrolling = NO;
    }
}

अब, यदि आपका स्क्रॉल एक प्रोग्रामेटिक सेटकंटेंटऑफसेट / स्क्रोल रेक्टवेविविज़ ( animated= यस के साथ है या आप स्पष्ट रूप से जानते हैं कि स्क्रॉल कब समाप्त हुआ है):

 - (void)scrollViewDidEndScrollingAnimation {
     _isScrolling = NO;
}

यदि आपकी स्क्रॉल कुछ और (जैसे कीबोर्ड खोलने या कीबोर्ड बंद होने) के कारण है, तो ऐसा लगता है कि आपको हैक के साथ घटना का पता लगाना होगा scrollViewDidEndScrollingAnimation यह उपयोगी नहीं है।

एक के मामले पृष्ठवार स्क्रॉल दृश्य:

क्योंकि, मुझे लगता है, Apple एक त्वरण वक्र लागू करता है, scrollViewDidEndDeceleratingप्रत्येक ड्रैग के लिए बुलाया जाता है , इसलिए scrollViewDidEndDraggingइस मामले में उपयोग करने की कोई आवश्यकता नहीं है ।


आपके पृष्ठांकित स्क्रॉल दृश्य मामले में एक समस्या है: यदि आप स्क्रॉलिंग को पृष्ठ के अंतिम स्थान पर छोड़ते हैं (जब कोई स्क्रॉल आवश्यक नहीं), scrollViewDidEndDeceleratingतो कॉल नहीं किया जाएगा
छाया

19

यह कुछ अन्य उत्तरों में वर्णित किया गया है, लेकिन यहां (कोड में) स्क्रॉलिंग समाप्त होने पर कुछ ऑपरेशन को कैसे संयोजित scrollViewDidEndDeceleratingऔर scrollViewDidEndDragging:willDecelerateनिष्पादित करना है:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    [self stoppedScrolling];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView 
                  willDecelerate:(BOOL)decelerate
{
    if (!decelerate) {
        [self stoppedScrolling];
    }
}

- (void)stoppedScrolling
{
    // done, do whatever
}

7

मुझे केवल यह प्रश्न मिला, जो मैंने पूछा था, जो वास्तव में पता है : UIScrollView की स्क्रॉलिंग बंद होने पर कैसे पता चलेगा?

हालांकि स्क्रॉल करते समय काम करता था, स्थिर रिलीज के साथ पैनिंग रजिस्टर नहीं करता है।

मुझे आखिरकार एक समाधान मिल गया। didEndDragging में एक पैरामीटर WillDecelerate है, जो स्थिर रिलीज़ स्थिति में गलत है।

DidEndDecelerating के साथ संयुक्त, DidEndDragging में decelerate की जाँच करके, आप दोनों स्थितियों को स्क्रॉल करने का अंत प्राप्त करते हैं।


5

यदि आप Rx में हैं, तो आप UIScrollView को इस तरह बढ़ा सकते हैं:

import RxSwift
import RxCocoa

extension Reactive where Base: UIScrollView {
    public var didEndScrolling: ControlEvent<Void> {
        let source = Observable
            .merge([base.rx.didEndDragging.map { !$0 },
                    base.rx.didEndDecelerating.mapTo(true)])
            .filter { $0 }
            .mapTo(())
        return ControlEvent(events: source)
    }
}

जो आपको ऐसा करने की अनुमति देगा:

scrollView.rx.didEndScrolling.subscribe(onNext: {
    // Do what needs to be done here
})

यह खींचने और मंदी दोनों को ध्यान में रखेगा।


यही वह है जिसकी तलाश में मैं हूं। धन्यवाद!
नोमन

4
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    scrollingFinished(scrollView: scrollView)
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if decelerate {
        //didEndDecelerating will be called for sure
        return
    }
    else {
        scrollingFinished(scrollView: scrollView)
    }
}

func scrollingFinished(scrollView: UIScrollView) {
   // Your code
}

3

मैंने एशले स्मार्ट के जवाब की कोशिश की है और यह एक आकर्षण की तरह काम करता है। यहाँ केवल स्क्रॉल व्यूड्रॉल का उपयोग करने के साथ एक और विचार है

-(void)scrollViewDidScroll:(UIScrollView *)sender 
{   
    if(self.scrollView_Result.contentOffset.x == self.scrollView_Result.frame.size.width)       {
    // You have reached page 1
    }
}

मेरे पास सिर्फ दो पेज थे इसलिए इसने मेरे लिए काम किया। हालाँकि, यदि आपके पास एक से अधिक पृष्ठ हैं, तो यह समस्याग्रस्त हो सकता है (आप जाँच कर सकते हैं कि क्या वर्तमान ऑफ़सेट चौड़ाई का एक से अधिक है, लेकिन तब आपको यह नहीं पता होगा कि उपयोगकर्ता 2 पेज पर रुका है या 3rd या उसके रास्ते में है) अधिक)


3

स्वीकृत उत्तर का स्विफ्ट संस्करण:

func scrollViewDidScroll(scrollView: UIScrollView) {
     // example code
}
func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        // example code
}
func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView!, atScale scale: CGFloat) {
      // example code
}

3

अगर किसी को जरूरत है, तो यहां स्विफ्ट में एशले स्मार्ट जवाब है

func scrollViewDidScroll(_ scrollView: UIScrollView) {
        NSObject.cancelPreviousPerformRequests(withTarget: self)
        perform(#selector(UIScrollViewDelegate.scrollViewDidEndScrollingAnimation), with: nil, afterDelay: 0.3)
    ...
}

func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
        NSObject.cancelPreviousPerformRequests(withTarget: self)
    ...
}

2

स्क्रॉलिंग का पता लगाने के लिए बस विकसित समाधान https://gist.github.com/k06a/731654e3168277fb1fd0e64abc7d899e

यह रनलूप मोड के परिवर्तनों पर नज़र रखने के विचार पर आधारित है। और स्क्रॉल करने के बाद कम से कम 0.2 सेकंड के बाद ब्लॉक करें।

रनओलोप मोड को बदलने के लिए यह मुख्य विचार है iOS10 +:

- (void)tick {
    [[NSRunLoop mainRunLoop] performInModes:@[ UITrackingRunLoopMode ] block:^{
        [self tock];
    }];
}

- (void)tock {
    self.runLoopModeWasUITrackingAgain = YES;
    [[NSRunLoop mainRunLoop] performInModes:@[ NSDefaultRunLoopMode ] block:^{
        [self tick];
    }];
}

और iOS2 + जैसे कम तैनाती लक्ष्य के लिए समाधान:

- (void)tick {
    [[NSRunLoop mainRunLoop] performSelector:@selector(tock) target:self argument:nil order:0 modes:@[ UITrackingRunLoopMode ]];
}

- (void)tock {
    self.runLoopModeWasUITrackingAgain = YES;
    [[NSRunLoop mainRunLoop] performSelector:@selector(tick) target:self argument:nil order:0 modes:@[ NSDefaultRunLoopMode ]];
}

Lol ऐप-वाइड स्क्रॉल डिटेक्शन - जैसे कि, यदि ऐप के किसी भी UIScrollVIew की वर्तमान में स्क्रॉल हो रही है ... बेकार की तरह लगता है ...
इसहाक कैरोल Weisberg

@IsaacCarolWeisberg आप उस समय तक भारी परिचालन में देरी कर सकते थे जब चिकनी स्क्रॉलिंग इसे सुचारू रखने के लिए समाप्त हो गई।
k06a

भारी ऑपरेशन, आप कहते हैं ... कि मैं वैश्विक समवर्ती प्रेषण कतारों में निष्पादित कर रहा हूं, शायद
इसहाक कैरोल वेसबर्ग

1

पुनर्कथन (और newbies के लिए)। यह उतना दर्दनाक नहीं है। बस प्रोटोकॉल जोड़ें, फिर उन कार्यों को जोड़ें जो आपको पता लगाने के लिए आवश्यक हैं।

UIScrolView वाले दृश्य (वर्ग) में, प्रोटोकॉल जोड़ें, फिर यहां से किसी भी फ़ंक्शन को आपके दृश्य (वर्ग) में जोड़ा गया।

// --------------------------------
// In the "h" file:
// --------------------------------
@interface myViewClass : UIViewController  <UIScrollViewDelegate> // <-- Adding the protocol here

// Scroll view
@property (nonatomic, retain) UIScrollView *myScrollView;
@property (nonatomic, assign) BOOL isScrolling;

// Protocol functions
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView;


// --------------------------------
// In the "m" file:
// --------------------------------
@implementation BlockerViewController

- (void)viewDidLoad {
    CGRect scrollRect = self.view.frame; // Same size as this view
    self.myScrollView = [[UIScrollView alloc] initWithFrame:scrollRect];
    self.myScrollView.delegate = self;
    self.myScrollView.contentSize = CGSizeMake(scrollRect.size.width, scrollRect.size.height);
    self.myScrollView.contentInset = UIEdgeInsetsMake(0.0,22.0,0.0,22.0);
    // Allow dragging button to display outside the boundaries
    self.myScrollView.clipsToBounds = NO;
    // Prevent buttons from activating scroller:
    self.myScrollView.canCancelContentTouches = NO;
    self.myScrollView.delaysContentTouches = NO;
    [self.myScrollView setBackgroundColor:[UIColor darkGrayColor]];
    [self.view addSubview:self.myScrollView];

    // Add stuff to scrollview
    UIImage *myImage = [UIImage imageNamed:@"foo.png"];
    [self.myScrollView addSubview:myImage];
}

// Protocol functions
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    NSLog(@"start drag");
    _isScrolling = YES;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    NSLog(@"end decel");
    _isScrolling = NO;
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    NSLog(@"end dragging");
    if (!decelerate) {
       _isScrolling = NO;
    }
}

// All of the available functions are here:
// https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIScrollViewDelegate_Protocol/Reference/UIScrollViewDelegate.html

1

मेरे पास टैपिंग और ड्रैगिंग एक्शन का मामला था और मुझे पता चला कि ड्रैगिंग स्क्रॉलव्यूडीडइंडएक्लेटरिंग कह रहा था

और परिवर्तन कोड ([_scrollView setContentOffset: contentOffset animated: YES];) के साथ मैन्युअल रूप से ऑफसेट है।

//This delegate method is called when the dragging scrolling happens, but no when the     tapping
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    //do whatever you want to happen when the scroll is done
}

//This delegate method is called when the tapping scrolling happens, but no when the  dragging
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
     //do whatever you want to happen when the scroll is done
}

1

एक विकल्प यह होगा कि scrollViewWillEndDragging:withVelocity:targetContentOffsetजब भी उपयोगकर्ता उंगली उठाता है और लक्ष्य सामग्री ऑफसेट होती है, जहां स्क्रॉल बंद हो जाएगा, जिसका उपयोग किया जाता है। scrollViewDidScroll:जब स्क्रॉल दृश्य ने स्क्रॉल करना बंद कर दिया है, तो इस सामग्री की ऑफसेट का उपयोग सही तरीके से पहचान करता है।

private var targetY: CGFloat?
public func scrollViewWillEndDragging(_ scrollView: UIScrollView,
                                      withVelocity velocity: CGPoint,
                                      targetContentOffset: UnsafeMutablePointer<CGPoint>) {
       targetY = targetContentOffset.pointee.y
}
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if (scrollView.contentOffset.y == targetY) {
        print("finished scrolling")
    }

0

UIScrollview में एक प्रतिनिधि विधि है

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

प्रतिनिधि विधि में कोड की निचली पंक्तियों को जोड़ें

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
CGSize scrollview_content=scrollView.contentSize;
CGPoint scrollview_offset=scrollView.contentOffset;
CGFloat size=scrollview_content.width;
CGFloat x=scrollview_offset.x;
if ((size-self.view.frame.size.width)==x) {
    //You have reached last page
}
}

0

एक तरीका है, UIScrollViewDelegateजिसका उपयोग पता लगाने के लिए किया जा सकता है (या 'भविष्यवाणी' कहने के लिए बेहतर है) जब स्क्रॉलिंग वास्तव में समाप्त हो गई है:

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)

का UIScrollViewDelegate जो पता लगाने के लिए (या कहने के लिए 'भविष्यवाणी' बेहतर) जब स्क्रॉल वास्तव में समाप्त हो गया है इस्तेमाल किया जा सकता।

मेरे मामले में मैंने इसे निम्न (क्षैतिज 3 में ) के रूप में क्षैतिज स्क्रॉलिंग के साथ उपयोग किया :

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    perform(#selector(self.actionOnFinishedScrolling), with: nil, afterDelay: Double(velocity.x))
}
func actionOnFinishedScrolling() {
    print("scrolling is finished")
    // do what you need
}

0

एशले स्मार्ट तर्क का उपयोग करना और स्विफ्ट 4.0 और इसके बाद के संस्करण में परिवर्तित किया जा रहा है।

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
         NSObject.cancelPreviousPerformRequests(withTarget: self)
        perform(#selector(UIScrollViewDelegate.scrollViewDidEndScrollingAnimation(_:)), with: scrollView, afterDelay: 0.3)
    }

    func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
        NSObject.cancelPreviousPerformRequests(withTarget: self)
    }

ऊपर दिए गए तर्क ऐसे मुद्दों को हल करते हैं जैसे कि उपयोगकर्ता टेबलव्यू को स्क्रॉल कर रहा है। बिना तर्क के, जब आप टेबलव्यू को स्क्रॉल करते हैं,didEnd कॉल किया जाएगा लेकिन यह कुछ भी निष्पादित नहीं करेगा। वर्तमान में इसका उपयोग वर्ष 2020 में किया जा रहा है।


0

कुछ पुराने iOS संस्करणों पर (जैसे iOS 9, 10), scrollViewDidEndDecelerating यदि स्क्रॉलव्यू को अचानक टच करने से रोका नहीं जाता है, तो ट्रिगर नहीं होगा।

लेकिन वर्तमान संस्करण (iOS 13) में, scrollViewDidEndDeceleratingसुनिश्चित करने के लिए ट्रिगर किया जाएगा (जहाँ तक मुझे पता है)।

इसलिए, यदि आपका ऐप पहले के संस्करणों को भी लक्षित करता है, तो आपको एशले स्मार्ट द्वारा बताए गए तरीके से वर्कअराउंड की आवश्यकता हो सकती है, या आप निम्नलिखित को ले सकते हैं।


    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if !scrollView.isTracking, !scrollView.isDragging, !scrollView.isDecelerating { // 1
            scrollViewDidEndScrolling(scrollView)
        }
    }

    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        if !decelerate, scrollView.isTracking, !scrollView.isDragging, !scrollView.isDecelerating { // 2
            scrollViewDidEndScrolling(scrollView)
        }
    }

    func scrollViewDidEndScrolling(_ scrollView: UIScrollView) {
        // Do something here
    }

व्याख्या

UIScrollView को तीन तरीकों
से रोका जाएगा: - जल्दी से स्क्रॉल किया और अपने आप से रोका
- जल्दी से स्क्रॉल किया और फिंगर टच (जैसे इमरजेंसी ब्रेक) द्वारा रोका
- धीरे-धीरे स्क्रॉल किया और रोका

पहले से पता लगाया जा सकता है scrollViewDidEndDecelerating और अन्य समान तरीकों से जबकि अन्य दो नहीं।

सौभाग्य से, UIScrollViewतीन स्थितियां हैं जिनका उपयोग हम उन्हें पहचानने के लिए कर सकते हैं, जिसका उपयोग "// 1" और "2" द्वारा टिप्पणी की गई दो पंक्तियों में किया जाता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.