Görünüm görünmeden önce iPhone'da UITableView'ın altına nasıl kaydırılır


136

Bir var UITableViewbir değişken yükseklik hücrelerle doldurulur söyledi. Görünüm görünüme getirildiğinde tablonun alta kaymasını istiyorum.

Şu anda aşağıdaki fonksiyona sahibim

NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[log count]-1 inSection:0];
[self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];

log, her bir hücrenin içeriğini oluşturan nesneleri içeren değiştirilebilir bir dizidir.

Yukarıdaki kod iyi çalışıyor, viewDidAppearancak görünüm ilk göründüğünde tablonun üstünü görüntülemenin ve sonra alta atlamanın talihsiz bir yan etkisi vardır. Eğer ortaya table viewçıkmadan önce aşağı kaydırılabilir olsaydı bunu tercih ederim .

Kaydırma denedim viewWillAppearve viewDidLoadher iki durumda da veri henüz tabloya yüklenmedi ve her ikisi de bir istisna attı.

Herhangi bir rehberlik çok takdir edilecektir, sadece bana sahip olduğumu söylemek mümkün olan her şey olsa bile.

Yanıtlar:


148

İnanıyorum ki

 tableView.setContentOffset(CGPoint(x: 0, y: CGFloat.greatestFiniteMagnitude), animated: false)

ne istersen yapacak.


14
Mükemmel teşekkürler. Her zaman en altta görüntülenmesini sağlayacak kadar yüksek bir Y değerine sahip bir CGPoint oluşturdum. Aynı yöntemle alt geçmek için - (self.table.frame.size.height self.table.contentSize.height) görünümü yüklendikten sonra kullanmak için
acqu13sce

8
Bu mükemmel bir cevap olmasına rağmen, kaç hücre, tableview yüksekliği vb. İçin hesaplama yapmamız gerekmediği için[table reloadData];
Fahim Parkar

4
ios 10-12 üzerinde çalışmaz - masa ilk kez kaybolur
Vyachaslav Gerchicov

2
Veya sadece CGPoint(x: 0, y: tableView.contentSize.height)?
Amber K

5
Amanın !!! Masayı ortadan kaldırır: -o. En iyi [self.table scrollToRowAtIndexPath: indexPath atScrollPosition: UITableViewScrollPositionBottom animated: NO];
Carl Hine

122

Bence en kolay yol şudur:

if (self.messages.count > 0)
{
    [self.tableView 
        scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.messages.count-1 
        inSection:0] 
        atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

Swift 3 Sürümü:

if messages.count > 0 {
    userDefinedOptionsTableView.scrollToRow(at: IndexPath(item:messages.count-1, section: 0), at: .bottom, animated: true)
}

3
Hücre UITableView'dan daha
uzunsa işe yaramaz

3
Hücre UITableView'dan Daha Uzun? hiç böyle bir kullanım durumu duymadım.
Chamira Fernando

@ChamiraFernando bu en kolay yol :)
AITAALI_ABDERRAHMANE

messages.countZaten uygulanmış olan ile değiştirmenin mantıklı olabileceğini unutmayın myTableView.dataSource!.tableView(myTableView, numberOfRowsInSection: 0). Evet, daha uzun, ancak kod tekrarını önleyebilir. Ayrıca isteğe bağlı işlemek gerekir dataSource(bu örnekte olduğu gibi açmayı zorlamayın).
Nikolay Suvandzhiev

@ChamiraFernando Bu sorunun eski olduğunu biliyorum, ama hiç görmediğiniz için bu gerçekleşmediği anlamına gelmiyor. Sorunuzu cevaplamak için Foursquare gibi uygulamalar, kullanıcının bir inceleme yazdığı bu duruma sahip olabilir. Hücrenin yüksekliği, tablo görünümünün yüksekliğinden daha büyüktür. Mükemmel bir durum.
Caio

121

Gönderen Yakup'un cevabı , bu kod şudur:

