web-dev-qa-db-fra.com

Comment puis-je forcer par programmation l'arrêt du défilement dans un UIScrollView?

Remarque: le réponse donnée ici ne fonctionne pas pour moi.

J'ai un UIScrollView (pas une vue de tableau, juste une chose personnalisée), et lorsque l'utilisateur effectue certaines actions, je veux tuer tout défilement (glisser ou décélération) dans la vue. J'ai essayé de faire par exemple ce:

[scrollView scrollRectToVisible:CGRectInset([scrollView bounds], 10, 10) animated:NO];

sur la théorie que, dans un rect qui est déjà visible visible, le défilement s'arrête là où il se trouve, mais il s'avère que cela n'a aucun effet - apparemment, la vue du défilement voit que le rect donné est dans les limites et prend pas d'action. Je peux obtenir le défilement pour arrêter, si je donne un rect qui est certainement en dehors de les limites actuellement visibles, mais à l'intérieur de la taille de contenu de la vue. Cela semble interrompre la vue comme prévu ... mais la fait également basculer vers un autre emplacement. Je pourrais probablement jouer un peu aux marges pour que cela fonctionne raisonnablement bien, mais est-ce que quelqu'un connaît un moyen propre de suspendre une vue par défilement qui fait son travail?

Merci.

68
Ben Zotto

J'ai un peu joué avec votre solution d'origine, et cela semble bien fonctionner. Je pense que vous l’avez presque eu, mais vous avez juste compensé le rect que vous aviez trop utilisé et vous avez oublié que vous pouviez simplement faire défiler le rect à l’arrière du rectangle d’origine.

La solution généralisée pour toute action de défilement est la suivante:

- (void)killScroll 
{
    CGPoint offset = scrollView.contentOffset;
    offset.x -= 1.0;
    offset.y -= 1.0;
    [scrollView setContentOffset:offset animated:NO];
    offset.x += 1.0;
    offset.y += 1.0;
    [scrollView setContentOffset:offset animated:NO];
}

[Modifier] Depuis iOS 4.3 (et peut-être plus tôt), cela semble également fonctionner

- (void)killScroll 
{
    CGPoint offset = scrollView.contentOffset;
    [scrollView setContentOffset:offset animated:NO];
}
95
David Liu

La réponse générale est que [scrollView setContentOffset:offset animated:NO] est pas le même que [scrollView setContentOffset:offset]!

  • [scrollView setContentOffset:offset animated:NO] arrête en réalité toute animation en cours.
  • [scrollView setContentOffset:offset] n'arrête aucune animation en cours. 
  • Idem pour scrollView.contentOffset = offset: n'arrête aucune animation en cours. 

Ce n'est documenté nulle part, mais c'est le comportement testé sur iOS 6.1 et iOS 7.1 - probablement aussi avant.

Donc, la solution pour arrêter une animation/décélération en cours est aussi simple que cela:

[scrollView setContentOffset:scrollView.contentOffset animated:NO];

Eh bien, fondamentalement, ce que David Liu a dit dans sa réponse corrigée. Mais je voulais préciser que ces deux API sont ET NON identiques.

Swift 3:

scrollView.setContentOffset(scrollView.contentOffset, animated: false)
69
calimarkus

Pour moi, David Lui a accepté la réponse ci-dessus n'a pas fonctionné pour moi. C'est ce que j'ai fini par faire:

- (void)killScroll {
    self.scrollView.scrollEnabled = NO;
    self.scrollView.scrollEnabled = YES;
}

Pour ce que cela vaut, j'utilise le simulateur iPhone 6.0 pour iPhone.

64
TPoschel

Voici ce que je fais pour mes vues de défilement et toutes les autres sous-classes associées:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
 {
    *targetContentOffset = scrollView.contentOffset;
 }

Ceci définit la targetContentOffset sur le décalage actuel de la scrollView, ce qui arrête le défilement car il a atteint la cible ..__ Il est logique d'utiliser une méthode ayant pour but de permettre aux utilisateurs de définir la contentOffset cible.

Rapide

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    targetContentOffset.pointee = scrollView.contentOffset
}
16
funct7

Arrêtez le défilement dans Swift :

scrollView.setContentOffset(scrollView.contentOffset, animated: false)
12
budidino

En fait ... La manière la plus "moderne" serait ->

scrollview.panGestureRecognizer.enabled = false;
scrollview.panGestureRecognizer.enabled = true;

Cela désactive la reconnaissance des gestes qui est chargée de faire défiler l'écran pendant un instant, ce qui supprimera le toucher actuel. L'utilisateur aurait besoin de lever le doigt et de le reposer pour recommencer à faire défiler.

Éditer: Cela tue simplement le glissement actuel de l'utilisateur mais n'arrête pas immédiatement la décélération si la vue de défilement est actuellement dans cet état. Pour ce faire, les réponses acceptées sont en quelque sorte le meilleur moyen pour xD

[scrollview setContentOffset: scrollview.contentOffset animated:false];
12
Xatian

La manière la plus propre consiste à sous-classer UIScrollView et à fournir votre propre méthode setContentOffset. Cela devrait transmettre le message, seulement si vous n'avez pas activé votre propriété freeze boolean.

Ainsi:

BOOL freeze; // and the @property, @synthesize lines..

-(void)setContentOffset:(CGPoint)offset
{
    if ( !freeze ) [super setContentOffset:offset];
}

Puis, pour geler:

scrollView.freeze = YES;
5
mvds

Cette réponse a fonctionné pour moi: Désactiver le ralentissement d’UIScrollView

-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView{
    [scrollView setContentOffset:scrollView.contentOffset animated:YES];
}
3
Johnny Rockex

Je voulais désactiver le défilement uniquement lorsqu'un certain UIView dans la vue de défilement est la source de contact au cours du balayage. Il aurait fallu un peu de refactoring pour déplacer UIView en dehors de UIScrollView, car nous avions une hiérarchie de vues complexe.

En guise de solution de contournement, j'ai ajouté un seul UIPanGestureRecognizer à la sous-vue dans laquelle je voulais empêcher le défilement. Ce UIPanGestureRecognizer aura cancelsTouchesInView qui empêche l'activation du panGesture de UIScrollView.

C'est un peu un "bidouillage", mais c'est un changement super facile, et si vous utilisez un XIB ou un Storyboard, tout ce que vous avez à faire est de faire glisser le geste panoramique sur la sous-vue en question.

0
Daniel Williams

Cela fonctionne pour moi dans Swift 4.2:

   func killScroll() {
    self.scrollView.isScrollEnabled = false;
    self.scrollView.isScrollEnabled = true;
}

... comme une extension:

extension UIScrollView {
    func killScroll() {
        self.isScrollEnabled = false;
        self.isScrollEnabled = true;

    }
}
0
Stefan Liesendahl