UIScrollView gulir ke bawah secara terprogram

298

Bagaimana saya bisa membuat UIScrollViewgulir ke bagian bawah dalam kode saya? Atau dengan cara yang lebih umum, ke suatu titik subview?

nico
sumber

Jawaban:

626

Anda dapat menggunakan fungsi UIScrollView setContentOffset:animated:untuk menggulir ke bagian mana pun dari tampilan konten. Berikut beberapa kode yang akan bergulir ke bawah, dengan anggapan scrollView Anda adalah 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];

Semoga itu bisa membantu!

Ben Gotow
sumber
25
Anda mungkin ingin yang berikut untuk menggulir ke bawah, bukannya keluar dari scrollview: CGPoint bottomOffset = CGPointMake (0, [self.scrollView contentSize] .height - self.scrollView.frame.size.height);
Grantland Chew
70
Anda tidak memperhitungkan inset. Saya pikir Anda sebaiknya memilih bottomOffset ini:CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.contentInset.bottom);
Martin
1
ini tidak berfungsi dalam mode Lansekap! tidak sepenuhnya gulir ke bawah
Mo Farhand
1
Tidak akan berfungsi dengan baik jika contentSizelebih rendah dari bounds. Jadi harus seperti ini:scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)
Bartłomiej Semańczyk
1
Ini menangani inset bagian bawah, dan melampaui 0:let bottomOffsetY = max(collectionView.contentSize.height - collectionView.bounds.height + collectionView.contentInset.bottom, 0)
Senseful
136

Versi cepat dari jawaban yang diterima untuk menempelkan salinan dengan mudah:

let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.size.height)
scrollView.setContentOffset(bottomOffset, animated: true)
Esqarrouth
sumber
3
bottomOffset harus dibiarkan dan tidak var dalam contoh Anda.
Hobbes the Tige
benar, mengubahnya. swift2 stuff :)
Esqarrouth
9
Anda perlu memeriksa apakah scrollView.contentSize.height - scrollView.bounds.size.height> 0 jika tidak, Anda akan mendapatkan hasil yang aneh
iluvatar_GR
2
Solusi ini tidak sempurna ketika Anda memiliki bilah navigasi baru.
Swift Rabbit
@Josh, Masih menunggu hari SO tahu tentang ini
Alexandre G
53

Solusi paling sederhana:

[scrollview scrollRectToVisible:CGRectMake(scrollview.contentSize.width - 1,scrollview.contentSize.height - 1, 1, 1) animated:YES];
Hai Hw
sumber
Terima kasih. Saya lupa saya mengubah scrollview saya menjadi UITableView dan kode ini juga berfungsi untuk UITableView, FYI.
LevinsonTeknologi
1
Mengapa tidak adil: [scrollview scrollRectToVisible: CGRectMake (0, scrollview.contentSize.height, 1, 1) animated: YES];
Johannes
29

Hanya penyempurnaan jawaban yang ada.

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

Ini menangani inset bagian bawah juga (jika Anda menggunakannya untuk menyesuaikan tampilan gulir saat keyboard terlihat)

vivek241
sumber
Ini harus menjadi jawaban yang benar ... bukan yang lain yang akan pecah jika mereka memiliki keyboard yang muncul.
Ben Guild
20

Implementasi cepat:

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

Gunakan:

yourScrollview.scrollToBottom(animated: true)
duan
sumber
19

Menyetel konten diimbangi dengan ketinggian ukuran konten salah: ia menggulung bagian bawah konten ke bagian atas tampilan gulir, dan karenanya tidak terlihat.

