UITableView에서 데이터 재로드가 완료되었는지 확인하는 방법

'[self.tableView reloadData]'를 수행한 후 UITableView의 맨 아래로 스크롤하려고 합니다.

나는 원래 가지고 있었다.

 [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", "self.tableViewnumberOfSections", "self.tableViewnumberOfSections"가 모두 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);

콘솔에서 섹션 = 1, 행 = -1을 반환합니다.

'cell For RowAt'에서 동일한 NSLogs를 수행하는 경우IndexPath' I get 섹션 = 1 및 행 = 8; (8이 맞다)

질문에 대한 의견 (4)
해결책

다시 로드 작업은 다음 레이아웃 통과 중에 수행되며, 일반적으로 실행 루프에 제어권을 반환할 때 발생합니다(예: 버튼 작업 또는 반환 후).

따라서 테이블 뷰가 다시 로드된 후 실행할 수 있는 한 가지 방법은 테이블 뷰가 레이아웃을 즉시 수행하도록 강제하는 것입니다.

[self.tableView reloadData];
[self.tableView layoutIfNeeded];
 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];

또 다른 방법은 'dispatch_async'를 사용하여 나중에 실행되도록 레이아웃 코드를 예약하는 것입니다.

[self.tableView reloadData];

dispatch_async(dispatch_get_main_queue(), ^{
     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'에서 돌아오기 전에 데이터 소스로 'tableView:numberOfSections:' 및 'tableView:numberOfRowsInSection:'을 전송하고 있습니다. 딜러가 'tableView:heightForRowAt'를 구현하는 경우IndexPath: 테이블 뷰는 또한 'reloadData'에서 반환하기 전에 (각 행에 대해) 이 메시지를 보냅니다.

그러나 테이블 보기는 'tableView:cellForRowAt'를 전송하지 않습니다.IndexPath:' 또는 'tableView:headerViewForSection'은 레이아웃 단계까지이며, 기본적으로 실행 루프에 제어권을 반환할 때 발생합니다.

또한 작은 테스트 프로그램에서 질문의 코드가 테이블 보기의 맨 아래로 올바르게 스크롤됩니다. ***는 특별한 작업(예: 'layoutIfNeeded' 전송 또는 'dispatch_async' 사용)을 수행하지 않아도 됩니다.

해설 (16)

전환:

extension UITableView {
    func reloadData(completion: ()->()) {
        UIView.animateWithDuration(0, animations: { self.reloadData() })
            { _ in completion() }
    }
}

...somewhere later...

tableView.reloadData {
    println("done")
}

목표-C:

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

Xcode vmware. 8.2.1, iOS 및 빠른 3, 10,

'말' 타바레비예w.레로다타 쉽게 확인할 수 있습니다 () 를 사용하여 카트란스카션 요소:

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

39 위) 도 끝 결정하기 위한 것이고, s, s # 39 UICollectionView& 레로이 카다타 () 와 UIPickerView& 레오들콤프로넨츠 ().

해설 (3)

Dispatch_async (dispatch_get_main_queue ()) '이' 위에 메서드로부터 작동합니까 보장할 수 없습니다. # 39 m, 이를 통해 볼 수 있는 비결정론적 비헤이비어를 i&, 때때로 시스템 및 셀 블록, 때로는 렌더링합니다 완료 전에 레이우쓰브리우스 완료한 후.

39 의 100%, iOS 에서 사용할 수 있는 솔루션을 here& 가져다줄래요 10. 수 있는 맞춤형 하위 클래스 또는 위콜레스티오노비예프 브리타 레비예프 인스턴스화하지 필요합니다. 하지만, s, s # 39 here& 위콜레스티오노비예프 솔루션이므로 it& # 39 의 똑같은 브리타 레비예프:

쿠스토무스콜레스티온비 윌리엄스:

#import 

@interface CustomCollectionView: UICollectionView

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

@end

쿠스토무스콜레스티온비우스마:

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

[Here] 참조 (https://stackoverflow.com/a/39798079) 이 질문에 대한 조속한 버전니다.

해설 (7)

타일러 퓰러 같은 문제를 갖고 있다.

난 내 문제를 해결할 것을 구축됩니까 자신의 해결책을 에서 및 정보기술 (it).

  • 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
}
해설 (4)

사람들은 여전히 이 질문과 답을 읽고 있는 것 같다. 그 후, 저는 이것과 정말 상관없는 동기라는 단어를 삭제하기 위해 제 답변을 편집하고 있습니다.

"[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];
해설 (11)

39 의 버전, '와' 위콜레스티오노비예프 kolaworld& 따라 대답:

https://stackoverflow.com/a/43162226/1452758

테스트 합니다. Xcode 에서 iOS 9.2cm, 베타 2 는 지금까지 작동됨 9.2cm 콜레스티오노비예프 스크롤하지 함께 충족되었으며 인덱스화하여 비호환성의 폐쇄.

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()
    }
}
  • Usage:*
