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


当前回答

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

其他回答

我和泰勒·谢弗有同样的问题。

我在Swift中实现了他的解决方案,它解决了我的问题。

斯威夫特3.0:

final class UITableViewWithReloadCompletion: UITableView {
  private var reloadDataCompletionBlock: (() -> Void)?

  override func layoutSubviews() {
    super.layoutSubviews()

    reloadDataCompletionBlock?()
    reloadDataCompletionBlock = nil
  }


  func reloadDataWithCompletion(completion: @escaping () -> Void) {
    reloadDataCompletionBlock = completion
    self.reloadData()
  }
}

斯威夫特2:

class UITableViewWithReloadCompletion: UITableView {

  var reloadDataCompletionBlock: (() -> Void)?

  override func layoutSubviews() {
    super.layoutSubviews()

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

  func reloadDataWithCompletion(completion:() -> Void) {
      reloadDataCompletionBlock = completion
      self.reloadData()
  }
}

使用示例:

tableView.reloadDataWithCompletion() {
 // reloadData is guaranteed to have completed
}

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

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

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

和一个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)
}

上面的dispatch_async(dispatch_get_main_queue())方法不能保证有效。我看到了它的非确定性行为,其中有时系统在完成块之前完成了layoutSubviews和单元格渲染,有时在完成块之后。

这里有一个解决方案,100%适用于我,在iOS 10上。它需要实例化UITableView或UICollectionView作为自定义子类的能力。这是UICollectionView的解决方案,但它对UITableView是完全一样的:

CustomCollectionView.h:

#import <UIKit/UIKit.h>

@interface CustomCollectionView: UICollectionView

- (void)reloadDataWithCompletion:(void (^)(void))completionBlock;

@end

CustomCollectionView.m:

#import "CustomCollectionView.h"

@interface CustomCollectionView ()

@property (nonatomic, copy) void (^reloadDataCompletionBlock)(void);

@end

@implementation CustomCollectionView

- (void)reloadDataWithCompletion:(void (^)(void))completionBlock
{
    self.reloadDataCompletionBlock = completionBlock;
    [self reloadData];
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    if (self.reloadDataCompletionBlock) {
        self.reloadDataCompletionBlock();
        self.reloadDataCompletionBlock = nil;
    }
}

@end

使用示例:

[self.collectionView reloadDataWithCompletion:^{
    // reloadData is guaranteed to have completed
}];

请看这个答案的Swift版本