UIScrollView scrollen programmgesteuert nach unten

298

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

nico
quelle

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!

Ben Gotow
quelle
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)
Esqarrouth
quelle
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];
Hai Hw
quelle
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)

vivek241
quelle
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];
}
matt
quelle
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)
duan
quelle
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

onmyway133
quelle
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)
Bartłomiej Semańczyk
quelle
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];
Tahir Waseem
quelle
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];
nico
quelle
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)
Kim
quelle
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:

Honghao Zhang
quelle
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];
Pieter
quelle
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)
Mauro García
quelle
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];
Mischa
quelle
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];
BadPirate
quelle
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)
Esmaeil
quelle
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)
}
Robert Koval
quelle
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];
}
jjrscott
quelle
1

Wenn Sie keine Animation benötigen, funktioniert dies:

[self.scrollView setContentOffset:CGPointMake(0, CGFLOAT_MAX) animated:NO];
Krys Jurgowski
quelle
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];
}
Tiguero
quelle
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)
    }
Ponja
quelle
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)
}
MarionFlex
quelle
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];
valdyr
quelle
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];
8suhas
quelle
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)
mikeho
quelle
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)
        }
    }
}
Jeune-Loup
quelle
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);
Abdur
quelle
-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);
Abdur
quelle