myCollectionView.reloadDataThenPerform {
    myCollectionView.scrollToItem(at: indexPath,
            at: .centeredVertically,
            animated: true)
}
해설 (0)

내가 사용하는 트릭, 이미 이 게시하기를 redhat. 복제본입니다 이 질문은 꽤 확신한다.

-(void)tableViewDidLoadRows:(UITableView *)tableView{
    // do something after loading, e.g. select a cell.
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // trick to detect when table view has finished loading.
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(tableViewDidLoadRows:) object:tableView];
    [self performSelector:@selector(tableViewDidLoadRows:) withObject:tableView afterDelay:0];

    // specific to your controller
    return self.objects.count;
}
해설 (2)

실제로 이 번호요 이 문제를 해결했다.

-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

NSSet *visibleSections = [NSSet setWithArray:[[tableView indexPathsForVisibleRows] valueForKey:@"section"]];
if (visibleSections) {
    // hide the activityIndicator/Loader
}}
해설 (0)

이 방식으로 작동합니다 시도하시겠습니까

[tblViewTerms performSelectorOnMainThread:@selector(dataLoadDoneWithLastTermIndex:) withObject:lastTermIndex waitUntilDone:YES];waitUntilDone:YES];

@interface UITableView (TableViewCompletion)

-(void)dataLoadDoneWithLastTermIndex:(NSNumber*)lastTermIndex;

@end

@implementation UITableView(TableViewCompletion)

-(void)dataLoadDoneWithLastTermIndex:(NSNumber*)lastTermIndex
{
    NSLog(@"dataLoadDone");

NSIndexPath* indexPath = [NSIndexPath indexPathForRow: [lastTermIndex integerValue] inSection: 0];

[self selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];

}
@end

내아기마저도 autoexec. 때 완전히 로드되었는지 테이블

하위 브리타 레비예프 다른 솔루션을 사용할 수 있습니다.

해설 (0)

<! - 모든 언어: 스위프트 - &gt.

39 의 변형 Shawn& 됐지 내가 사용하는 솔루션:

사용자 정의할 수 있는 브리타 레비예프 클래스용 위임합니다:

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

또한 볼 수 있는 테이블을 설정한 smartupdate 쿠스토무스타바레비예프 인터페이스 빌더 ():

! [입력하십시오. 이미지 여기에 설명을] [1]

해설 (2)
  • 빠른 3.0 에서 +* 작성할 수 있습니다 "에 대한 폐쇄 등 '와' 확장명으로 브리타 레비예프 이스케이프된 ' (아래 참조).
extension UITableView {
    func reloadData(completion: @escaping () -> ()) {
        UIView.animate(withDuration: 0, animations: { self.reloadData()})
        {_ in completion() }
    }
}
  • It like 아래에 있는 판매업체에서 운영까지도 :* 및 사용
Your_Table_View.reloadData {
   print("reload done")
 }
  • ᄋ "이번 누군가에게 도움이 될 것입니다. 건배 ~ ~ *
