UIScrollView scrollen programmgesteuert nach unten


298

Wie kann ich UIScrollViewin meinem Code nach unten scrollen? Oder allgemeiner zu einem Punkt einer Unteransicht?

Antworten:


626

Mit der setContentOffset:animated:Funktion von UIScrollView können Sie zu einem beliebigen Teil der Inhaltsansicht blättern. Hier ist ein Code, der nach unten scrollen würde, vorausgesetzt, Ihre scrollView lautet 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];

Ich hoffe, das hilft!


25
Sie möchten wahrscheinlich Folgendes nach unten scrollen, anstatt aus der Bildlaufansicht heraus: CGPoint bottomOffset = CGPointMake (0, [self.scrollView contentSize] .height - self.scrollView.frame.size.height);
Grantland Chew

70
Sie berücksichtigen keine Einfügungen. Ich denke, Sie sollten dieses bottomOffset bevorzugen:CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
Martin

1
Dies funktioniert nicht im Querformat! nicht vollständig nach unten scrollen
Mo Farhand

1
Es wird nicht richtig funktionieren, wenn contentSizees niedriger als ist bounds. So sollte es sein:scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)
Bartłomiej Semańczyk

1
Dies behandelt den unteren Einschub und geht über 0 hinaus:let bottomOffsetY = max(collectionView.contentSize.height - collectionView.bounds.height + collectionView.contentInset.bottom, 0)
Senseful

136

Schnelle Version der akzeptierten Antwort zum einfachen Einfügen von Kopien:

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

3
bottomOffset sollte in Ihrem Beispiel let und nicht var sein.
Hobbes the Tige

stimmt, hat es geändert. swift2 stuff :)
Esqarrouth

9
Sie müssen überprüfen, ob scrollView.contentSize.height - scrollView.bounds.size.height> 0 ist, sonst haben Sie seltsame Ergebnisse
iluvatar_GR

2
Diese Lösung ist nicht perfekt, wenn Sie die neue Navigationsleiste haben.
Swift Rabbit

@ Josh, immer noch auf den Tag warten SO erfährt davon
Alexandre G

53

Einfachste Lösung:

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

Vielen Dank. Ich habe vergessen, dass ich meine Bildlaufansicht in eine UITableView geändert habe, und dieser Code funktionierte auch für eine UITableView, FYI.
LevinsonTechnologies

1
Warum nicht einfach: [scrollview scrollRectToVisible: CGRectMake (0, scrollview.contentSize.height, 1, 1) animiert: YES];
Johannes

29

Nur eine Erweiterung der bestehenden Antwort.

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

Es kümmert sich auch um den unteren Einschub (falls Sie diesen verwenden, um Ihre Bildlaufansicht anzupassen, wenn die Tastatur sichtbar ist)


Dies sollte die richtige Antwort sein ... nicht die anderen, die kaputt gehen, wenn jemals eine Tastatur auftaucht.
Ben Guild

19

Das Einstellen des Inhaltsversatzes auf die Höhe der Inhaltsgröße ist falsch: Es wird der untere Rand des Inhalts nach oben verschoben der Bildlaufansicht und ist somit nicht sichtbar.

Die richtige Lösung besteht darin, den unteren Rand des Inhalts wie folgt zum unteren Randsv der Bildlaufansicht zu scrollen ( ist die 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
Sollte es nicht sein if (sv.contentOffset.y + csz.height > bsz.height) {?
i_am_jorf

@ Jeffamaphone - Danke, dass Sie gefragt haben. Eigentlich bin ich mir zu diesem Zeitpunkt noch nicht einmal sicher, welchen Zweck die Bedingung erfüllen sollte! Es ist der setContentOffset:Befehl, der wichtig ist.
Matt

Ich gehe davon aus, dass der Aufruf von setContentOffset vermieden wird, wenn nichts unternommen wird.
i_am_jorf

@jeffamaphone - Früher konnte eine Bildlaufansicht nach dem Drehen des Geräts mit einem höheren Inhalt als dem unteren Rand des Rahmens enden (denn wenn wir eine Bildlaufansicht drehen, bleibt der Inhaltsversatz konstant, aber die Höhe der Bildlaufansicht ist möglicherweise gewachsen zur automatischen Größenänderung). Die Bedingung lautet: Wenn dies passiert, legen Sie den Inhalt wieder unten im Rahmen ab. Aber es war dumm von mir, die Bedingung beim Einfügen in den Code aufzunehmen. Die Bedingung testet jedoch korrekt auf das, worauf sie getestet wurde.
Matt

19

Eine schnelle Implementierung:

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

benutze es:

yourScrollview.scrollToBottom(animated: true)

15

Eine Swift 2.2-Lösung unter contentInsetBerücksichtigung

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

Dies sollte in einer Erweiterung sein

extension UIScrollView {

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

Beachten Sie, dass Sie bottomOffset.y > 0vor dem Scrollen überprüfen möchten, ob


14

Was ist, wenn contentSizeniedriger als ist bounds?

Für Swift ist es:

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

13

Scrolle nach oben

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

Scrollen Sie nach unten

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

7

Ich habe auch einen anderen nützlichen Weg gefunden, dies zu tun, wenn Sie eine UITableview verwenden (eine Unterklasse von UIScrollView):

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

Zu Ihrer Information, das ist nur eine UITableView-Funktion
OhadM

7

Verwenden der UIScrollView- setContentOffset:animated:Funktion, um in Swift nach unten zu scrollen.

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

5

Es sieht so aus, als hätten alle Antworten hier den sicheren Bereich nicht berücksichtigt. Seit iOS 11 wurde auf dem iPhone X ein sicherer Bereich eingeführt. Dies kann sich auf die scrollView auswirken contentInset.

Scrollen Sie für iOS 11 und höher mit dem enthaltenen Inhaltseinsatz ordnungsgemäß nach unten. Sie sollten adjustedContentInsetanstelle von verwenden contentInset. Überprüfen Sie diesen Code:

  • Schnell:
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.height + scrollView.adjustedContentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)
  • Ziel c
CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.adjustedContentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];
  • Schnelle Erweiterung (das Original bleibt erhalten 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)
    }
}