- (void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    if (self.messagesTableView.contentSize.height > self.messagesTableView.frame.size.height) 
    {
        CGPoint offset = CGPointMake(0, self.messagesTableView.contentSize.height - self.messagesTableView.frame.size.height);
        [self.messagesTableView setContentOffset:offset animated:YES];
    }
}

İOS 11'de ayarlanmış tablo görünümü çerçeve yüksekliğini kullanmalısınız:UIEdgeInsetsInsetRect(self.messagesTableView.frame, self.messagesTableView.safeAreaInsets).height
Slav

41

İçeriğin TAM sonuna gitmeniz gerekiyorsa, bunu şu şekilde yapabilirsiniz:

- (void)scrollToBottom
{
    CGFloat yOffset = 0;

    if (self.tableView.contentSize.height > self.tableView.bounds.size.height) {
        yOffset = self.tableView.contentSize.height - self.tableView.bounds.size.height;
    }

    [self.tableView setContentOffset:CGPointMake(0, yOffset) animated:NO];
}


Bunu neden yapmamız gerektiğini açıklar yOffset = self.tableView.contentSize.height - self.tableView.bounds.size.height; mısınız? Teşekkürler.
15'te Unheilig

3
@Unheilig self.tableView.contentSize.heightTablo görünümünün içeriğine kaydırırsanız , içeriğin altına kaydırdığınız için görünmeyebilir. Bu nedenle, tablo görünümünün sonunun üstünde bir "görünür tablo görünümü boşluğu" na ilerlemeniz gerekir.
Hans One

31

Ben autolayout kullanıyorum ve cevapların hiçbiri işe yaramadı. İşte sonunda işe yarayan çözümüm:

@property (nonatomic, assign) BOOL shouldScrollToLastRow;


- (void)viewDidLoad {
    [super viewDidLoad];

    _shouldScrollToLastRow = YES;
}


- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    // Scroll table view to the last row
    if (_shouldScrollToLastRow)
    {
        _shouldScrollToLastRow = NO;
        [self.tableView setContentOffset:CGPointMake(0, CGFLOAT_MAX)];
    }
}

1
Bu neredeyse benim için çalışıyor ama tablo verilerim harici bir API'dan yüklenirken garip bir grafik aksaklığı alıyorum. Benim durumumda setContentOffsetveriler getirilip tablo görünümü yeniden yüklendiğinde başka bir noktada aramam gerekiyor mu?
jmoz

Dengeyi, isteğinizin bir tamamlama işleyicisinde ayarlamayı deneyin.
Rafya

2
Bu ios 10'da çalışmaz - sadece siyah bir arka plana sahip bir tablo gösterir
RunLoop

2
Kullanmak yerine ilk içerik ofsetini ayarlarken CGFLOAT_MAXkullandım contentSize.height - frame.height + contentInset.bottom. Kullanımı CGFLOAT_MAXbenim için berbat görünüyordu.
Baza207

23

@JacobRelkin tarafından kabul edilen çözüm Otomatik Düzen kullanarak iOS 7.0 benim için çalışmadı.

Özel bir alt sınıf var UIViewControllerve _tableViewonun bir alt görünümü olarak bir örnek değişkeni ekledi view. _tableViewOtomatik Düzen kullanarak konumlandırdım . Sonunda viewDidLoadve hatta bu yöntemi çağırmayı denedim viewWillAppear:. İkisi de işe yaramadı.

Bu nedenle, özel alt sınıfıma aşağıdaki yöntemi ekledim UIViewController.

- (void)tableViewScrollToBottomAnimated:(BOOL)animated {
    NSInteger numberOfRows = [_tableView numberOfRowsInSection:0];
    if (numberOfRows) {
        [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:numberOfRows-1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated];
    }
}

İşlerin [self tableViewScrollToBottomAnimated:NO]sonunda arama viewDidLoad. Ne yazık ki, aynı zamanda neden olurtableView:heightForRowAtIndexPath: her hücre için üç kez çağrılmasına .


23

İşte Swift 2.0'da uyguladığım bir uzantı. Bu işlevler tableviewyüklendikten sonra çağrılmalıdır :

import UIKit

