UIScrollView przewiń programowo do dołu


Odpowiedzi:


626

Możesz użyć funkcji UIScrollView, setContentOffset:animated:aby przewinąć do dowolnej części widoku zawartości. Oto kod, który przewinąłby do dołu, zakładając, że scrollView to 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];

Mam nadzieję, że to pomaga!


25
prawdopodobnie chcesz, aby przewinąć do dołu zamiast z widoku przewijania: CGPoint bottomOffset = CGPointMake (0, [self.scrollView contentSize] .height - self.scrollView.frame.size.height);
Grantland Chew

70
Nie bierzesz pod uwagę wypustek. Myślę, że powinieneś preferować ten bottomOffset:CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
Martin

1
to nie działa w trybie poziomym! nie do końca przewiń w dół
Mo Farhand,

1
Nie będzie działać poprawnie, jeśli contentSizejest niższy niż bounds. Tak powinno być:scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)
Bartłomiej Semańczyk

1
To obsługuje dolną wstawkę i wykracza poza 0:let bottomOffsetY = max(collectionView.contentSize.height - collectionView.bounds.height + collectionView.contentInset.bottom, 0)
Sensowne

136

Szybka wersja zaakceptowanej odpowiedzi do łatwego wklejania kopii:

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

3
bottomOffset powinno być dozwolone, a nie zmienne w twoim przykładzie.
Hobbes the Tige,

prawda, zmieniłem to. swift2 rzeczy :)
Esqarrouth,

9
musisz sprawdzić, czy scrollView.contentSize.height - scrollView.bounds.size.height> 0 w przeciwnym razie będziesz mieć dziwne wyniki
iluvatar_GR

2
To rozwiązanie nie jest idealne, gdy masz nowy pasek nawigacyjny.
Swift Rabbit

@Josh, Wciąż czekam na dzień SO dowiaduje się o tym
Alexandre G

53

Najprostsze rozwiązanie:

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

Dzięki. Zapomniałem, że zmieniłem mój scrollview na UITableView i ten kod działał również dla UITableView, FYI.
LevinsonTechnologies

1
Dlaczego nie tylko: [scrollview scrollRectToVisible: CGRectMake (0, scrollview.contentSize.height, 1, 1) animowany: TAK];
Johannes,

29

Tylko rozszerzenie istniejącej odpowiedzi.

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

Dba również o dolną wstawkę (na wypadek, gdybyś używał jej do dostosowania widoku przewijania, gdy klawiatura jest widoczna)


To powinna być poprawna odpowiedź ... nie te, które się złamią, jeśli kiedykolwiek pojawią się klawisze.
Ben Guild

19

Ustawienie przesunięcia zawartości do wysokości rozmiaru zawartości jest nieprawidłowe: przewija dolną część zawartości do góry widoku przewijania, a tym samym poza zasięg wzroku.

Prawidłowym rozwiązaniem jest przewinięcie dolnej części zawartości do dolnej części przewijanego widoku, tak jak to ( svto 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
Nie powinno tak być if (sv.contentOffset.y + csz.height > bsz.height) {?
i_am_jorf

@jeffamaphone - Dziękujemy za pytanie. Właściwie w tym momencie nie jestem nawet pewien, w jakim celu ten warunek miał służyć! To setContentOffset:polecenie jest ważne.
mat.

Zakładam, że omija połączenie setContentOffset, jeśli nic nie zamierza zrobić?
i_am_jorf

@jeffamaphone - Kiedyś po obróceniu urządzenia widok przewijania mógł kończyć się treścią na dole wyższą niż dół ramki (ponieważ jeśli obrócimy widok przewijania, jego przesunięcie zawartości pozostanie stałe, ale wysokość widoku przewijania może wzrosnąć z powodu do automatycznej zmiany rozmiaru). Warunek mówi: Jeśli tak się stanie, odłóż zawartość z powrotem na dół na dole ramki. Ale głupio z mojej strony było uwzględnienie tego warunku, kiedy wkleiłem kod. Jednak warunek jest poprawnie testowany pod kątem tego, dla czego był testowany.
mat

19

Szybka implementacja:

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

Użyj tego:

yourScrollview.scrollToBottom(animated: true)

15

Biorąc contentInsetpod uwagę rozwiązanie Swift 2.2

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

To powinno być rozszerzenie

extension UIScrollView {

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

Pamiętaj, że możesz sprawdzić, czy bottomOffset.y > 0przed przewinięciem


14

Co jeśli contentSizejest niższy niż bounds?

Dla Swift jest to:

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

13

Przewiń na górę

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

Przewiń do dołu

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

7

Znalazłem również inny użyteczny sposób na zrobienie tego w przypadku korzystania z UITableview (który jest podklasą UIScrollView):

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

Do Twojej wiadomości, to tylko
funkcja

7

Korzystanie z setContentOffset:animated:funkcji UIScrollView do przewijania w dół w Swift.

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

5

Wygląda na to, że wszystkie odpowiedzi tutaj nie uwzględniały bezpiecznego obszaru. Od iOS 11 iPhone X miał bezpieczny obszar. Może to wpłynąć na scrollViewcontentInset .

W przypadku iOS 11 i nowszych, aby poprawnie przewinąć do dołu z dołączoną wstawką zawartości. Powinieneś użyć adjustedContentInsetzamiast contentInset. Sprawdź ten kod:

  • Szybki:
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.height + scrollView.adjustedContentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)
  • Cel C
CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.adjustedContentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];
  • Szybkie rozszerzenie (zachowuje oryginał 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)
    }
}

