我试图滚动到UITableView的底部后,它完成执行[自我。tableView reloadData]。

我原本有

 [self.tableView reloadData]
 NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1) inSection: ([self.tableView numberOfSections]-1)];

[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

但后来我读到reloadData是异步的,所以自self以来滚动就没有发生。表视图,自我。tableView numberOfSections和[self.]tableView numberOfRowsinSection都是0。

很奇怪,我用的是:

[self.tableView reloadData];
NSLog(@"Number of Sections %d", [self.tableView numberOfSections]);
NSLog(@"Number of Rows %d", [self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1);

在控制台中,它返回Sections = 1, Row = -1;

当我在cellForRowAtIndexPath做完全相同的nslog时,我得到Sections = 1和Row = 8;(8是正确的)


当前回答

和一个UICollectionView版本,基于kolaworld的答案。

它需要测试。到目前为止,它在iOS 9.2和Xcode 9.2 beta 2上工作,将collectionView滚动到一个索引,作为一个闭包。

extension UICollectionView
{
    /// Calls reloadsData() on self, and ensures that the given closure is
    /// called after reloadData() has been completed.
    ///
    /// Discussion: reloadData() appears to be asynchronous. i.e. the
    /// reloading actually happens during the next layout pass. So, doing
    /// things like scrolling the collectionView immediately after a
    /// call to reloadData() can cause trouble.
    ///
    /// This method uses CATransaction to schedule the closure.

    func reloadDataThenPerform(_ closure: @escaping (() -> Void))
    {
        CATransaction.begin()
            CATransaction.setCompletionBlock(closure)
            self.reloadData()
        CATransaction.commit()
    }
}

用法:

myCollectionView.reloadDataThenPerform {
    myCollectionView.scrollToItem(at: indexPath,
            at: .centeredVertically,
            animated: true)
}

其他回答

试试这个:

tableView.backgroundColor = .black

tableView.reloadData()

DispatchQueue.main.async(execute: {

    tableView.backgroundColor = .green

})

只有在reloadData()函数完成后,tableView的颜色才会从黑色变成绿色。

创建CATransaction的可重用扩展:

public extension CATransaction {
    static func perform(method: () -> Void, completion: @escaping () -> Void) {
        begin()
        setCompletionBlock {
            completion()
        }
        method()
        commit()
    }
}

现在创建一个UITableView的扩展,使用CATransaction的扩展方法:

public extension UITableView {
    func reloadData(completion: @escaping (() -> Void)) {
       CATransaction.perform(method: {
           reloadData()
       }, completion: completion)
    }
}

用法:

tableView.reloadData(completion: {
    //Do the stuff
})

从Xcode 8.2.1, iOS 10和Swift 3开始,

你可以通过使用CATransaction块轻松地确定tableView.reloadData()的结束:

CATransaction.begin()
CATransaction.setCompletionBlock({
    print("reload completed")
    // Your completion code here
})
print("reloading")
tableView.reloadData()
CATransaction.commit()

上述方法也适用于确定UICollectionView的reloadData()和UIPickerView的reloadAllComponents()的结束。

当[tableView reloadData]返回时,tableView背后的内部数据结构已经被更新。因此,当方法完成时,您可以安全地滚动到底部。我在自己的应用程序中验证了这一点。rob mayoff的答案被广泛接受,尽管术语也令人困惑,但他在最近的更新中承认了这一点。

如果你的tableView没有滚动到底部,你可能在你没有发布的其他代码中有问题。也许在滚动完成后,您正在更改数据,并且没有重新加载和/或滚动到底部?

添加如下的日志记录,以验证reloadData之后的表数据是否正确。我在一个示例应用程序中有以下代码,它可以完美地工作。

// Change the data source

NSLog(@"Before reload / sections = %d, last row = %d",
      [self.tableView numberOfSections],
      [self.tableView numberOfRowsInSection:[self.tableView numberOfSections]-1]);

[self.tableView reloadData];

NSLog(@"After reload / sections = %d, last row = %d",
      [self.tableView numberOfSections],
      [self.tableView numberOfRowsInSection:[self.tableView numberOfSections]-1]);

[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.tableView numberOfRowsInSection:[self.tableView numberOfSections]-1]-1
                                                          inSection:[self.tableView numberOfSections] - 1]
                      atScrollPosition:UITableViewScrollPositionBottom
                              animated:YES];

我最终使用了Shawn解决方案的一个变种:

创建一个带有委托的自定义UITableView类:

protocol CustomTableViewDelegate {
    func CustomTableViewDidLayoutSubviews()
}

class CustomTableView: UITableView {

    var customDelegate: CustomTableViewDelegate?

    override func layoutSubviews() {
        super.layoutSubviews()
        self.customDelegate?.CustomTableViewDidLayoutSubviews()
    }
}

然后在我的代码中,我使用

class SomeClass: UIViewController, CustomTableViewDelegate {

    @IBOutlet weak var myTableView: CustomTableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.myTableView.customDelegate = self
    }

    func CustomTableViewDidLayoutSubviews() {
        print("didlayoutsubviews")
        // Do other cool things here!!
    }
}

同时确保在Interface Builder中将表视图设置为CustomTableView: