我试图滚动到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是正确的)


当前回答

我最终使用了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:

其他回答

你可以使用它在重载数据后做一些事情:

[UIView animateWithDuration:0 animations:^{
    [self.contentTableView reloadData];
} completion:^(BOOL finished) {
    _isUnderwritingUpdate = NO;
}];

从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()的结束。

迅速:

extension UITableView {
    func reloadData(completion:@escaping ()->()) {
        UIView.animate(withDuration: 0, animations: reloadData)
            { _ in completion() }
    } 
}

// ...somewhere later...

tableView.reloadData {
    print("done")
}

objective - c:

[UIView animateWithDuration:0 animations:^{
    [myTableView reloadData];
} completion:^(BOOL finished) {
    //Do something after that...
}];

只是提供另一种方法,基于补全是发送到cellForRow的“最后可见”单元格的思想。

// Will be set when reload is called
var lastIndexPathToDisplay: IndexPath?

typealias ReloadCompletion = ()->Void

var reloadCompletion: ReloadCompletion?

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    // Setup cell

    if indexPath == self.lastIndexPathToDisplay {

        self.lastIndexPathToDisplay = nil

        self.reloadCompletion?()
        self.reloadCompletion = nil
    }

    // Return cell
...

func reloadData(completion: @escaping ReloadCompletion) {

    self.reloadCompletion = completion

    self.mainTable.reloadData()

    self.lastIndexPathToDisplay = self.mainTable.indexPathsForVisibleRows?.last
}

一个可能的问题是:如果reloadData()在lastIndexPathToDisplay被设置之前已经完成,“最后可见”单元格将在lastIndexPathToDisplay被设置之前显示,补全将不会被调用(并且将处于“等待”状态):

self.mainTable.reloadData()

// cellForRowAt could be finished here, before setting `lastIndexPathToDisplay`

self.lastIndexPathToDisplay = self.mainTable.indexPathsForVisibleRows?.last

如果反转,可能会在reloadData()之前通过滚动触发补全。

self.lastIndexPathToDisplay = self.mainTable.indexPathsForVisibleRows?.last

// cellForRowAt could trigger the completion by scrolling here since we arm 'lastIndexPathToDisplay' before 'reloadData()'

self.mainTable.reloadData()

如果你在viewDidLoad时重新加载数据,你可以把你的代码放到viewDidLayoutSubviews方法中。但是你应该小心viewDidLayoutSubviews可能会被多次调用。