UIScrollView scorrere fino in fondo a livello di codice


298

Come posso UIScrollViewscorrere verso il basso nel mio codice? O in un modo più generico, a un certo punto di una sottoview?

Risposte:


626

È possibile utilizzare la setContentOffset:animated:funzione di UIScrollView per scorrere fino a qualsiasi parte della visualizzazione del contenuto. Ecco del codice che scorre verso il basso, supponendo che scrollView sia 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];

Spero che aiuti!


25
probabilmente vorrai quanto segue per scorrere verso il basso, invece che fuori dalla vista di scorrimento: CGPoint bottomOffset = CGPointMake (0, [self.scrollView contentSize] .height - self.scrollView.frame.size.height);
Grantland Chew

70
Non stai prendendo in considerazione le inserzioni. Penso che dovresti preferire questo bottomOffset:CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
Martin

1
questo non funziona in modalità orizzontale! non scorrere fino in fondo
Lu Farhand

1
Non funzionerà correttamente se contentSizeè inferiore a bounds. Quindi dovrebbe essere così:scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)
Bartłomiej Semańczyk il

1
Gestisce l' let bottomOffsetY = max(collectionView.contentSize.height - collectionView.bounds.height + collectionView.contentInset.bottom, 0)
inserzione

136

Versione rapida della risposta accettata per un facile incollaggio della copia:

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

3
bottomOffset dovrebbe essere lasciato e non var nell'esempio.
Hobbes the Tige,

vero, l'ho cambiato. roba swift2 :)
Esqarrouth,

9
devi verificare se scrollView.contentSize.height - scrollView.bounds.size.height> 0 altrimenti otterrai risultati
strani

2
Questa soluzione non è perfetta quando si dispone della nuova barra di navigazione.
Swift Rabbit

@Josh, sto ancora aspettando il giorno in cui SO lo scoprirà
Alexandre G,

53

Soluzione più semplice:

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

Grazie. Ho dimenticato di aver cambiato il mio scrollview in un UITableView e questo codice ha funzionato anche per un UITableView, FYI.
LevinsonTechnologies,

1
Perché non solo: [scrollview scrollRectToVisible: CGRectMake (0, scrollview.contentSize.height, 1, 1) animato: SÌ];
Johannes,

29

Solo un miglioramento alla risposta esistente.

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

Si occupa anche dell'inserto inferiore (nel caso in cui lo si usi per regolare la vista di scorrimento quando la tastiera è visibile)


Questa dovrebbe essere la risposta corretta ... non le altre che si romperanno se mai si aprirà una tastiera.
Ben Guild,

20

Un'implementazione rapida:

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)
  }
}

usalo:

yourScrollview.scrollToBottom(animated: true)

19

L'impostazione dell'offset del contenuto all'altezza della dimensione del contenuto è errata: scorre la parte inferiore del contenuto fino alla parte superiore della vista di scorrimento e quindi fuori dalla vista.

La soluzione corretta è far scorrere la parte inferiore del contenuto fino alla parte inferiore della vista di scorrimento, in questo modo ( svè 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
Non dovrebbe essere if (sv.contentOffset.y + csz.height > bsz.height) {?
i_am_jorf

@jeffamaphone - Grazie per avermelo chiesto. In realtà a questo punto non sono nemmeno sicuro di quale scopo avrebbe dovuto servire la condizione! È il setContentOffset:comando che è importante.
matt

Presumo che sia per evitare la chiamata setContentOffset se non ha intenzione di fare nulla?
i_am_jorf

@jeffamaphone - In passato, dopo la rotazione del dispositivo, una vista di scorrimento poteva terminare con il suo contenuto nella parte inferiore più alto della parte inferiore del riquadro (perché se ruotiamo una vista di scorrimento il suo offset del contenuto rimane costante, ma l'altezza della vista di scorrimento potrebbe essere cresciuta a causa al ridimensionamento automatico). La condizione dice: Se ciò accade, rimetti il ​​contenuto in basso nella parte inferiore del riquadro. Ma è stato sciocco da parte mia includere la condizione quando ho incollato il codice. Tuttavia, la condizione sta testando correttamente ciò per cui stava testando.
matt

15

Una soluzione Swift 2.2, tenendo contentInsetconto di

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

Questo dovrebbe essere in un'estensione

extension UIScrollView {

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

Nota che potresti voler verificare se bottomOffset.y > 0prima di scorrere


14

Cosa succede se contentSizeè inferiore a bounds?

Per Swift è:

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

13

Scorri verso l'alto

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

Scorri verso il basso

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

7

Ho anche trovato un altro modo utile per farlo nel caso in cui tu stia usando UITableview (che è una sottoclasse di UIScrollView):

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

Cordiali saluti, questa è solo una funzionalità UITableView
OhadM

7

Utilizzando la setContentOffset:animated:funzione di UIScrollView per scorrere fino in fondo in Swift.

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

5

Sembra che tutte le risposte qui non abbiano preso in considerazione l'area sicura. Da iOS 11, iPhone X ha introdotto un'area sicura. Ciò può influire su scrollView contentInset.

Per iOS 11 e versioni successive, scorrere correttamente verso il basso con l'inserzione del contenuto inclusa. Dovresti usare adjustedContentInsetinvece di contentInset. Controlla questo codice:

  • Swift:
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.height + scrollView.adjustedContentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)
  • Objective-C
CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.adjustedContentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];
  • Estensione rapida (questo mantiene l'originale 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)
    }
}

Riferimenti:


5

Con un (opzionale) footerViewe contentInset, la soluzione è:

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

4

Swift:

È possibile utilizzare un'estensione come questa:

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

Uso:

scrollView.scrollsToBottom(animated: true)

3

valdyr, spero che questo ti possa aiutare:

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

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

2

Categoria in soccorso!

Aggiungi questo a un'intestazione di utilità condivisa da qualche parte:

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

E poi all'implementazione dell'utilità:

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

Quindi implementalo dove vuoi, ad esempio:

[[myWebView scrollView] scrollToBottomAnimated:YES];

Usa sempre, sempre, sempre, usa sempre le categorie! Perfetto :)
Fattie

