web-dev-qa-db-ja.com

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

可変高のセルが配置されたUITableViewがあります。ビューがビューにプッシュされたときに、テーブルが下にスクロールするようにします。

私は現在、次の機能を持っています

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

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

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

viewWillAppearviewDidLoadでスクロールを試みましたが、どちらの場合もデータはまだテーブルにロードされておらず、両方とも例外をスローします。

どんなガイダンスでも大丈夫です、たとえそれが私が持っているものを私に伝えることだけが可能であるという場合であっても。

129
acqu13sce

[table setContentOffset:CGPointMake(0, CGFLOAT_MAX)]を呼び出すことはあなたが望むことをすると信じています。

146
Jacob Relkin

Jacob's answer から、これはコードです:

- (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];
    }
}
121
Osama F Elias

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

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

コンテンツの最後までスクロールする必要がある場合は、次のようにします。

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

私は自動レイアウトを使用していますが、答えはどれもうまくいきませんでした。これが最終的に機能した私のソリューションです:

@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)];
    }
}
31
RaffAl

@ JacobRelkinが受け入れた解決策 は、Auto Layoutを使用したiOS 7.0では機能しませんでした。

UIViewControllerのカスタムサブクラスがあり、viewのサブビューとしてインスタンス変数_tableViewを追加しました。自動レイアウトを使用して_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];
    }
}

viewDidLoadの最後で[self tableViewScrollToBottomAnimated:NO]を呼び出すと機能します。残念ながら、それはまた、tableView:heightForRowAtIndexPath:がすべてのセルに対して3回呼び出される原因にもなります。

23
ma11hew28

以下は、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)
        }
    }
}
22
Ryan Herubin

詳細

  • 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
    }
}
16

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

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

私にぴったり。

14
XcodeNOOB

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

フレームに表示されているテーブルの最後にテーブルをロードしたかったのです。を使用して見つけた

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に設定されます。

9
T.J.

代わりにUITableViewScrollPositionBottomを使用する必要があります。

5
Erphan Rajput

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)
}
5
Irshad Qureshi

もちろんバグです。おそらくコードのどこかで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
Mohsen Karbassi

多くのいじりをした後、これは私のために働いたものです:

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

キーはnotであることが判明しました。一部の人が示唆しているようにdispatch_asyncの内側にscrollToRowAtIndexPathをラップしていますが、それに続いてlayoutIfNeededへの呼び出しが続きます。

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

(また、NB viewHasAppearedが必要なのは、goToBottomが呼び出されるたびにviewDidLayoutSubviewsが必要ないためです。たとえば、向きが変わるたびに呼び出されます。 )

3
skot

Swift 3.0で

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

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

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

答えてくれたジェイコブに感謝します。モノタッチ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 );
    }
}
2
Mahesh

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

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
SoftDesigner

オン機能Swift一番下までスクロール

 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
Tarik
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
user515060

このシンプルなコードを使用してtableViewの下部をスクロールします

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

テーブルビューのフレームをプログラムで設定している場合は、フレームを正しく設定していることを確認してください。

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
Josip B.

次のコードを使用すると、スクロールする必要はありません。

[YOURTABLEVIEWNAME setContentOffset:CGPointMake(0, CGFLOAT_MAX)];
1
Anuj Kumar Rai

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

1
leetvin

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

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

Swiftでは、必要なのは

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

自動的に底までスクロールする

0
Even Cheng

Swift 3.0の場合テーブルビューの特定のセルに移動する場合セルインデックスの値を変更します(「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
Amit Verma

次のような別の良い方法を見つけました。

func yourFunc() {
    //tableView.reloadData()
    self.perform(#selector(scrollToBottom), with: nil, afterDelay: 0.1)
}

@objc func scrollToBottom() {
    self.tableView.scrollToRow(at: IndexPath(row: 0, section: self.mesArr.count - 1), at: .bottom, animated: false)
}
0
RateRebriduo

TableViewの下部をスクロールする最も安全な方法は、「tableView.scrollRectToVisible」を使用することです。 tableView.reloadData()を呼び出した後に使用します。 In Swift 5

private func scrollAtTheBottom() {
    let lastIndexPath = IndexPath(row: state.myItemsArray.count - 1, section: 0)
    let lastCellPosition = tableView.rectForRow(at: lastIndexPath)
    tableView.scrollRectToVisible(lastCellPosition, animated: true)
}
0
Doca

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

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

tableView.scrollToRow(
    at: IndexPath(item: listCountInSection-1, section: sectionCount - 1 ), 
    at: .top, 
    animated: true)
0
ymutlu

IOS 12では、受け入れられた答えは壊れているようです。私はそれを次の拡張機能で動作させました



import UIKit

extension UITableView {
    public func scrollToBottom(animated: Bool = true) {
        guard let dataSource = dataSource else {
            return
        }

        let sections = dataSource.numberOfSections?(in: self) ?? 1
        let rows = dataSource.tableView(self, numberOfRowsInSection: sections-1)
        let bottomIndex = IndexPath(item: rows - 1, section: sections - 1)

        scrollToRow(at: bottomIndex,
                    at: .bottom,
                    animated: animated)
    }
}
0
gkaimakas