Obtenez UIScrollView pour faire défiler vers le haut

Réponses:

316

MISE À JOUR POUR iOS 7

[self.scrollView setContentOffset:
    CGPointMake(0, -self.scrollView.contentInset.top) animated:YES];

ORIGINAL

[self.scrollView setContentOffset:CGPointZero animated:YES];

ou si vous souhaitez conserver la position de défilement horizontal et simplement réinitialiser la position verticale:

[self.scrollView setContentOffset:CGPointMake(self.scrollView.contentOffset.x, 0)
    animated:YES];
Rob Mayoff
la source
50
Si vous faites cela pour iOS 7, vous devrez peut-être prendre en compte le UIScrollView contentInset, malheureusement. [self.scrollView setContentOffset:CGPointMake(self.scrollView.contentOffset.x, -self.scrollView.contentInset.top) animated:YES];fait le travail pour moi
runmad
11
J'ai pu faire défiler vers le haut, juste en dessous de la barre de navigation et d'état. [scrollView setContentOffset:CGPointMake(0, -scrollView.contentInset.top) animated:YES];Cela fonctionne sous iOS 6 & 7
Amozoss
Cela n'a pas fonctionné pour moi, probablement parce que je fais quelque chose de fou avec une vue inversée dans un kit de sprite. Mais si quelqu'un d'autre est aussi fou que moi - et votre scrollview est en bas par défaut - cela le fera défiler vers le haut:[scrollView setContentOffset:CGPointMake(0, scrollView.contentSize.height-scrollView.frame.size.height) animated:YES];
GilesDMiddleton
Commentaire de fin de journée: vous devez ajuster l'encart de contenu quelle que soit la version du système d'exploitation. iOS 7 l'utilise pour permettre le défilement du contenu sous la barre d'état, mais il a toujours été exposé en tant que propriété, donc toujours potentiellement utilisé par quelqu'un.
Tommy
2
Pour iOS 11+, la réponse de Jakub Truhlář ci-dessous à l'aide de AdjustContentInset a été utile, du moins dans mon cas.
tnev
61

Voici une extension Swift qui facilite les choses:

extension UIScrollView {
    func scrollToTop() {
        let desiredOffset = CGPoint(x: 0, y: -contentInset.top)
        setContentOffset(desiredOffset, animated: true)
   }
}

Usage:

myScrollView.scrollToTop()
Andrew Schreiber
la source
40

iOS 11 et supérieur

essayez de travailler avec le nouveau adjustedContentInset.

Par exemple (faites défiler vers le haut):

var offset = CGPoint(
    x: -scrollView.contentInset.left, 
    y: -scrollView.contentInset.top)

if #available(iOS 11.0, *) {
    offset = CGPoint(
        x: -scrollView.adjustedContentInset.left, 
        y: -scrollView.adjustedContentInset.top)    
}

scrollView.setContentOffset(offset, animated: true)

Même si vous utilisez prefersLargeTitles, safe areaetc.

Jakub Truhlář
la source
40

Pour Swift 4

scrollView.setContentOffset(.zero, animated: true)
Rajasekhar Pasupuleti
la source
23

Utilisation setContentOffset:animated:

[scrollView setContentOffset:CGPointZero animated:YES];
Bryan Chen
la source
17

Réponse pour Swift 2.0 / 3.0 / 4.0 et iOS 7+:

let desiredOffset = CGPoint(x: 0, y: -self.scrollView.contentInset.top)
self.scrollView.setContentOffset(desiredOffset, animated: true)
worthbak
la source
8

Dans iOS7, j'ai eu du mal à obtenir une vue de défilement particulière pour aller en haut, ce qui fonctionnait dans iOS6, et je l'ai utilisée pour définir la vue de défilement pour aller en haut.

[self.myScroller scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
hoak
la source
J'ai le même problème dans iOS7. Est-ce que quelqu'un sait comment activer cela pour un UITableView?
DZenBot
1
C'est drôle comment cela fonctionne lorsque l'animation est NON, mais pas lorsqu'elle est animée est OUI. Si OUI, alors il défile presque jusqu'au topdevgm16
Matt Becker
4

Version Swift 3.0.1 de la réponse de Rob Mayoff :

self.scrollView.setContentOffset(
CGPoint(x: 0,y: -self.scrollView.contentInset.top),
animated: true)
Roni Leshes
la source
2

Pour répliquer complètement le comportement scrollToTop de la barre d'état, nous devons non seulement définir le contentOffset, mais également nous assurer que les scrollIndicators sont affichés. Sinon, l'utilisateur peut se perdre rapidement.

La seule méthode publique pour y parvenir est flashScrollIndicators. Malheureusement, l'appeler une fois après avoir défini contentOffset n'a aucun effet car il est réinitialisé immédiatement. J'ai trouvé que cela fonctionnait lorsque je faisais le flash à chaque fois scrollViewDidScroll:.

// define arbitrary tag number in a global constants or in the .pch file
#define SCROLLVIEW_IS_SCROLLING_TO_TOP_TAG 19291

- (void)scrollContentToTop {
    [self.scrollView setContentOffset:CGPointMake(self.scrollView.contentOffset.x, -self.scrollView.contentInset.top) animated:YES];

    self.scrollView.tag = SCROLLVIEW_IS_SCROLLING_TO_TOP_TAG;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.scrollView.tag = 0;
    });
}