Solusi yang benar adalah dengan menggulir bagian bawah konten ke bagian bawah tampilan gulir, seperti ini ( svadalah 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
sumber
1
Bukankah seharusnya begitu if (sv.contentOffset.y + csz.height > bsz.height) {?
i_am_jorf
@jeffamaphone - Terima kasih telah bertanya. Sebenarnya pada titik ini saya bahkan tidak yakin apa tujuan dari kondisi yang seharusnya dilayani! Itu setContentOffset:perintah yang penting.
matt
Saya kira untuk menghindari panggilan setContentOffset jika tidak akan melakukan apa pun?
i_am_jorf
@jeffamaphone - Dulu setelah rotasi perangkat, tampilan gulir dapat berakhir dengan bagian bawah kontennya lebih tinggi dari bagian bawah bingkai (karena jika kita memutar tampilan gulir, kontennya offset tetap konstan, tetapi ketinggian tampilan gulir mungkin bertambah karena untuk autoresizing). Syaratnya mengatakan: Jika itu terjadi, letakkan kembali konten di bagian bawah bingkai. Tapi itu konyol saya untuk memasukkan kondisi ketika saya menempelkan dalam kode. Namun, kondisi ini menguji dengan benar untuk apa pengujian itu dilakukan.
matt
15

Solusi Swift 2.2, dengan contentInsetmempertimbangkan

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

Ini harus dalam ekstensi

extension UIScrollView {

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

Perhatikan bahwa Anda mungkin ingin memeriksa apakah bottomOffset.y > 0sebelum menggulir

onmyway133
sumber
14

Bagaimana jika contentSizelebih rendah daribounds ?

Untuk Swift adalah:

scrollView.setContentOffset(CGPointMake(0, max(scrollView.contentSize.height - scrollView.bounds.size.height, 0) ), animated: true)
Bartłomiej Semańczyk
sumber
13

Gulir ke Atas

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

Gulir ke Bawah

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

Saya juga menemukan cara lain yang bermanfaat untuk melakukan ini jika Anda menggunakan UITableview (yang merupakan subkelas dari UIScrollView):

[(UITableView *)self.view scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
nico
sumber
FYI, itu hanya kemampuan UITableView
OhadM
7

Menggunakan setContentOffset:animated:fungsi UIScrollView untuk menggulir ke bawah di Swift.

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

Sepertinya semua jawaban di sini tidak mempertimbangkan area aman. Sejak iOS 11, iPhone X telah memperkenalkan area yang aman. Ini mungkin memengaruhi scrollView contentInset.

Untuk iOS 11 dan di atasnya, untuk menggulir ke bawah dengan benar dengan memasukkan inset konten. Anda harus menggunakan adjustedContentInsetbukan contentInset. Periksa kode ini:

  • Cepat:
let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.height + scrollView.adjustedContentInset.bottom)
scrollView.setContentOffset(bottomOffset, animated: true)
  • Objektif-C
CGPoint bottomOffset = CGPointMake(0, self.scrollView.contentSize.height - self.scrollView.bounds.size.height + self.scrollView.adjustedContentInset.bottom);
[self.scrollView setContentOffset:bottomOffset animated:YES];
  • Ekstensi cepat (ini menjaga yang asli 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)
    }
}

Referensi:

Honghao Zhang
sumber
5

Dengan (opsional) footerViewdan contentInset, solusinya adalah:

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

Cepat:

Anda dapat menggunakan ekstensi seperti ini:

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

Menggunakan:

scrollView.scrollsToBottom(animated: true)
Mauro García
sumber
3

valdyr, harap ini akan membantu Anda:

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

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

Kategori untuk penyelamatan!

Tambahkan ini ke tajuk utilitas bersama di suatu tempat:

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

Dan kemudian untuk implementasi utilitas:

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

Kemudian Terapkan di mana pun Anda suka, misalnya:

[[myWebView scrollView] scrollToBottomAnimated:YES];
BadPirate
sumber
Selalu, selalu, selalu, selalu gunakan kategori! Sempurna :)
Fattie
2

Untuk ScrollView Horizontal

Jika Anda menyukai saya memiliki Horizontal ScrollView dan ingin menggulir ke akhir itu (dalam kasus saya di sebelah kanan itu), Anda perlu mengubah beberapa bagian dari jawaban yang diterima:

Objektif-C

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

Cepat

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
sumber
2

Jika Anda entah bagaimana mengubah scrollView contentSize (mis. Menambahkan sesuatu ke stackView yang ada di dalam scrollView) Anda harus memanggilscrollView.layoutIfNeeded() sebelum menggulir, jika tidak maka tidak akan melakukan apa-apa.

Contoh:

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
sumber
1

Cara yang baik untuk memastikan bagian bawah konten Anda terlihat adalah dengan menggunakan rumus:

contentOffsetY = MIN(0, contentHeight - boundsHeight)

Ini memastikan tepi bawah konten Anda selalu di atau di atas tepi bawah tampilan. Ini MIN(0, ...)diperlukan karena UITableView(dan mungkin UIScrollView) memastikan contentOffsetY >= 0ketika pengguna mencoba menggulir dengan menjentikkannya contentOffsetY = 0. Ini terlihat sangat aneh bagi pengguna.

Kode untuk mengimplementasikan ini adalah:

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
sumber
1

Jika Anda tidak membutuhkan animasi, ini berfungsi:

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

Sementara Mattsolusi tampaknya benar bagi saya, Anda perlu mempertimbangkan juga inset tampilan koleksi jika ada satu yang telah diatur.

Kode yang diadaptasi adalah:

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
sumber
1

Dengan cepat:

   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
sumber
1

Solusi untuk menggulir ke item terakhir dari tabel Lihat:

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
sumber
0

Tidak bekerja untuk saya, ketika saya mencoba untuk menggunakannya di UITableViewControlleratas self.tableView (iOS 4.1), setelah menambahkanfooterView . Itu bergulir keluar dari perbatasan, menunjukkan layar hitam.

Solusi alternatif:

 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
sumber
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
sumber
0

Saya menemukan bahwa contentSizeitu tidak benar-benar mencerminkan ukuran sebenarnya dari teks, jadi ketika mencoba untuk menggulir ke bawah, itu akan menjadi sedikit tidak aktif. Cara terbaik untuk menentukan ukuran konten sebenarnya adalah dengan menggunakan metode NSLayoutManager's usedRectForTextContainer::

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

Untuk menentukan berapa banyak teks yang benar-benar ditampilkan di UITextView, Anda dapat menghitungnya dengan mengurangi insets wadah teks dari ketinggian bingkai.

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

Maka menjadi mudah untuk menggulir:

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

Beberapa angka untuk referensi tentang apa ukuran yang berbeda mewakili untuk kosong 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
sumber
0

Perluas UIScrollView untuk menambahkan metode 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)
        }
    }
}
jeune-loup
sumber
Apa yang ini tambahkan ke jawaban yang ada?
greybeard
-1

Versi Xamarin.iOS dari jawaban yang diterima

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

scrollView.SetContentOffset (bottomOffset, false);
Abdur
sumber
-1

Versi Xamarin.iOS untuk UICollectionViewjawaban yang diterima untuk kemudahan dalam menyalin dan menempel

var bottomOffset = new CGPoint (0, CollectionView.ContentSize.Height - CollectionView.Frame.Size.Height + CollectionView.ContentInset.Bottom);          
CollectionView.SetContentOffset (bottomOffset, false);
Abdur
sumber