UITableView가 수행 된 후 맨 아래로 스크롤하려고합니다. [self.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
, [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을 반환합니다.
정확히 동일한 NSLog를 수행하면 cellForRowAtIndexPath
Sections = 1 및 Row = 8이 표시됩니다. (8이 맞다)
답변
재로드는 다음 레이아웃 패스 중에 발생하며 일반적으로 제어를 실행 루프로 되돌릴 때 발생합니다 (예를 들어, 단추 동작 또는 모든 반환).
따라서 테이블 뷰를 다시로드 한 후 무언가를 실행하는 한 가지 방법은 테이블 뷰가 즉시 레이아웃을 수행하도록하는 것입니다.
[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];
});
최신 정보
추가 조사 후, 나는 테이블 뷰 보내는 것을 발견 tableView:numberOfSections:
하고 tableView:numberOfRowsInSection:
부터 반환하기 전에 데이터 소스에 reloadData
. 대리자가을 구현 tableView:heightForRowAtIndexPath:
하면 테이블 뷰는에서 반환하기 전에 (각 행에 대해)도 보냅니다 reloadData
.
그러나 테이블보기는 tableView:cellForRowAtIndexPath:
또는 tableView:headerViewForSection
레이아웃 단계를 보내지 않습니다.이 단계는 제어를 실행 루프로 되돌릴 때 기본적으로 발생합니다.
또한 작은 테스트 프로그램에서 질문의 코드가 (보기 또는 보내기와 같은) 특별한 작업을 수행 하지 않고 테이블보기의 맨 아래로 올바르게 스크롤됩니다 .layoutIfNeeded
dispatch_async
답변
빠른:
extension UITableView {
func reloadData(completion:@escaping ()->()) {
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...
}];
답변
Xcode 8.2.1, iOS 10 및 swift 3부터
tableView.reloadData()
CATransaction 블록을 사용하여 쉽게 끝을 결정할 수 있습니다 .
CATransaction.begin()
CATransaction.setCompletionBlock({
print("reload completed")
//Your completion code here
})
print("reloading")
tableView.reloadData()
CATransaction.commit()
위의 내용은 UICollectionView의 reloadData () 및 UIPickerView의 reloadAllComponents ()의 끝을 결정하는 데에도 사용됩니다.
답변
dispatch_async(dispatch_get_main_queue())
위 의 방법은 작동하지 않을 수 있습니다 . 때로는 시스템이 완료 블록 전에, 때로는 렌더링 후 layoutSubviews 및 셀 렌더링을 완료 한 비 결정적 동작을보고 있습니다.
다음은 iOS 10에서 100 % 작동하는 솔루션입니다. 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에서 구현 했으며 내 문제를 해결했습니다.
스위프트 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
}
답변
그리고 UICollectionView
kolaworld의 답변을 기반으로 한 버전 :
https://stackoverflow.com/a/43162226/1452758
테스트가 필요합니다. iOS 9.2, Xcode 9.2 베타 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)
}
답변
사람들은 여전히이 질문과 답변을 읽고있는 것 같습니다. 그것의 B / C, 나는 실제로 이것과 관련이없는 Synchronous 라는 단어를 제거하기 위해 대답을 편집하고 있습니다.
When [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];