해설 (1)

# 세부내용에서

  • 5 (10E1001), Swift Xcode 버전 10.2.1

# 솔루션이므로

import UIKit

// MARK: - UITableView reloading functions

protocol ReloadCompletable: class { func reloadData() }

extension ReloadCompletable {
    func run(transaction closure: (() -> Void)?, completion: (() -> Void)?) {
        guard let closure = closure else { return }
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)
        closure()
        CATransaction.commit()
    }

    func run(transaction closure: (() -> Void)?, completion: ((Self) -> Void)?) {
        run(transaction: closure) { [weak self] in
            guard let self = self else { return }
            completion?(self)
        }
    }

    func reloadData(completion closure: ((Self) -> Void)?) {
        run(transaction: { [weak self] in self?.reloadData() }, completion: closure)
    }
}

// MARK: - UITableView reloading functions

extension ReloadCompletable where Self: UITableView {
    func reloadRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation, completion closure: ((Self) -> Void)?) {
        run(transaction: { [weak self] in self?.reloadRows(at: indexPaths, with: animation) }, completion: closure)
    }

    func reloadSections(_ sections: IndexSet, with animation: UITableView.RowAnimation, completion closure: ((Self) -> Void)?) {
        run(transaction: { [weak self] in self?.reloadSections(sections, with: animation) }, completion: closure)
    }
}

// MARK: - UICollectionView reloading functions

extension ReloadCompletable where Self: UICollectionView {

    func reloadSections(_ sections: IndexSet, completion closure: ((Self) -> Void)?) {
        run(transaction: { [weak self] in self?.reloadSections(sections) }, completion: closure)
    }

    func reloadItems(at indexPaths: [IndexPath], completion closure: ((Self) -> Void)?) {
        run(transaction: { [weak self] in self?.reloadItems(at: indexPaths) }, completion: closure)
    }
}

# 사용

&gt. 브리타 레비예프

// Activate
extension UITableView: ReloadCompletable { }

// ......
let tableView = UICollectionView()

// reload data
tableView.reloadData { tableView in print(collectionView) }

// or
tableView.reloadRows(at: indexPathsToReload, with: rowAnimation) { tableView in print(tableView) }

// or
tableView.reloadSections(IndexSet(integer: 0), with: rowAnimation) { _tableView in print(tableView) }

&gt. 위콜레스티오노비예프

// Activate
extension UICollectionView: ReloadCompletable { }

// ......
let collectionView = UICollectionView()

// reload data
collectionView.reloadData { collectionView in print(collectionView) }

// or
collectionView.reloadItems(at: indexPathsToReload) { collectionView in print(collectionView) }

// or
collectionView.reloadSections(IndexSet(integer: 0)) { collectionView in print(collectionView) }

# 전체 샘플링합니다

&gt. 여기에 빼놓을 수 없는 이 솔루션을 추가 코드를

import UIKit

class ViewController: UIViewController {

    private weak var navigationBar: UINavigationBar?
    private weak var tableView: UITableView?

    override func viewDidLoad() {
        super.viewDidLoad()
        setupNavigationItem()
        setupTableView()
    }
}
// MARK: - Activate UITableView reloadData with completion functions

extension UITableView: ReloadCompletable { }

// MARK: - Setup(init) subviews

extension ViewController {

    private func setupTableView() {
        guard let navigationBar = navigationBar else { return }
        let tableView = UITableView()
        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.topAnchor.constraint(equalTo: navigationBar.bottomAnchor).isActive = true
        tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        tableView.dataSource = self
        self.tableView = tableView
    }

