UIScrollView faire défiler vers le bas par programme


298

Comment faire UIScrollViewdéfiler vers le bas dans mon code? Ou d'une manière plus générique, à n'importe quel point d'une sous-vue?

Réponses:


626

Vous pouvez utiliser la setContentOffset:animated:fonction UIScrollView pour faire défiler jusqu'à n'importe quelle partie de la vue de contenu. Voici un code qui défilerait vers le bas, en supposant que votre scrollView est self.scrollView:

CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];

J'espère que cela pourra aider!


25
vous souhaiterez probablement que les éléments suivants défilent vers le bas, au lieu de sortir de la vue de défilement: CGPoint bottomOffset = CGPointMake (0, [self.scrollView contentSize] .height - self.scrollView.frame.size.height);
Grantland Chew

70
Vous ne tenez pas compte des encarts. Je pense que vous devriez préférer ce bas Offset:CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
Martin

1
cela ne fonctionne pas en mode paysage! pas complètement défiler vers le bas
Mo Farhand

1
Il ne fonctionnera pas correctement s'il contentSizeest inférieur à bounds. Il devrait donc en être ainsi:scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)
Bartłomiej Semańczyk

1
Cela gère l'encart inférieur et va au-delà de 0:let bottomOffsetY = max(collectionView.contentSize.height - collectionView.bounds.height + collectionView.contentInset.bottom, 0)
Sensible

136

Version rapide de la réponse acceptée pour un copier-coller facile:

let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height)
scrollView.setContentOffset(bottomOffset, animated: true)

3
bottomOffset doit être laissé et non var dans votre exemple.
Hobbes le Tige

vrai, l'a changé. swift2 stuff :)
Esqarrouth

9
vous devez vérifier si scrollView.contentSize.height - scrollView.bounds.size.height> 0 sinon vous obtiendrez des résultats
bizarres

2
Cette solution n'est pas parfaite lorsque vous avez la nouvelle barre de navigation.
Swift Rabbit

@Josh, toujours dans l'attente du jour où SO l'apprend
Alexandre G

53

Solution la plus simple:

[scrollview scrollRectToVisible:CGRectMake(scrollview.contentSize.width - 1,scrollview.contentSize.height - 1, 1, 1) animated:YES];

Merci. J'ai oublié que j'ai changé mon scrollview en UITableView et ce code a également fonctionné pour UITableView, FYI.
LevinsonTechnologies

1
Pourquoi pas simplement: [scrollview scrollRectToVisible: CGRectMake (0, scrollview.contentSize.height, 1, 1) animé: YES];
Johannes

29

Juste une amélioration de la réponse existante.

CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];

Il prend également en charge l'encart inférieur (au cas où vous l'utiliseriez pour ajuster votre vue de défilement lorsque le clavier est visible)


Cela devrait être la bonne réponse ... pas les autres qui se briseront si jamais un clavier apparaît.
Ben Guild

20

Une implémentation rapide:

extension UIScrollView {
   func scrollToBottom(animated: Bool) {
     if self.contentSize.height < self.bounds.size.height { return }
     let bottomOffset = CGPoint(x: 0, y: self.contentSize.height - self.bounds.size.height)
     self.setContentOffset(bottomOffset, animated: animated)
  }
}

utilise le:

yourScrollview.scrollToBottom(animated: true)

19

La définition du décalage du contenu à la hauteur de la taille du contenu est incorrecte: il fait défiler le bas du contenu vers le haut de la vue de défilement, et donc hors de vue.

La bonne solution consiste à faire défiler le bas du contenu vers le bas de la vue de défilement, comme ceci ( svest le UIScrollView):

CGSize csz = sv.contentSize;
CGSize bsz = sv.bounds.size;
if (sv.contentOffset.y + bsz.height > csz.height) {
    [sv setContentOffset:CGPointMake(sv.contentOffset.x, 
                                     csz.height - bsz.height) 
                animated:YES];
}

1
N'est-ce pas if (sv.contentOffset.y + csz.height > bsz.height) {?
i_am_jorf