extension UITableView {
    func setOffsetToBottom(animated: Bool) {
        self.setContentOffset(CGPointMake(0, self.contentSize.height - self.frame.size.height), animated: true)
    }

    func scrollToLastRow(animated: Bool) {
        if self.numberOfRowsInSection(0) > 0 {
            self.scrollToRowAtIndexPath(NSIndexPath(forRow: self.numberOfRowsInSection(0) - 1, inSection: 0), atScrollPosition: .Bottom, animated: animated)
        }
    }
}

3
Bu, içerik boyutunu kullanmaktan daha iyidir. Swift3 için. eğer self.numberOfRows (inSection: 0)> 0 {self.scrollToRow (at: IndexPath.init (satır: self.numberOfRows (inSection: 0) -1, bölüm: 0), at: .bottom, canlandırılmış: canlandırılmış)}
Soohwan Park

scrollToLastRow bu yöntem mükemmel çalışmıyorsa, sadece self.layoutIfNeeded () ekleyin ve mükemmel çalışır!
Yogesh Patel

16

ayrıntılar

  • Xcode 8.3.2, hızlı 3.1
  • Xcode 10.2 (10E125), Swift 5

kod

import UIKit

extension UITableView {
    func scrollToBottom(animated: Bool) {
        let y = contentSize.height - frame.size.height
        if y < 0 { return }
        setContentOffset(CGPoint(x: 0, y: y), animated: animated)
    }
}

kullanım

tableView.scrollToBottom(animated: true)

Tam örnek

Çözüm kodunu yapıştırmayı unutmayın!

import UIKit

class ViewController: UIViewController {

    private weak var tableView: UITableView?
    private lazy var cellReuseIdentifier = "CellReuseIdentifier"

    override func viewDidLoad() {
        super.viewDidLoad()
        let tableView = UITableView(frame: view.frame)
        view.addSubview(tableView)
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)
        self.tableView = tableView
        tableView.dataSource = self
        tableView.performBatchUpdates(nil) { [weak self] result in
            if result { self?.tableView?.scrollToBottom(animated: true) }
        }
    }
}

extension ViewController: UITableViewDataSource {

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath )
        cell.textLabel?.text = "\(indexPath)"
        return cell
    }
}

15

Aslında bunu hızlı bir şekilde yapmanın bir "hızlı" yolu:

var lastIndex = NSIndexPath(forRow: self.messages.count - 1, inSection: 0)
self.messageTableView.scrollToRowAtIndexPath(lastIndex, atScrollPosition: UITableViewScrollPosition.Bottom, animated: true)

iş Benim için mükemmel.


9

Tablonun çerçevede gösterilen tablonun ucuyla yüklenmesini istedim. Kullanarak buldum

NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0];
[[self tableView] scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];

tablo yüksekliği çerçeve yüksekliğinden daha az olduğunda hata verdiğinden çalışmadı. Tablonun sadece bir bölümü olduğunu unutmayın.

Benim için çalışan çözüm viewWillAppear'da aşağıdaki kodu uygulamaktı:

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// on the initial cell load scroll to the last row (ie the latest Note)
if (initialLoad==TRUE) {
    initialLoad=FALSE; 
    NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0];
    [[self tableView] scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO];
        CGPoint offset = CGPointMake(0, (1000000.0));
        [self.tableView setContentOffset:offset animated:NO];
    }
}

BOOL ivar initialLoad, viewDidLoad içinde TRUE olarak ayarlanır.


Hiç aramaya ihtiyacın var scrollToRowAtIndexPathmı? Daha setContentOffsetsonra araıyorsunuz, bu da ilk çağrıyı anlamsız kılabilir.
Carlos P

9

Swift için:

if tableView.contentSize.height > tableView.frame.size.height {
    let offset = CGPoint(x: 0, y: tableView.contentSize.height - tableView.frame.size.height)
    tableView.setContentOffset(offset, animated: false)
}


5

Swift 3 (Xcode 8.1) için:

override func viewDidAppear(_ animated: Bool) {
    let numberOfSections = self.tableView.numberOfSections
    let numberOfRows = self.tableView.numberOfRows(inSection: numberOfSections-1)

    let indexPath = IndexPath(row: numberOfRows-1 , section: numberOfSections-1)
    self.tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.middle, animated: true)
}

3
Bu OP sorusunu cevaplamıyor, en başından beri çalışıyordu. Ayrıca super.viewDidAppear çağırmalıdır
Streem

4

Elbette bir böcek. Muhtemelen kodunuzda bir yerde kullanıyorsunuz table.estimatedRowHeight = value(örneğin 100). Bu değeri , örneğin bir satır yüksekliğinin alabileceğini düşündüğünüz en yüksek değerle değiştirin , örneğin 500 .. Bu, sorunu aşağıdaki kodla birlikte çözmelidir:

//auto scroll down example
let delay = 0.1 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))

dispatch_after(time, dispatch_get_main_queue(), {
    self.table.scrollToRowAtIndexPath(NSIndexPath(forRow: self.Messages.count - 1, inSection: 0), atScrollPosition: UITableViewScrollPosition.Bottom, animated: false)
})

4

Çok uğraştıktan sonra bu benim için işe yaradı:

var viewHasAppeared = false

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    if !viewHasAppeared { goToBottom() }
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    viewHasAppeared = true
}

private func goToBottom() {
    guard data.count > 0 else { return }
    let indexPath = NSIndexPath(forRow: data.count - 1, inSection: 0)
    tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: false)
    tableView.layoutIfNeeded()
}

Çıktı anahtar edilecek değil sarma scrollToRowAtIndexPathiçini dispatch_asyncbazılarının ileri sürdüğü gibi, ama sadece bir çağrıya ile aşağıdakilayoutIfNeeded .

Bunu anladığım kadarıyla, geçerli iş parçacığındaki kaydırma yöntemini çağırmak , görünüm görüntülenmeden önce kaydırma ofsetinin hemen ayarlanmasını garanti eder . Ana iş parçacığına gönderirken, kaydırma etkinleşmeden önce görünüm bir an için görüntüleniyordu.

(Ayrıca NB viewHasAppearedbayrağa ihtiyacınız vardır, çünkü goToBottomher seferinde aranmak istemezsiniz viewDidLayoutSubviews. Örneğin, yönelim değiştiğinde çağrılır.)


3

Yukarıdaki çözümleri kullanarak, tablonuzun en altına kaydırılır (yalnızca tablo içeriği önce yüklenmişse):

//Scroll to bottom of table
CGSize tableSize = myTableView.contentSize;
[myTableView setContentOffset:CGPointMake(0, tableSize.height)];

3

Swift 3.0'da

self.tableViewFeeds.setContentOffset(CGPoint(x: 0, y: CGFLOAT_MAX), animated: true)

2

Aşağı kaydırmadan önce verileri eşzamansız olarak yüklemeniz gerekiyorsa, olası çözüm şudur:

tableView.alpha = 0 // We want animation!
lastMessageShown = false // This is ivar

viewModel.fetch { [unowned self] result in
    self.tableView.reloadData()

    if !self.lastMessageShown {
        dispatch_async(dispatch_get_main_queue()) { [unowned self] in
            if self.rowCount > 0 {
                self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: self.rowCount, inSection: 0), atScrollPosition: .Bottom, animated: false)
            }

            UIView.animateWithDuration(0.1) {
                self.tableView.alpha = 1
                self.lastMessageShown = true // Do it once
            }
        }
    }
}

2

Hızlı 3'te işlev alta kaydır

 override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(false)
        //scroll down
        if lists.count > 2 {
            let numberOfSections = self.tableView.numberOfSections
            let numberOfRows = self.tableView.numberOfRows(inSection: numberOfSections-1)
            let indexPath = IndexPath(row: numberOfRows-1 , section: numberOfSections-1)
            self.tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.middle, animated: true)
        }
    }

2
func scrollToBottom() {

    let sections = self.chatTableView.numberOfSections

    if sections > 0 {

        let rows = self.chatTableView.numberOfRows(inSection: sections - 1)

        let last = IndexPath(row: rows - 1, section: sections - 1)

        DispatchQueue.main.async {

            self.chatTableView.scrollToRow(at: last, at: .bottom, animated: false)
        }
    }
}