    private func setupNavigationItem() {
        let navigationBar = UINavigationBar()
        view.addSubview(navigationBar)
        self.navigationBar = navigationBar
        navigationBar.translatesAutoresizingMaskIntoConstraints = false
        navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        navigationBar.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
        navigationBar.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
        let navigationItem = UINavigationItem()
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "all", style: .plain, target: self, action: #selector(reloadAllCellsButtonTouchedUpInside(source:)))
        let buttons: [UIBarButtonItem] = [
                                            .init(title: "row", style: .plain, target: self,
                                                  action: #selector(reloadRowButtonTouchedUpInside(source:))),
                                            .init(title: "section", style: .plain, target: self,
                                                  action: #selector(reloadSectionButtonTouchedUpInside(source:)))
                                            ]
        navigationItem.leftBarButtonItems = buttons
        navigationBar.items = [navigationItem]
    }
}

// MARK: - Buttons actions

extension ViewController {

    @objc func reloadAllCellsButtonTouchedUpInside(source: UIBarButtonItem) {
        let elementsName = "Data"
        print("-- Reloading \(elementsName) started")
        tableView?.reloadData { taleView in
            print("-- Reloading \(elementsName) stopped \(taleView)")
        }
    }

    private var randomRowAnimation: UITableView.RowAnimation {
        return UITableView.RowAnimation(rawValue: (0...6).randomElement() ?? 0) ?? UITableView.RowAnimation.automatic
    }

    @objc func reloadRowButtonTouchedUpInside(source: UIBarButtonItem) {
        guard let tableView = tableView else { return }
        let elementsName = "Rows"
        print("-- Reloading \(elementsName) started")
        let indexPathToReload = tableView.indexPathsForVisibleRows?.randomElement() ?? IndexPath(row: 0, section: 0)
        tableView.reloadRows(at: [indexPathToReload], with: randomRowAnimation) { _tableView in
            //print("-- \(taleView)")
            print("-- Reloading \(elementsName) stopped in \(_tableView)")
        }
    }

    @objc func reloadSectionButtonTouchedUpInside(source: UIBarButtonItem) {
        guard let tableView = tableView else { return }
        let elementsName = "Sections"
        print("-- Reloading \(elementsName) started")
        tableView.reloadSections(IndexSet(integer: 0), with: randomRowAnimation) { _tableView in
            //print("-- \(taleView)")
            print("-- Reloading \(elementsName) stopped in \(_tableView)")
        }
    }
}

extension ViewController: UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int { return 1 }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 20 }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        cell.textLabel?.text = "\(Date())"
        return cell
    }
}

# 결과

! [입력하십시오. 이미지 여기에 설명을] [1]

해설 (0)

그냥 다른 외곽진입 제공할 수 있다고 & # 39 의 개념을 토대로 완료, 마지막 visible& # 39. 보낼 셀프로 '셀'.

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

가능한 한 가지 문제는: 만약 '레로이 카다타 ()' 이 ',' & # 39, 마지막 전에 끝난 라스티네스파스토디스플라이 설정되었습니다 visible& # 39. '셀' 설정되었습니다 및 완료 전에 라스티네스파스토디스플라이 표시할지를 않을 (# 39, & # 39 에 있을 것이며, waiting& 불렀으매 스테이드):

self.mainTable.reloadData()

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

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

만약 우리가 역동기화, 우리 함께 기술입니까 스크롤하지 의해 트리거됨 레로이 카다타 다니엘을 완료율을 전에 ' ()'.

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

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

self.mainTable.reloadData()
해설 (0)

이거 드세요.

이블랙 타바레비예w.백그로운콜러 =

타바레비예w.레로다타 ()

디스파치케위스망스라인스 (실행: {

tableView.backgroundColor = .green

})

그린 컬러 // 이 타바레비예프 거역한다면 변경일 from black 후에만 레로이 카다타 () 함수를 완료.

해설 (0)

뭔가 할 수 있도록 리로드하려면 후 데이터를 사용할 수 있습니다.

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

설정값입니다 시도하시겠습니까 지연:

[_tableView performSelector:@selector(reloadData) withObject:nil afterDelay:0.2];
[_activityIndicator performSelector:@selector(stopAnimating) withObject:nil afterDelay:0.2];
해설 (1)