ビューが表示される前にiPhoneのUITableViewの下部までスクロールする方法


136

私が持っているUITableView変数の高さの細胞に取り込まれています。ビューをビューに押し込んだときに、テーブルを一番下までスクロールしてください。

現在以下の機能を持っています

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

logは、各セルのコンテンツを構成するオブジェクトを含む可変配列です。

上記のコードは正常に機能viewDidAppearしますが、ビューが最初に表示されたときにテーブルの上部が表示され、次に下部にジャンプするという残念な副作用があります。table viewそれが表示される前に下部にスクロールできるのであれば、私はそれを好みます。

私はスクロールを試してみましたviewWillAppearし、viewDidLoad両方のケースではデータを表にロードされ、まだ、例外をスローし、両方されていません。

私が持っているものを私に伝えるだけの場合であっても、どんなガイダンスも大歓迎です。

回答:


148

私は

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

あなたがしたいことをします。


14
ありがとうございます。常に下部が表示されるように、Y値が十分に高いCGPointを作成しました。ビューが読み込まれたら、(self.table.contentSize.height-self.table.frame.size.height)を使用して同じメソッドで一番下に移動できます
acqu13sce

8
セル数やテーブルビューの高さなどを計算する必要がないので、これは完全な答えですが、テーブルビューをリロードする前にこれを呼び出す必要があることを指摘します...この後の書き込み[table reloadData];
Fahim Parkar

4
ios 10-12では機能しません-テーブルが初めて消えるだけ
Vyachaslav Gerchicov

2
または単にスクロールしCGPoint(x: 0, y: tableView.contentSize.height)ますか?
アンバーK

5
うわぁ!!!テーブルを消します:-o。[self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:NO]を使用するのが最適です。
Carl Hine、

122

最も簡単な方法はこれだと思います:

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

Swift 3バージョン:

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

3
セルがUITableViewよりも高い場合は機能しません
Olav Gausaker

3
セルはUITableViewよりも高いですか?そのようなユースケースを聞いたことがない。
チャミラフェルナンド2017

@ChamiraFernandoこれが最も簡単な方法です:)
AITAALI_ABDERRAHMANE 2017

messages.count既に実装されているで置き換えることは意味があるかもしれないことに注意してくださいmyTableView.dataSource!.tableView(myTableView, numberOfRowsInSection: 0)。はい、それはより長いですが、コードの繰り返しを回避するかもしれません。オプションも処理する必要がありますdataSource(このサンプルのように強制的にアンラップしないでください)。
Nikolay Suvandzhiev 2018年

@ChamiraFernando私はこの質問が古いことを知っていますが、あなたが見たことがないからといって、それが起こらないという意味ではありません。あなたの質問に答えるために、Foursquareのようなアプリは、ユーザーがレビューを書くこのような状況を持っているかもしれません。セルの高さは、テーブルビューの高さよりも大きくなっています。その完璧な状況。
Caio

121

ヤコブの答えは、これはコードです:

- (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];
    }
}

iOSの11日あなたは、調整されたテーブルビューフレームの高さを使用する必要があります:UIEdgeInsetsInsetRect(self.messagesTableView.frame, self.messagesTableView.safeAreaInsets).height
スラブ

41

コンテンツの正確な最後までスクロールする必要がある場合は、次のように行うことができます。

- (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];
}

7
自動レイアウトで機能しますが、viewDidLayoutSubviewsからこのメソッドを呼び出すことが重要です
Omaty

これを行う必要がある理由を説明してください yOffset = self.tableView.contentSize.height - self.tableView.bounds.size.height; 。ありがとう。
Unheilig

3
@Unheilig self.tableView.contentSize.heightテーブルビューのコンテンツにスクロールすると、コンテンツの下にスクロールするため、表示されない場合があります。したがって、テーブルビューの末尾の上にある1つの「目に見えるテーブルビューのギャップ」までスクロールする必要があります。
Hans One

31

私はautolayoutを使用していますが、どの回答もうまくいきませんでした。これが最終的に機能した私の解決策です:

@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
これはほとんど問題なく動作しますが、テーブルデータが外部APIからロードされているときに、奇妙なグラフィックグリッチが発生します。私の場合setContentOffset、データがフェッチされてテーブルビューが再ロードされたときに、他の時点で呼び出す必要がありますか?
jmoz 14年

リクエストの完了ハンドラでオフセットを設定してみてください。
RaffAl、2014年

2
これは
iOS

2
CGFLOAT_MAXを使用contentSize.height - frame.height + contentInset.bottomする代わりに、初期コンテンツのオフセットを設定するときに使用しました。使用CGFLOAT_MAXすることは私を台無しにしたようです。
Baza207

23

@JacobRelkinで受け入れソリューション自動レイアウトを使用してのiOS 7.0で私のために動作しませんでした。