@jeffamaphone - Merci d'avoir demandé. En fait, à ce stade, je ne sais même pas à quoi servait la condition! C'est la setContentOffset:commande qui est importante.
mat

Je suppose que c'est pour éviter l'appel setContentOffset s'il ne veut rien faire?
i_am_jorf

@jeffamaphone - Auparavant, après la rotation de l'appareil, une vue de défilement pouvait se terminer avec son contenu inférieur plus haut que le bas du cadre (car si nous faisons pivoter une vue de défilement, son décalage de contenu reste constant, mais la hauteur de la vue de défilement peut avoir augmenté en raison à redimensionnement automatique). La condition dit: Si cela se produit, remettez le contenu en bas en bas du cadre. Mais c'était stupide de ma part d'inclure la condition lorsque j'ai collé dans le code. La condition est de tester correctement pour ce qu'il a été testé, cependant.
mat

15

Une solution Swift 2.2, prenant contentInseten compte

let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)

Cela devrait être dans une extension

extension UIScrollView {

  func scrollToBottom() {
    let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height + contentInset.bottom)
    setContentOffset(bottomOffset, animated: true)
  }
}

Notez que vous voudrez peut-être vérifier si bottomOffset.y > 0avant de faire défiler


14

Et si contentSizeest inférieur àbounds ?

Pour Swift, c'est:

scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)

13

Faire défiler vers le haut

- CGPoint topOffset = CGPointMake(0, 0);
- [scrollView setContentOffset:topOffset animated:YES];

Faire défiler vers le bas

- CGPoint bottomOffset = CGPointMake(0, scrollView.contentSize.height - self.scrollView.bounds.size.height);
 - [scrollView setContentOffset:bottomOffset animated:YES];

7

J'ai également trouvé un autre moyen utile de le faire dans le cas où vous utilisez un UITableview (qui est une sous-classe de UIScrollView):

[(UITableView *)self.view scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

Pour info, c'est une capacité UITableView uniquement
OhadM

7

Utilisation de la setContentOffset:animated:fonction UIScrollView pour faire défiler vers le bas dans Swift.

let bottomOffset : CGPoint = CGPointMake(0, scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)

5

Il semble que toutes les réponses ici n'aient pas pris en compte la zone de sécurité. Depuis iOS 11, l'iPhone X a introduit une zone de sécurité. Cela peut affecter les scrollView contentInset.

Pour iOS 11 et supérieur, pour faire défiler correctement vers le bas avec l'encart de contenu inclus. Vous devez utiliser à la adjustedContentInsetplace de contentInset. Vérifiez ce code:

  • Rapide:
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.height + scrollView.adjustedContentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)
  • Objectif c
CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.adjustedContentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];
  • Extension Swift (cela conserve l'original contentOffset.x):
extension UIScrollView {
    func scrollsToBottom(animated: Bool) {
        let bottomOffset = CGPoint(x: contentOffset.x,
                                   y: contentSize.height - bounds.height + adjustedContentInset.bottom)
        setContentOffset(bottomOffset, animated: animated)
    }
}

Références:


5

Avec un (facultatif) footerViewet contentInset, la solution est:

CGPoint bottomOffset = CGPointMake(0, _tableView.contentSize.height - tableView.frame.size.height + _tableView.contentInset.bottom);
if (bottomOffset.y > 0) [_tableView setContentOffset: bottomOffset animated: YES];

4

Rapide:

Vous pouvez utiliser une extension comme celle-ci:

extension UIScrollView {
    func scrollsToBottom(animated: Bool) {
        let bottomOffset = CGPoint(x: 0, y: contentSize.height - bounds.size.height)
        setContentOffset(bottomOffset, animated: animated)
    }
}

Utilisation:

scrollView.scrollsToBottom(animated: true)

3

valdyr, j'espère que cela vous aidera:

CGPoint bottomOffset = CGPointMake(0, [textView contentSize].height - textView.frame.size.height);

if (bottomOffset.y > 0)
 [textView setContentOffset: bottomOffset animated: YES];

2

Catégorie à la rescousse!

Ajoutez ceci à un en-tête d'utilitaire partagé quelque part:

@interface UIScrollView (ScrollToBottom)
- (void)scrollToBottomAnimated:(BOOL)animated;
@end

Et puis à cette implémentation de l'utilitaire:

@implementation UIScrollView(ScrollToBottom)
- (void)scrollToBottomAnimated:(BOOL)animated
{
     CGPoint bottomOffset = CGPointMake(0, self.contentSize.height - self.bounds.size.height);
     [self setContentOffset:bottomOffset animated:animated];
}
@end

Ensuite, implémentez-le où vous voulez, par exemple:

[[myWebView scrollView] scrollToBottomAnimated:YES];

Toujours, toujours, toujours, utilisez toujours des catégories! Parfait :)
Fattie

