UIScrollViewDelegate a deux méthodes de délégation, scrollViewDidScroll:
et scrollViewDidEndScrollingAnimation:
, mais aucune de ces méthodes ne vous indique que le défilement est terminé. scrollViewDidScroll
vous avertit seulement que la vue de défilement n'a pas fait défiler l'écran.
L'autre méthode scrollViewDidEndScrollingAnimation
ne semble se déclencher que si vous déplacez la vue de défilement par programme, pas si l'utilisateur fait défiler.
Est-ce que quelqu'un sait de schéma pour détecter quand une vue de défilement a terminé le défilement?
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self stoppedScrolling];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
[self stoppedScrolling];
}
}
- (void)stoppedScrolling {
// ...
}
Les implémentations 320 sont tellement meilleures - voici un correctif pour obtenir des débuts/fins cohérents du défilement.
-(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];
...
}
Je pense que scrollViewDidEndDecelerating est celui que vous voulez. Sa méthode facultative UIScrollViewDelegates:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
Indique au délégué que la vue de défilement a fini de décélérer le mouvement de défilement.
Pour tous les parchemins liés aux interactions de glissement, cela sera suffisant:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
_isScrolling = NO;
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
_isScrolling = NO;
}
}
Maintenant, si votre défilement est dû à un setContentOffset/scrollRectVisible par programme (avec animated
= YES ou si vous savez évidemment quand son défilement est terminé):
- (void)scrollViewDidEndScrollingAnimation {
_isScrolling = NO;
}
Si votre défilement est dû à quelque chose d'autre (comme l'ouverture ou la fermeture du clavier), il semble que vous deviez détecter l'événement avec un hack, car scrollViewDidEndScrollingAnimation
n'est pas utile non plus.
Le cas d'une vuePAGINÉEscroll:
Parce que, je suppose, Apple applique une courbe d’accélération, scrollViewDidEndDecelerating
se fait appeler à chaque glissement, il n’est donc pas nécessaire d’utiliser scrollViewDidEndDragging
dans ce cas.
Cela a été décrit dans certaines des autres réponses, mais voici (dans le code) comment combiner scrollViewDidEndDecelerating
et scrollViewDidEndDragging:willDecelerate
pour effectuer certaines opérations lorsque le défilement est terminé:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self stoppedScrolling];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate
{
if (!decelerate) {
[self stoppedScrolling];
}
}
- (void)stoppedScrolling
{
// done, do whatever
}
Je viens juste de trouver cette question, qui est à peu près la même chose que celle que j’avais posée: Comment savoir exactement quand le défilement d’UIScrollView s’est arrêté?
Bien que didEndDecelerating fonctionne lors du défilement, le panoramique avec la libération stationnaire ne s’enregistre pas.
J'ai finalement trouvé une solution. didEndDragging a un paramètre WillDecelerate, qui est faux dans la situation de publication stationnaire.
En cochant! Déceler dans DidEndDragging, en combinaison avec didEndDecelerating, vous obtenez les deux situations qui constituent la fin du défilement.
Version rapide de la réponse acceptée:
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
}
J'ai essayé la réponse d'Ashley Smart et cela a fonctionné à merveille. Voici une autre idée, en utilisant uniquement scrollViewDidScroll
-(void)scrollViewDidScroll:(UIScrollView *)sender
{
if(self.scrollView_Result.contentOffset.x == self.scrollView_Result.frame.size.width) {
// You have reached page 1
}
}
Je viens d'avoir deux pages alors ça a fonctionné pour moi. Cependant, si vous avez plus d'une page, cela peut poser problème (vous pouvez vérifier si le décalage actuel est un multiple de la largeur mais vous ne saurez pas si l'utilisateur s'est arrêté à la 2e page ou s'il est en route pour la 3e ou plus)
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
}
Vient de développer une solution pour détecter le défilement terminé dans l’ensemble de l’application: https://Gist.github.com/k06a/731654e3168277fb1fd0e64abc7d899e
Il est basé sur l'idée de suivre les modifications des modes runloop. Et effectuez des blocs au moins 0,2 seconde après le défilement.
C'est l'idée de base pour suivre les modifications des modes de cycle d'exécution iOS10 +:
- (void)tick {
[[NSRunLoop mainRunLoop] performInModes:@[ UITrackingRunLoopMode ] block:^{
[self tock];
}];
}
- (void)tock {
self.runLoopModeWasUITrackingAgain = YES;
[[NSRunLoop mainRunLoop] performInModes:@[ NSDefaultRunLoopMode ] block:^{
[self tick];
}];
}
Et solution pour les cibles à faible déploiement comme 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 ]];
}
J'ai eu un cas de taper et de glisser des actions et j'ai découvert que le glisser appelait scrollViewDidEndDecelerating
Et le changement a été compensé manuellement avec le code ([_scrollView setContentOffset: contentOffset animé: YES];) appelait scrollViewDidEndScrollingAnimation.
//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
}
Si quelqu'un a besoin d'aide, voici la réponse de Ashley Smart dans Swift
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)
...
}
Pour récapituler (et pour les débutants). Ce n'est pas si douloureux. Ajoutez simplement le protocole, puis ajoutez les fonctions dont vous avez besoin pour la détection.
Dans la vue (classe) contenant UIScrolView, ajoutez le protocole, puis ajoutez les fonctions d’ici à votre vue (classe).
// --------------------------------
// 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
Une alternative serait d'utiliser scrollViewWillEndDragging:withVelocity:targetContentOffset
qui est appelé chaque fois que l'utilisateur lève le doigt et contient le décalage du contenu cible à l'endroit où le défilement s'arrête. L'utilisation de ce contenu dans scrollViewDidScroll:
identifie correctement le moment où l'affichage de défilement a cessé de défiler.
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")
}
Il existe une méthode de UIScrollViewDelegate
qui peut être utilisée pour détecter (ou mieux dire 'prédire') lorsque le défilement est vraiment terminé:
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
de UIScrollViewDelegate
qui peut être utilisé pour détecter (ou mieux dire 'prédire') lorsque le défilement est vraiment terminé.
Dans mon cas, je l'ai utilisé avec un défilement horizontal comme suit (in Swift 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
}
Si vous aimez Rx, vous pouvez étendre UIScrollView comme ceci:
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)
}
}
ce qui vous permettra de faire comme ceci:
scrollView.rx.didEndScrolling.subscribe(onNext: {
// Do what needs to be done here
})
Cela tiendra compte à la fois de la traînée et de la décélération.
UIScrollview a une méthode déléguée
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
Ajoutez les lignes de code ci-dessous dans la méthode delegate
- (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
}
}