Verweise:


5

Mit einem (optionalen) footerViewund contentInsetlautet die Lösung:

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

4

Schnell:

Sie könnten eine Erweiterung wie diese verwenden:

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

Verwenden:

scrollView.scrollsToBottom(animated: true)

3

valdyr, hoffe das hilft dir:

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

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

2

Kategorie zur Rettung!

Fügen Sie dies irgendwo zu einem gemeinsam genutzten Dienstprogramm-Header hinzu:

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

Und dann zu dieser Dienstprogrammimplementierung:

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

Implementieren Sie es dann, wo immer Sie möchten, zum Beispiel:

[[myWebView scrollView] scrollToBottomAnimated:YES];

Verwenden Sie immer, immer, immer, immer Kategorien! Perfekt :)
Fattie

2

Für horizontale ScrollView

Wenn Sie möchten, dass ich eine horizontale Bildlaufansicht habe und zum Ende scrollen möchte (in meinem Fall ganz rechts davon), müssen Sie einige Teile der akzeptierten Antwort ändern:

Ziel c

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

Schnell

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

Wenn Sie scrollView contentSize irgendwie ändern (z. B. etwas zu stackView hinzufügen, das sich in scrollView befindet), müssen Sie aufrufenscrollView.layoutIfNeeded() vor dem Scrollen , da dies sonst nichts bewirkt.

Beispiel:

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

Eine gute Möglichkeit, um sicherzustellen, dass der untere Teil Ihres Inhalts sichtbar ist, ist die Verwendung der folgenden Formel:

contentOffsetY = MIN(0, contentHeight - boundsHeight)

Dadurch wird sichergestellt, dass sich der untere Rand Ihres Inhalts immer am oder über dem unteren Rand der Ansicht befindet. Dies MIN(0, ...)ist erforderlich, weil UITableView(und wahrscheinlich UIScrollView) sichergestellt wird, contentOffsetY >= 0wenn der Benutzer versucht, durch sichtbares Einrasten einen Bildlauf durchzuführencontentOffsetY = 0 . Das sieht für den Benutzer ziemlich seltsam aus.

Der Code, um dies zu implementieren, lautet:

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

Wenn Sie keine Animation benötigen, funktioniert dies:

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

1

Während Mattmir die Lösung richtig erscheint, müssen Sie auch die eingefügte Sammlungsansicht berücksichtigen, wenn eine eingerichtet wurde.

Der angepasste Code lautet:

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 Kürze:

   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

Lösung zum Scrollen zum letzten Element einer Tabelle Ansicht:

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

Haben Sie nicht für mich arbeiten, als ich versuchte , es zu benutzen , in UITableViewControllerauf self.tableView (iOS 4.1), nach dem HinzufügenfooterView . Es rollt aus den Rändern heraus und zeigt einen schwarzen Bildschirm.

Alternative Lösung:

 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

Ich habe festgestellt, dass contentSizedies nicht wirklich die tatsächliche Größe des Textes widerspiegelt. Wenn Sie also versuchen, nach unten zu scrollen, wird es ein wenig abweichen. Der beste Weg , die tatsächlichen Inhalt Größe zu bestimmen , ist tatsächlich die verwenden NSLayoutManager‚s - usedRectForTextContainer:Methode:

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

Um festzustellen, wie viel Text tatsächlich in der angezeigt wird UITextView, können Sie ihn berechnen, indem Sie die Textcontainer-Einfügungen von der Rahmenhöhe abziehen.

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

Dann wird es einfach zu scrollen:

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

Einige Zahlen als Referenz dafür, was die verschiedenen Größen für ein leeres darstellen 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

Erweitern Sie UIScrollView, um eine scrollToBottom-Methode hinzuzufügen:

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

Was trägt dies zu bestehenden Antworten bei?
Graubart

-1

Xamarin.iOS- Version der akzeptierten Antwort

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

scrollView.SetContentOffset (bottomOffset, false);

-1

Xamarin.iOS- Version für UICollectionViewdie akzeptierte Antwort zum einfachen Kopieren und Einfügen

var bottomOffset = new CGPoint (0, CollectionView.ContentSize.Height - CollectionView.Frame.Size.Height + CollectionView.ContentInset.Bottom);          
CollectionView.SetContentOffset (bottomOffset, false);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.