로드가 완료되면 테이블의 오프셋을 변경하고 해당 오프셋은 테이블에로드 된 셀 수에 따라 다릅니다.
uitableview 로딩이 완료된 시점을 아는 것이 SDK에 있습니까? 대리인이나 데이터 소스 프로토콜에는 아무것도 보이지 않습니다.
보이는 셀만로드하기 때문에 데이터 소스 수를 사용할 수 없습니다.
로드가 완료되면 테이블의 오프셋을 변경하고 해당 오프셋은 테이블에로드 된 셀 수에 따라 다릅니다.
uitableview 로딩이 완료된 시점을 아는 것이 SDK에 있습니까? 대리인이나 데이터 소스 프로토콜에는 아무것도 보이지 않습니다.
보이는 셀만로드하기 때문에 데이터 소스 수를 사용할 수 없습니다.
답변:
@RichX 답변으로 향상 :
또는 lastRow
모두 가능합니다 . 따라서 코드는 다음과 같습니다.[tableView numberOfRowsInSection: 0] - 1
((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath row] == ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row){
//end of loading
//for example [activityIndicator stopAnimating];
}
}
업데이트 :
글쎄, @htafoya의 의견이 맞습니다. 이 코드가 소스에서 모든 데이터로드의 끝을 감지하도록하려면 그렇지 않지만 원래 질문은 아닙니다. 이 코드는 보이는 모든 셀이 표시되는시기를 감지하기위한 것입니다. willDisplayCell:
보다 부드러운 UI를 위해 여기에 사용됩니다 (단일 셀은 일반적으로 willDisplay:
통화 후 빠르게 표시됨 ). 로 시도해 볼 수도 있습니다 tableView:didEndDisplayingCell:
.
viewForFooterInSection
방법 에 동일하게 적용 할 수 있습니다 .
스위프트 3 & 4 & 5 버전 :
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if let lastVisibleIndexPath = tableView.indexPathsForVisibleRows?.last {
if indexPath == lastVisibleIndexPath {
// do here...
}
}
}
나는 항상이 매우 간단한 솔루션을 사용합니다 :
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath row] == lastRow){
//end of loading
//for example [activityIndicator stopAnimating];
}
}
[tableView numberOfRowsInSection: 0] - 1
입니다. 0
필요한 값으로 교체해야합니다 . 그러나 그것은 문제가 아닙니다. 문제는 UITableView가 표시되는 것만로드한다는 것입니다. 그러나 ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row
문제를 해결합니다.
나를 위해 일하는 것처럼 보이는 또 다른 옵션이 있습니다. viewForFooter 델리게이트 메소드에서 마지막 섹션인지 확인하고 코드를 추가하십시오. 이 방법은 willDisplayCell이 바닥 글이있는 경우 바닥 글을 고려하지 않는다는 것을 인식 한 후에 생각되었습니다.
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
// Perform some final layout updates
if (section == ([tableView numberOfSections] - 1)) {
[self tableViewWillFinishLoading:tableView];
}
// Return nil, or whatever view you were going to return for the footer
return nil;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
// Return 0, or the height for your footer view
return 0.0;
}
- (void)tableViewWillFinishLoading:(UITableView *)tableView
{
NSLog(@"finished loading");
}
이 방법은 UITableView
단순히 보이는 셀이 아니라 전체에 대한 최종로드를 찾고자 할 때 가장 효과적 입니다. 필요에 따라 보이는 셀만 원할 수 있으며,이 경우 folex의 대답 은 좋은 경로입니다.
nil
에서 tableView:viewForFooterInSection:
자동 레이아웃을 사용하여 아이폰 OS 7 내 레이아웃을 엉망.
return CGRectMake(0,0,0,0);
self.tableView.sectionFooterHeight = 0
. 어느 쪽이든, 높이가 약 10 인 섹션 바닥 글 뷰를 삽입하는 것 같습니다. 반환하는 뷰에 0 높이 제약 조건을 추가 하여이 문제를 해결할 수 있습니다. 어쨌든, 실제로 마지막 셀에서 UITableView를 시작하는 방법을 알고 싶었지만 이것을 처음 보았 기 때문에 좋습니다.
스위프트 2 솔루션 :
// willDisplay function
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
let lastRowIndex = tableView.numberOfRowsInSection(0)
if indexPath.row == lastRowIndex - 1 {
fetchNewDataFromServer()
}
}
// data fetcher function
func fetchNewDataFromServer() {
if(!loading && !allDataFetched) {
// call beginUpdates before multiple rows insert operation
tableView.beginUpdates()
// for loop
// insertRowsAtIndexPaths
tableView.endUpdates()
}
}
개인 API 사용 :
@objc func tableViewDidFinishReload(_ tableView: UITableView) {
print(#function)
cellsAreLoaded = true
}
퍼블릭 API 사용 :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// cancel the perform request if there is another section
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(tableViewDidLoadRows:) object:tableView];
// create a perform request to call the didLoadRows method on the next event loop.
[self performSelector:@selector(tableViewDidLoadRows:) withObject:tableView afterDelay:0];
return [self.myDataSource numberOfRowsInSection:section];
}
// called after the rows in the last section is loaded
-(void)tableViewDidLoadRows:(UITableView*)tableView{
self.cellsAreLoaded = YES;
}
더 나은 디자인은 보이는 셀을 세트에 추가하는 것입니다. 테이블이로드되었는지 확인해야하는 경우 대신이 세트 주위에 for 루프를 수행 할 수 있습니다.
var visibleCells = Set<UITableViewCell>()
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
visibleCells.insert(cell)
}
override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
visibleCells.remove(cell)
}
// example property you want to show on a cell that you only want to update the cell after the table is loaded. cellForRow also calls configure too for the initial state.
var count = 5 {
didSet {
for cell in visibleCells {
configureCell(cell)
}
}
}
Swift 3에서 선택한 답변 버전 :
var isLoadingTableView = true
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if tableData.count > 0 && isLoadingTableView {
if let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows, let lastIndexPath = indexPathsForVisibleRows.last, lastIndexPath.row == indexPath.row {
isLoadingTableView = false
//do something after table is done loading
}
}
}
기본 셀을 선택하기 전에 테이블로드가 완료되도록하기 위해 isLoadingTableView 변수가 필요했습니다. 이것을 포함하지 않으면 테이블을 스크롤 할 때마다 코드가 다시 호출됩니다.
내가 아는 가장 좋은 방법은 Eric의 답변입니다. UITableView가 데이터 요청을 마치면 알림을 받습니까?
업데이트 : 작동하려면이 전화를 넣어야합니다. -tableView:cellForRowAtIndexPath:
[tableView beginUpdates];
[tableView endUpdates];
테이블 뷰가 내용로드를 완료 한 시점을 알기 위해서는 먼저 뷰가 화면에 표시되는 방식에 대한 기본적인 이해가 필요합니다.
앱의 수명주기에는 4 가지 주요 순간이 있습니다.
2 번과 3 번은 완전히 분리되어 있습니다. 왜 ?성능상의 이유로 수정이 완료 될 때마다 순간 3의 모든 계산을 수행하고 싶지 않습니다.
그래서 나는 당신이 다음과 같은 경우에 직면하고 있다고 생각합니다.
tableView.reloadData()
tableView.visibleCells.count // wrong count oO
무슨 일이야?
다른 뷰와 마찬가지로 테이블 뷰는 내용을 느리게 다시로드합니다. 실제로 reloadData
여러 번 호출 하면 성능 문제가 발생하지 않습니다. 테이블 뷰는 델리게이트 구현을 기반으로 컨텐츠 크기 만 다시 계산하고 순간 3이 셀을로드 할 때까지 기다립니다. 이번을 레이아웃 패스라고합니다.
좋아, 레이아웃 패스에 들어가는 방법?
레이아웃 단계에서 앱은 뷰 계층의 모든 프레임을 계산합니다. 참여하려면 전용 방법을 재정의 할 수 있습니다 layoutSubviews
, updateLayoutConstraints
A의 등 UIView
보기 컨트롤러 서브 클래스와 동등한 방법.
이것이 바로 테이블 뷰가하는 일입니다. layoutSubviews
대리자 구현에 따라 재정의 하고 셀을 추가하거나 제거합니다. cellForRow
바로 새로운 셀을 추가하고 배치하기 직전에 호출합니다 willDisplay
. reloadData
테이블 뷰를 호출 하거나 계층 구조에 방금 추가 한 경우 테이블 뷰는이 주요 순간에 프레임을 채우는 데 필요한만큼의 셀을 추가합니다.
자, 이제 테이블 뷰가 내용을 다시로드하는 시점을 아는 방법은 무엇입니까?
이제 테이블 뷰가 서브 뷰 레이아웃을 완료 한 시점을 아는 방법은 무엇입니까?
• 가장 쉬운 방법은 테이블 뷰의 레이아웃에 들어가는 것입니다.
class MyTableView: UITableView {
func layoutSubviews() {
super.layoutSubviews()
// the displayed cells are loaded
}
}
이 방법은 테이블 뷰의 수명주기에서 여러 번 호출됩니다. 테이블보기의 스크롤 및 큐 제거 동작으로 인해 셀이 자주 수정, 제거 및 추가됩니다. 그러나 super.layoutSubviews()
셀이로드 된 직후에 작동합니다 . 이 솔루션은 willDisplay
마지막 인덱스 경로 의 이벤트 를 기다리는 것과 같습니다 . 이 이벤트는 layoutSubviews
추가 된 각 셀에 대해 테이블보기를 실행하는 동안 호출 됩니다.
• 또 다른 방법은 앱이 레이아웃 패스를 마치면 호출되는 것입니다.
설명서에 설명 된대로 다음 옵션을 사용할 수 있습니다 UIView.animate(withDuration:completion)
.
tableView.reloadData()
UIView.animate(withDuration: 0) {
// layout done
}
이 솔루션은 작동하지만 레이아웃이 완료된 시간과 블록이 호출 된 시간 사이에 화면이 한 번 새로 고쳐집니다. 이것은 DispatchMain.async
솔루션 과 동일 하지만 지정되었습니다.
• 또는 테이블 뷰의 레이아웃을 강제로 설정하고 싶습니다
모든 뷰가 서브 뷰 프레임을 즉시 계산하도록하는 전용 방법이 있습니다 layoutIfNeeded
.
tableView.reloadData()
table.layoutIfNeeded()
// layout done
그러나 이렇게하면 시스템에서 사용하는 지연 로딩이 제거됩니다. 이러한 메소드를 반복해서 호출하면 성능 문제가 발생할 수 있습니다. 테이블 뷰의 프레임이 계산되기 전에 호출되지 않도록하십시오. 그렇지 않으면 테이블 뷰가 다시로드되고 알림이 표시되지 않습니다.
나는 완벽한 해결책이 없다고 생각합니다. 서브 클래 싱 클래스는 문제를 일으킬 수 있습니다. 레이아웃 패스는 위에서부터 시작하여 맨 아래로 이동하므로 모든 레이아웃이 완료되면 알림을 받기가 쉽지 않습니다. 그리고 layoutIfNeeded()
성능 문제 등을 만들 수있다하지만 그 옵션을 알고, 당신은 당신의 필요에 맞게됩니다 하나 개의 대안으로 생각 할 수 있어야한다.
Swift 3에서 수행하는 방법은 다음과 같습니다.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
// perform your logic here, for the first row in the table
}
// ....
}
여기 내가 스위프트 3에서 어떻게하는지
let threshold: CGFloat = 76.0 // threshold from bottom of tableView
internal func scrollViewDidScroll(_ scrollView: UIScrollView) {
let contentOffset = scrollView.contentOffset.y
let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height;
if (!isLoadingMore) && (maximumOffset - contentOffset <= threshold) {
self.loadVideosList()
}
}
여기 내가 할 일이 있습니다.
기본 클래스 (rootVC BaseVc 등일 수 있음)에서
A. "DidFinishReloading"콜백을 보낼 프로토콜을 작성하십시오.
@protocol ReloadComplition <NSObject>
@required
- (void)didEndReloading:(UITableView *)tableView;
@end
B. 테이블 뷰를 다시로드하는 일반적인 방법을 작성하십시오.
-(void)reloadTableView:(UITableView *)tableView withOwner:(UIViewController *)aViewController;
기본 클래스 메소드 구현에서 reloadData를 호출 한 다음 지연과 함께 delegateMethod를 호출하십시오.
-(void)reloadTableView:(UITableView *)tableView withOwner:(UIViewController *)aViewController{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[tableView reloadData];
if(aViewController && [aViewController respondsToSelector:@selector(didEndReloading:)]){
[aViewController performSelector:@selector(didEndReloading:) withObject:tableView afterDelay:0];
}
}];
}
콜백이 필요한 모든 뷰 컨트롤러에서 재로드 완료 프로토콜을 확인하십시오.
-(void)didEndReloading:(UITableView *)tableView{
//do your stuff.
}
참조 : https://discussions.apple.com/thread/2598339?start=0&tstart=0
@ 플렉스 대답이 맞습니다.
그러나 tableView에 한 번에 둘 이상의 섹션이 표시 되면 실패 합니다 .
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath isEqual:((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject])]){
//end of loading
}
}
나는 이것이 대답되었다는 것을 알고 있습니다. 추천을 추가하고 있습니다.
다음 문서에 따라
https://www.objc.io/issues/2-concurrency/thread-safe-class-design/
dispatch_async로 타이밍 문제를 해결하는 것은 좋지 않습니다. FLAG 또는 무언가를 추가하여이 문제를 처리해야한다고 제안합니다.
섹션이 여러 개인 경우 마지막 섹션 (Swift 3)에서 마지막 행을 얻는 방법은 다음과 같습니다.
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if let visibleRows = tableView.indexPathsForVisibleRows, let lastRow = visibleRows.last?.row, let lastSection = visibleRows.map({$0.section}).last {
if indexPath.row == lastRow && indexPath.section == lastSection {
// Finished loading visible rows
}
}
}
Andrew의 코드를 복사하여 확장하여 테이블에 1 행이있는 경우를 설명합니다. 그것은 지금까지 일하고 있습니다!
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
// detect when all visible cells have been loaded and displayed
// NOTE: iOS7 workaround used - see: http://stackoverflow.com/questions/4163579/how-to-detect-the-end-of-loading-of-uitableview?lq=1
NSArray *visibleRows = [tableView indexPathsForVisibleRows];
NSIndexPath *lastVisibleCellIndexPath = [visibleRows lastObject];
BOOL isPreviousCallForPreviousCell = self.previousDisplayedIndexPath.row + 1 == lastVisibleCellIndexPath.row;
BOOL isLastCell = [indexPath isEqual:lastVisibleCellIndexPath];
BOOL isFinishedLoadingTableView = isLastCell && ([tableView numberOfRowsInSection:0] == 1 || isPreviousCallForPreviousCell);
self.previousDisplayedIndexPath = indexPath;
if (isFinishedLoadingTableView) {
[self hideSpinner];
}
}
참고 : Andrew의 코드에서 1 섹션 만 사용하므로 명심하십시오.
iOS7.0x에서는 솔루션이 약간 다릅니다. 여기에 내가 생각해 낸 것이 있습니다.
- (void)tableView:(UITableView *)tableView
willDisplayCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath
{
BOOL isFinishedLoadingTableView = [self isFinishedLoadingTableView:tableView
indexPath:indexPath];
if (isFinishedLoadingTableView) {
NSLog(@"end loading");
}
}
- (BOOL)isFinishedLoadingTableView:(UITableView *)tableView
indexPath:(NSIndexPath *)indexPath
{
// The reason we cannot just look for the last row is because
// in iOS7.0x the last row is updated before
// looping through all the visible rows in ascending order
// including the last row again. Strange but true.
NSArray * visibleRows = [tableView indexPathsForVisibleRows]; // did verify sorted ascending via logging
NSIndexPath *lastVisibleCellIndexPath = [visibleRows lastObject];
// For tableviews with multiple sections this will be more complicated.
BOOL isPreviousCallForPreviousCell =
self.previousDisplayedIndexPath.row + 1 == lastVisibleCellIndexPath.row;
BOOL isLastCell = [indexPath isEqual:lastVisibleCellIndexPath];
BOOL isFinishedLoadingTableView = isLastCell && isPreviousCallForPreviousCell;
self.previousDisplayedIndexPath = indexPath;
return isFinishedLoadingTableView;
}
목표 C
[self.tableView reloadData];
[self.tableView performBatchUpdates:^{}
completion:^(BOOL finished) {
/// table-view finished reload
}];
빠른
self.tableView?.reloadData()
self.tableView?.performBatchUpdates({ () -> Void in
}, completion: { (Bool finished) -> Void in
/// table-view finished reload
})
UICollectionView
내가 이해하는 한만 사용할 수 있습니다 .