Dans votre UIScrollViewDelegate (ou UITable / UICollectionViewDelegate) implémentez ceci:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (scrollView.tag == SCROLLVIEW_IS_SCROLLING_TO_TOP_TAG) {
        [scrollView flashScrollIndicators];
    }
}

Le délai de masquage est un peu plus court par rapport au comportement scrollToTop de la barre d'état, mais il a toujours l'air agréable.

Notez que j'abuse de la balise de vue pour communiquer l'état "isScrollingToTop" parce que j'en ai besoin à travers les contrôleurs de vue. Si vous utilisez des balises pour autre chose, vous voudrez peut-être les remplacer par une iVar ou une propriété.

Ortwin Gentz
la source
Je n'ai pas testé, mais cela devrait lancer cela sur la prochaine boucle d'exécution si vous utilisez simplement DISPATCH_TIME_NOW à la place. Merci d'avoir fait ce travail, je ne suis pas sûr d'en avoir encore besoin, mais bon à savoir.
Dan Rosenstark
Il y a quelque temps et je ne me souviens pas exactement mais je pense que cela n'a pas fonctionné. Le report au prochain runloop est généralement la première chose que j'essaye dans de tels cas.
Ortwin Gentz
2

Je pense avoir une réponse qui devrait être entièrement compatible avec iOS 11 ainsi que les versions antérieures (pour le défilement vertical)

Cela prend en compte le nouvel ajustéContentInset et tient également compte du décalage supplémentaire requis lorsque prefersLargeTitles est activé sur la barre de navigation, ce qui semble nécessiter un décalage supplémentaire de 52px en plus de la valeur par défaut.

C'était un peu délicat car l'ajustéContentInset change en fonction de l'état de titleBar (grand titre vs petit titre), je devais donc vérifier et voir quelle était la hauteur de titleBar et ne pas appliquer le décalage de 52px s'il était déjà dans le grand état. Impossible de trouver une autre méthode pour vérifier l'état de la barre de navigation, donc si quelqu'un a une meilleure option que de voir si la hauteur est> 44,0, j'aimerais l'entendre

func scrollToTop(_ scrollView: UIScrollView, animated: Bool = true) {
    if #available(iOS 11.0, *) {
        let expandedBar = (navigationController?.navigationBar.frame.height ?? 64.0 > 44.0)
        let largeTitles = (navigationController?.navigationBar.prefersLargeTitles) ?? false
        let offset: CGFloat = (largeTitles && !expandedBar) ? 52: 0
        scrollView.setContentOffset(CGPoint(x: 0, y: -(scrollView.adjustedContentInset.top + offset)), animated: animated)
    } else {
        scrollView.setContentOffset(CGPoint(x: 0, y: -scrollView.contentInset.top), animated: animated)
    }
}

Inspiré de la solution de Jakub

user2898617
la source
2

Il est très courant que votre barre de navigation chevauche la petite partie du contenu scrollView et qu'il semble que le contenu ne commence pas par le haut. Pour le réparer, j'ai fait 2 choses:

  • Inspecteur de taille - Affichage de défilement - Encarts de contenu -> Passer d'automatique à Jamais.
  • Inspecteur de taille - Contraintes - "Aligner le haut sur" (Contraintes d'alignement du haut) - Deuxième élément -> Passer de Superview.Top à Safe Area.Top et la valeur (champ constant) définie sur 0

Encarts de contenu - Jamais Alignez ScrolView.Top sur Safe Area.Top

Vitya Shurapov
la source
1

Défilement vers le haut pour UITableViewController, UICollectionViewControllerou tout UIViewControllerayantUIScrollView

extension UIViewController {

  func scrollToTop(animated: Bool) {
    if let tv = self as? UITableViewController {
        tv.tableView.setContentOffset(CGPoint.zero, animated: animated)
    } else if let cv = self as? UICollectionViewController{
        cv.collectionView?.setContentOffset(CGPoint.zero, animated: animated)
    } else {
        for v in view.subviews {
            if let sv = v as? UIScrollView {
                sv.setContentOffset(CGPoint.zero, animated: animated)
            }
        }
    }
  }
}
Shoaib
la source
0

Dans SWIFT 5, réglez simplement le décalage de contenu à zéro

scrollView.setContentOffset(CGPoint.zero, animated: true)
Abdul Karim Khan
la source
-4

J'ai essayé toutes les manières. Mais rien n'a fonctionné pour moi. Finalement, j'ai aimé ça.

J'ai ajouté une self.view .addSubview(self.scroll)ligne de code dans le viewDidLoad. Après avoir commencé à configurer le cadre pour la vue de défilement et ajouté des composants pour la vue de défilement.

Cela a fonctionné pour moi.

Assurez-vous d'avoir ajouté une self.view .addSubview(self.scroll)ligne au début. alors vous pouvez ajouter des éléments d'interface utilisateur.

Narasimha Nallamsetty
la source
Veuillez supprimer ceci. Bien que cela ait sûrement eu du sens à l'époque, vos problèmes de hiérarchie de sous-vues ne sont pas pertinents pour cette question.
Dan Rosenstark
1
@Cristik si votre vue de défilement n'est pas dans la hiérarchie des vues ou si votre ordinateur est éteint, bien sûr, rien de tout cela ne fonctionnera. Mais le PO a demandé quelque chose d'assez étroit
Dan Rosenstark