eklemelisin

DispatchQueue.main.async {
            self.chatTableView.scrollToRow(at: last, at: .bottom, animated: false)
        }

yoksa aşağıya kaydırılmaz.


2

Tabloyu kaydırmak için bu basit kodu kullanın

NSInteger rows = [tableName numberOfRowsInSection:0];
if(rows > 0) {
    [tableName scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:rows-1 inSection:0]
                     atScrollPosition:UITableViewScrollPositionBottom
                             animated:YES];
}

1
Bu OP'nin zaten denediğini söyledi. Bu yanıt, OP'nin viewWillAppear'da düzgün çalışmasını nasıl sağlayacağı sorusunu ele almaz.
jk7

2

Cevabınız için teşekkürler Jacob. monotouch c # sürümü ile ilginç kimse gerçekten yararlı

private void SetScrollPositionDown() {
    if (tblShoppingListItem.ContentSize.Height > tblShoppingListItem.Frame.Size.Height) {
        PointF offset = new PointF(0, tblShoppingListItem.ContentSize.Height - tblShoppingListItem.Frame.Size.Height);
        tblShoppingListItem.SetContentOffset(offset,true );
    }
}

1

İOS'ta bu benim için iyi çalıştı

CGFloat height = self.inputTableView.contentSize.height;
if (height > CGRectGetHeight(self.inputTableView.frame)) {
    height -= (CGRectGetHeight(self.inputTableView.frame) - CGRectGetHeight(self.navigationController.navigationBar.frame));
}
else {
    height = 0;
}
[self.inputTableView setContentOffset:CGPointMake(0, height) animated:animated];

Denilmesi gerekiyor viewDidLayoutSubviews


1

[self.tableViewInfo scrollRectToVisible: CGRectMake (0, self.tableViewInfo.contentSize.height-self.tableViewInfo.height, self.tableViewInfo.width, self.tableViewInfo.height) canlandırıldı: YES];


1

Kabul edilen cevap masamla çalışmadı (binlerce satır, dinamik yükleme), ancak aşağıdaki kod çalışıyor:

- (void)scrollToBottom:(id)sender {
    if ([self.sections count] > 0) {
        NSInteger idx = [self.sections count] - 1;
        CGRect sectionRect = [self.tableView rectForSection:idx];
        sectionRect.size.height = self.tableView.frame.size.height;
        [self.tableView scrollRectToVisible:sectionRect animated:NO];
    }
}

1

Herhangi bir kaydırmaya gerek yok, sadece bu kodu kullanarak yapabilirsiniz:

[YOURTABLEVIEWNAME setContentOffset:CGPointMake(0, CGFLOAT_MAX)];

1

Tablo görünümü için çerçeveyi programlı olarak ayarlıyorsanız, çerçeveyi doğru ayarladığınızdan emin olun.


0

Swift'te sadece ihtiyacın var

self.tableView.scrollToNearestSelectedRowAtScrollPosition(UITableViewScrollPosition.Bottom, animated: true)

otomatik olarak arkaya kaydırmak için


0

Hızlı bir şekilde 3.0 Herhangi bir tablo görünümü Hücre gitmek istiyorsanız Hücre dizinini değiştirmek Değişim "self.yourArr.count" değeri gibi değiştirin.

self.yourTable.reloadData()
self.scrollToBottom() 
func scrollToBottom(){
    DispatchQueue.global(qos: .background).async {
        let indexPath = IndexPath(row: self.yourArr.count-1, section: 0)
        self.tblComment.scrollToRow(at: indexPath, at: .bottom, animated: true)
    }
}

0

Eski çözümlerin swift3 ile çalışmadığına inanıyorum.

Tablodaki sayı satırlarını biliyorsanız şunları kullanabilirsiniz:

tableView.scrollToRow(
    at: IndexPath(item: listCountInSection-1, section: sectionCount - 1 ), 
    at: .top, 
    animated: true)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.