のカスタムサブクラスがUIViewControllerあり_tableView、そののサブビューとしてインスタンス変数を追加しましたview。私は位置_tableView自動レイアウトを使用しました。私はこのメソッドを最後にviewDidLoad、さらにはviewWillAppear:。どちらもうまくいきませんでした。

そこで、のカスタムサブクラスに次のメソッドを追加しましたUIViewController

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

仕事[self tableViewScrollToBottomAnimated:NO]の終わりに電話viewDidLoad。残念ながら、これtableView:heightForRowAtIndexPath:はすべてのセルに対して3回呼び出される原因にもなります。


23

これが、Swift 2.0で実装した拡張機能です。これらの関数は、tableviewが読み込まれた後に呼び出す必要があります。

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
これはコンテンツサイズを使用するよりも優れています。Swift3の場合。if self.numberOfRows(inSection:0)> 0 {self.scrollToRow(at:IndexPath.init(row:self.numberOfRows(inSection:0)-1、section:0)、at:.bottom、animated:animated)}
Soohwan Park 2017

scrollToLastRowの場合、このメソッドは完全に機能せず、self.layoutIfNeeded()を追加するだけで完全に機能します。
Yogesh Patel

16

細部

  • Xcode 8.3.2、Swift 3.1
  • Xcode 10.2(10E125)、Swift 5

コード

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

使用法

tableView.scrollToBottom(animated: true)

完全なサンプル

ソリューションコードを貼り付けることを忘れないでください!

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

実際に迅速にそれを行う「Swifter」の方法は次のとおりです。

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

私にぴったりです。


9

フレームに表示されているテーブルの最後でテーブルをロードする必要がありました。私は使用して見つけました

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

テーブルの高さがフレームの高さよりも低いときにエラーが発生したため、機能しませんでした。テーブルにはセクションが1つしかないことに注意してください。

私のために働いた解決策は、viewWillAppearに次のコードを実装することでした:

- (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でTRUEに設定されています。


電話する必要がありscrollToRowAtIndexPathますか?あなたはすでにsetContentOffset後で電話しているので、その最初の電話は無意味になるかもしれません。
Carlos P

9

Swiftの場合:

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)の場合:

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
これはOPの質問には答えません。これは最初から機能していたものです。また、super.viewDidAppearを呼び出す必要があります
streem

4

もちろんバグです。おそらく、コードのどこかを使用しますtable.estimatedRowHeight = value(たとえば100)。この値を、行の高さが得ることができると思われる最高値、たとえば500に置き換えます。これにより、次のコードと組み合わせて問題を解決できるはずです。

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

たくさんいじった後、これは私にとってうまくいきました:

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

一部の人が示唆しているように、キーは内側にラップしていないことがわかりましたが、単にscrollToRowAtIndexPathdispatch_asynclayoutIfNeeded

これについての私の理解は、現在のスレッドでscrollメソッドを呼び出すと、ビューが表示される前に、スクロールオフセットがすぐに設定されることが保証されます。メインスレッドにディスパッチしているとき、スクロールが有効になる前に、ビューが一瞬表示されていました。

(またviewHasAppearedgoToBottom毎回viewDidLayoutSubviews呼び出される必要がないため、NBも必要です。たとえば、方向が変わるたびに呼び出されます。)


3

上記のソリューションを使用すると、これはテーブルの一番下までスクロールします(テーブルのコンテンツが最初に読み込まれた場合のみ)。

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


2

下にスクロールする前に非同期でデータをロードする必要がある場合、考えられる解決策は次のとおりです。

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

Swift 3の機能を下にスクロール

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

追加する必要があります

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

または、下にスクロールしません。


2

この単純なコードを使用して、tableViewを下にスクロールします。

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

1
これは基本的にOPが彼がすでに試みたと言ったことです。この回答は、viewWillAppearでOPを適切に機能させる方法についてのOPの質問には対応していません。
jk7 2016年

2

答えをジェイコブに感謝します。モノタッチc#バージョンに興味のある人がいれば、本当に役に立ちます

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

iOSではこれは私にとってうまくいきました

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

から呼び出す必要があります viewDidLayoutSubviews


1

[self.tableViewInfo scrollRectToVisible:CGRectMake(0、self.tableViewInfo.contentSize.height-self.tableViewInfo.height、self.tableViewInfo.width、self.tableViewInfo.height)animated:YES];


1

受け入れられた回答は私のテーブル(数千行、動的ロード)では機能しませんでしたが、以下のコードは機能します。

- (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

スクロールする必要はなく、次のコードを使用するだけで実行できます。

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


0

Swiftでは、あなたはただ必要です

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

自動的にボタンにスクロールするようにするには


0

swift 3.0でtableviewの特定のセルに移動したい場合は、「self.yourArr.count」のようにセルインデックス値を変更します。

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

古いソリューションはswift3では機能しないと思います。

テーブルの行数がわかっている場合は、次を使用できます。

tableView.scrollToRow(
    at: IndexPath(item: listCountInSection-1, section: sectionCount - 1 ), 
    at: .top, 
    animated: true)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.