2

Pour ScrollView horizontal

Si vous m'aimez a un ScrollView horizontal et que vous souhaitez faire défiler jusqu'à la fin (dans mon cas à l'extrême droite), vous devez modifier certaines parties de la réponse acceptée:

Objectif c

CGPoint rightOffset = CGPointMake(self.scrollView.contentSize.width - self.scrollView.bounds.size.width + self.scrollView.contentInset.right, 0 );
[self.scrollView setContentOffset:rightOffset animated:YES];

Rapide

let rightOffset: CGPoint = CGPoint(x: self.scrollView.contentSize.width - self.scrollView.bounds.size.width + self.scrollView.contentInset.right, y: 0)
self.scrollView.setContentOffset(rightOffset, animated: true)

2

Si vous changez en quelque sorte scrollView contentSize (par exemple, ajoutez quelque chose à stackView qui se trouve à l'intérieur de scrollView), vous devez appelerscrollView.layoutIfNeeded() avant de faire défiler, sinon cela ne fait rien.

Exemple:

scrollView.layoutIfNeeded()
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height + scrollView.contentInset.bottom)
if(bottomOffset.y > 0) {
    scrollView.setContentOffset(bottomOffset, animated: true)
}

1

Un bon moyen de s'assurer que le bas de votre contenu est visible est d'utiliser la formule:

contentOffsetY = MIN(0, contentHeight - boundsHeight)

Cela garantit que le bord inférieur de votre contenu est toujours au niveau ou au-dessus du bord inférieur de la vue. Le MIN(0, ...)est requis car UITableView(et probablement UIScrollView) garantit contentOffsetY >= 0que l'utilisateur essaie de faire défiler en claquant visiblementcontentOffsetY = 0 . Cela semble assez étrange pour l'utilisateur.

Le code pour implémenter ceci est:

UIScrollView scrollView = ...;
CGSize contentSize = scrollView.contentSize;
CGSize boundsSize = scrollView.bounds.size;
if (contentSize.height > boundsSize.height)
{
    CGPoint contentOffset = scrollView.contentOffset;
    contentOffset.y = contentSize.height - boundsSize.height;
    [scrollView setContentOffset:contentOffset animated:YES];
}

1

Si vous n'avez pas besoin d'animation, cela fonctionne:

[self.scrollView setContentOffset:CGPointMake(0, CGFLOAT_MAX) animated:NO];

1

Bien que la Mattsolution me semble correcte, vous devez également prendre en compte l'encart de la vue de collection s'il y en a un qui a été configuré.

Le code adapté sera:

CGSize csz = sv.contentSize;
CGSize bsz = sv.bounds.size;
NSInteger bottomInset = sv.contentInset.bottom;
if (sv.contentOffset.y + bsz.height + bottomInset > csz.height) {
    [sv setContentOffset:CGPointMake(sv.contentOffset.x, 
                                     csz.height - bsz.height + bottomInset) 
                animated:YES];
}

1

En bref:

   if self.mainScroll.contentSize.height > self.mainScroll.bounds.size.height {
        let bottomOffset = CGPointMake(0, self.mainScroll.contentSize.height - self.mainScroll.bounds.size.height);
        self.mainScroll.setContentOffset(bottomOffset, animated: true)
    }

1

Solution pour faire défiler jusqu'au dernier élément d'une table Voir:

Swift 3:

if self.items.count > 0 {
        self.tableView.scrollToRow(at:  IndexPath.init(row: self.items.count - 1, section: 0), at: UITableViewScrollPosition.bottom, animated: true)
}

0

N'a pas travaillé pour moi, quand j'ai essayé de l' utiliser dans UITableViewControllerle self.tableView (iOS 4.1), après avoir ajoutéfooterView . Il défile hors des frontières, montrant un écran noir.

Solution alternative:

 CGFloat height = self.tableView.contentSize.height; 

 [self.tableView setTableFooterView: myFooterView];
 [self.tableView reloadData];

 CGFloat delta = self.tableView.contentSize.height - height;
 CGPoint offset = [self.tableView contentOffset];
 offset.y += delta;

 [self.tableView setContentOffset: offset animated: YES];

0
CGFloat yOffset = scrollView.contentOffset.y;

CGFloat height = scrollView.frame.size.height;

CGFloat contentHeight = scrollView.contentSize.height;

CGFloat distance = (contentHeight  - height) - yOffset;

if(distance < 0)
{
    return ;
}

CGPoint offset = scrollView.contentOffset;

offset.y += distance;

[scrollView setContentOffset:offset animated:YES];

0

J'ai trouvé que contentSizecela ne reflète pas vraiment la taille réelle du texte, donc lorsque vous essayez de faire défiler vers le bas, il sera un peu éteint. La meilleure façon de déterminer la taille du contenu réel est en fait d'utiliser la NSLayoutManagerde » usedRectForTextContainer:méthode:

UITextView *textView;
CGSize textSize = [textView.layoutManager usedRectForTextContainer:textView.textContainer].size;

Pour déterminer la quantité de texte réellement affichée dans le UITextView, vous pouvez le calculer en soustrayant les encarts du conteneur de texte de la hauteur du cadre.

UITextView *textView;
UIEdgeInsets textInsets = textView.textContainerInset;
CGFloat textViewHeight = textView.frame.size.height - textInsets.top - textInsets.bottom;

Ensuite, il devient facile de faire défiler:

// if you want scroll animation, use contentOffset
UITextView *textView;
textView.contentOffset = CGPointMake(textView.contentOffset.x, textSize - textViewHeight);

// if you don't want scroll animation
CGRect scrollBounds = textView.bounds;
scrollBounds.origin = CGPointMake(textView.contentOffset.x, textSize - textViewHeight);
textView.bounds = scrollBounds;

Quelques chiffres pour référence sur ce que les différentes tailles représentent pour un vide UITextView.

textView.frame.size = (width=246, height=50)
textSize = (width=10, height=16.701999999999998)
textView.contentSize = (width=246, height=33)
textView.textContainerInset = (top=8, left=0, bottom=8, right=0)

0

Étendez UIScrollView pour ajouter une méthode scrollToBottom:

extension UIScrollView {
    func scrollToBottom(animated:Bool) {
        let offset = self.contentSize.height - self.visibleSize.height
        if offset > self.contentOffset.y {
            self.setContentOffset(CGPoint(x: 0, y: offset), animated: animated)
        }
    }
}

Qu'est-ce que cela ajoute aux réponses existantes?
greybeard

-1

Version Xamarin.iOS de la réponse acceptée

var bottomOffset = new CGPoint (0,
     scrollView.ContentSize.Height - scrollView.Frame.Size.Height
     + scrollView.ContentInset.Bottom);

scrollView.SetContentOffset (bottomOffset, false);

-1

Version Xamarin.iOS pour UICollectionViewla réponse acceptée pour faciliter le copier-coller

var bottomOffset = new CGPoint (0, CollectionView.ContentSize.Height - CollectionView.Frame.Size.Height + CollectionView.ContentInset.Bottom);          
CollectionView.SetContentOffset (bottomOffset, false);
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.