Bibliografia:


5

W przypadku (opcjonalnie) footerViewi contentInsetrozwiązaniem jest:

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

4

Szybki:

Możesz użyć takiego rozszerzenia:

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

Posługiwać się:

scrollView.scrollsToBottom(animated: true)

3

valdyr, mam nadzieję, że to ci pomoże:

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

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

2

Kategoria na ratunek!

Dodaj to do współdzielonego nagłówka narzędzia gdzieś:

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

A następnie do implementacji tego narzędzia:

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

Następnie zaimplementuj go w dowolnym miejscu, na przykład:

[[myWebView scrollView] scrollToBottomAnimated:YES];

Zawsze, zawsze, zawsze, zawsze używaj kategorii! Idealnie :)
Fattie

2

Dla widoku poziomego ScrollView

Jeśli podoba mi się, że ma widok poziomy ScrollView i chcesz przewinąć do jego końca (w moim przypadku do jego prawej strony), musisz zmienić niektóre części akceptowanej odpowiedzi:

Cel C

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

Szybki

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

Jeśli w jakiś sposób zmienisz scrollView contentSize (np. Dodasz coś do stackView, który znajduje się w scrollView), musisz wywołać scrollView.layoutIfNeeded()przed przewinięciem , w przeciwnym razie nic nie zrobi.

Przykład:

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

Dobrym sposobem na zapewnienie widoczności dolnej części treści jest użycie formuły:

contentOffsetY = MIN(0, contentHeight - boundsHeight)

Dzięki temu dolna krawędź treści zawsze znajduje się na dolnej krawędzi widoku lub powyżej niej. Jest MIN(0, ...)to wymagane, ponieważ UITableView(i prawdopodobnie UIScrollView) zapewnia, contentOffsetY >= 0gdy użytkownik próbuje przewijać poprzez widoczne przyciąganie contentOffsetY = 0. Wygląda to dość dziwnie dla użytkownika.

Kod do wdrożenia to:

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

Jeśli nie potrzebujesz animacji, działa to:

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

1

Chociaż Mattrozwiązanie wydaje mi się poprawne, należy uwzględnić również wstawkę widoku kolekcji, jeśli istnieje taka, która została skonfigurowana.

Dostosowany kod będzie:

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

W krótkim:

   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

Rozwiązanie, aby przewinąć do ostatniego elementu tabeli Widok:

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

Nie działa na mnie, gdy próbowałem go używać w UITableViewControllersprawie self.tableView (iOS 4.1), po dodaniufooterView . Przewija się poza granice, pokazując czarny ekran.

Alternatywne rozwiązanie:

 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

Przekonałem się, że contentSizetak naprawdę nie odzwierciedla faktycznego rozmiaru tekstu, więc przy próbie przewinięcia w dół będzie to nieco odsuwane. Najlepszym sposobem, aby określić rzeczywisty rozmiar zawartość jest rzeczywiście korzystać z NSLayoutManager„s usedRectForTextContainer:metody:

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

Aby ustalić, ile tekstu faktycznie ma być wyświetlany UITextView, możesz go obliczyć, odejmując wstawki kontenera tekstu od wysokości ramki.

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

Następnie łatwo jest przewijać:

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

Niektóre liczby w celach informacyjnych dotyczące różnych rozmiarów pustych 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

Rozszerz UIScrollView, aby dodać metodę 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)
        }
    }
}

Co to dodaje do istniejących odpowiedzi?
Greybeard

-1

Wersja akceptowanej odpowiedzi Xamarin.iOS

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

scrollView.SetContentOffset (bottomOffset, false);

-1

Wersja Xamarin.iOS dla UICollectionViewzaakceptowanej odpowiedzi ułatwiająca kopiowanie i wklejanie

var bottomOffset = new CGPoint (0, CollectionView.ContentSize.Height - CollectionView.Frame.Size.Height + CollectionView.ContentInset.Bottom);          
CollectionView.SetContentOffset (bottomOffset, false);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.