2

Per ScrollView orizzontale

Se ti piaccio ha un ScrollView orizzontale e vuoi scorrere fino alla fine (nel mio caso all'estrema destra di esso), devi cambiare alcune parti della risposta accettata:

Objective-C

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

veloce

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

Se in qualche modo cambi scrollView contentSize (es. Aggiungi qualcosa a stackView che è dentro scrollView) devi chiamare scrollView.layoutIfNeeded()prima dello scroll, altrimenti non fa nulla.

Esempio:

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 buon modo per garantire che la parte inferiore dei tuoi contenuti sia visibile è utilizzare la formula:

contentOffsetY = MIN(0, contentHeight - boundsHeight)

Ciò garantisce che il margine inferiore del contenuto sia sempre al di sopra o al margine inferiore della vista. L' MIN(0, ...)operazione è necessaria perché UITableView(e probabilmente UIScrollView) assicura contentOffsetY >= 0, quando i tentativi all'utente di scorrere da visibilmente scattare contentOffsetY = 0. Questo sembra abbastanza strano per l'utente.

Il codice per implementare questo è:

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

Se non hai bisogno di animazioni, funziona:

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

1

Mentre la Mattsoluzione mi sembra corretta, devi tenere conto anche dell'inserzione della vista raccolta se ce n'è una che è stata impostata.

Il codice adattato sarà:

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

In breve tempo:

   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

Soluzione per scorrere fino all'ultimo elemento di una tabella Visualizza:

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

Non ha funzionato per me, quando ho provato a usarlo UITableViewControllersu self.tableView (iOS 4.1)dopo aver aggiunto footerView. Scorre fuori dai bordi, mostrando uno schermo nero.

Soluzione alternativa:

 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

Ho scoperto che contentSizenon riflette davvero la dimensione effettiva del testo, quindi quando si tenta di scorrere verso il basso, sarà un po 'fuori. Il modo migliore per determinare la dimensione contenuto effettivo è in realtà per utilizzare il NSLayoutManager's usedRectForTextContainer:metodo:

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

Per determinare la quantità di testo effettivamente visualizzata in UITextView, è possibile calcolarla sottraendo gli inserti del contenitore di testo dall'altezza della cornice.

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

Quindi diventa facile scorrere:

// 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;

Alcuni numeri di riferimento su ciò che rappresentano le diverse dimensioni per un vuoto 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

Estendi UIScrollView per aggiungere un metodo 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)
        }
    }
}

Cosa aggiunge questo alle risposte esistenti?
greybeard,

-1

Versione Xamarin.iOS della risposta accettata

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

scrollView.SetContentOffset (bottomOffset, false);

-1

Versione Xamarin.iOS per UICollectionViewla risposta accettata per facilità di copia e incolla

var bottomOffset = new CGPoint (0, CollectionView.ContentSize.Height - CollectionView.Frame.Size.Height + CollectionView.ContentInset.Bottom);          
CollectionView.SetContentOffset (bottomOffset, false);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.