programing

UITableView에서 데이터 요청이 완료되면 알림을 받으시겠습니까?

instargram 2023. 6. 6. 00:25
반응형

UITableView에서 데이터 요청이 완료되면 알림을 받으시겠습니까?

언제인지 알 수 있는 방법이 있습니까?UITableView데이터 소스의 데이터 요청을 마쳤습니까?

아무 것도 없습니다.viewDidLoad/viewWillAppear/viewDidAppear 뷰 view controller)의(methods of the associated view controller()UITableViewController는 모두 여기서 는 모두 너무 일찍 발사되기 때문에 여기에 유용합니다.이들 중 어느 것도(완전히 이해할 수 있는) 당분간 데이터 소스에 대한 쿼리가 완료되었음을 보장하지 않습니다(예: 보기가 스크롤될 때까지).

한 가지 해결 방법은 전화를 거는 것입니다.reloadDataviewDidAppear 이후로, 언제부터.reloadData를 반환합니다. 테이블 보기는 당분간 필요한 만큼 데이터 소스 쿼리를 완료할 수 있습니다.

한 정보를 으로 한 번, 한 번) 에 다소으로 보입니다.reloadDatacall할 때 합니다.call).

이 작업을 수행하려는 이유는 파일의 스크롤 위치를 유지하고 싶기 때문입니다.UITableView가장 가까운 행뿐만 아니라 픽셀 레벨로 바로 내려갑니다.

위치를 할 때 ()scrollRectToVisible:animated:가 있어야 . 뷰에 충분한 데이터가 있어야 . 그렇지 않으면scrollRectToVisible:animated:은 아무 것도 하지 의 위치에 합니다).viewDidLoad,viewWillAppear또는viewDidAppear).

답변이 작성된 이후 UITableView 구현에 일부 변경 사항이 적용되었기 때문에 이 답변은 더 이상 작동하지 않는 것 같습니다.이 의견 참조: UITableView에서 데이터 요청이 완료되면 알림을 받으시겠습니까?

며칠 동안 이 문제를 가지고 놀았는데 하위 분류가UITableViewreloadData좋은 접근법입니다.

- (void)reloadData {

    NSLog(@"BEGIN reloadData");

    [super reloadData];

    NSLog(@"END reloadData");

}

reloadData테이블이 데이터 다시 로드를 완료하기 전에 종료되지 않습니다.두 래서가 때 두번째.NSLog가 실행되면 테이블 보기에서 데이터 요청이 완료됩니다.

하위분습다니했류를 하위 했습니다.UITableView전후에 대리인에게 방법을 보냅니다.reloadData그것은 매력적으로 작동합니다.

저는 제 앱에 같은 시나리오가 있었고 여기에 언급된 다른 답변이 iOS7 이상에서는 작동하지 않기 때문에 여러분에게 제 답변을 게시할 것이라고 생각했습니다.

마침내 이것이 저에게 잘 된 유일한 것입니다.

[yourTableview reloadData];

dispatch_async(dispatch_get_main_queue(),^{
        NSIndexPath *path = [NSIndexPath indexPathForRow:yourRow inSection:yourSection];
        //Basically maintain your logic to get the indexpath
        [yourTableview scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
 });

신속한 업데이트:

yourTableview.reloadData()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
    let path : NSIndexPath = NSIndexPath(forRow: myRowValue, inSection: mySectionValue)
    //Basically maintain your logic to get the indexpath
    yourTableview.scrollToRowAtIndexPath(path, atScrollPosition: UITableViewScrollPosition.Top, animated: true)

})

이게 어떻게 작동하는지.

기본적으로 당신이 다시 로드할 때 메인 스레드가 사용 중이 되어 우리가 디스패치 비동기 스레드를 할 때 블록은 메인 스레드가 완료될 때까지 대기합니다.따라서 테이블 뷰가 완전히 로드되면 메인 스레드가 완료되고 메소드 블록이 발송됩니다.

iOS7과 iOS8에서 테스트를 거쳤으며, 매우 잘 작동합니다;)

iOS9용 업데이트: iOS9도 작동합니다.저는 github을 POC로 샘플 프로젝트를 만들었습니다.https://github.com/ipraba/TableReloadingNotifier

제 시험 스크린샷을 여기에 첨부합니다.

테스트 환경: Xcode7의 iOS9 iPhone6 시뮬레이터

여기에 이미지 설명 입력

편집: 이 답변은 실제로 해결책이 아닙니다.처음에는 재로드가 매우 빠르게 발생할 수 있기 때문에 작동하는 것처럼 보이지만, reloadData가 차단하지 않기 때문에 데이터 재로드가 완전히 완료된 후에는 완료 블록이 호출되지 않습니다.당신은 아마도 더 나은 해결책을 찾아야 할 것입니다.

@Eric MORAND의 답변을 확장하기 위해 완료 블록을 삽입합니다.누가 블록을 사랑하지 않겠습니까?

@interface DUTableView : UITableView

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

@end

그리고...

#import "DUTableView.h"

@implementation DUTableView

- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
    [super reloadData];
    if(completionBlock) {
        completionBlock();
    }
}

@end

용도:

[self.tableView reloadDataWithCompletion:^{
                                            //do your stuff here
                                        }];

데이터를 다시 로드합니다. 보이는 셀에 대한 데이터만 요청합니다.될 때 를 후크하십시오.tableView: willDisplayCell:방법.

- (void) reloadDisplayData
{
    isLoading =  YES;
    NSLog(@"Reload display with last index %d", lastIndex);
    [_tableView reloadData];
    if(lastIndex <= 0){
    isLoading = YES;
    //Notify completed
}

- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(indexPath.row >= lastIndex){
    isLoading = NO;
    //Notify completed
}

그게 제 해결책입니다.100% 작동하며 많은 프로젝트에서 사용됩니다.단순한 UITableView 하위 클래스입니다.

@protocol MyTableViewDelegate<NSObject, UITableViewDelegate>
@optional
- (void)tableViewWillReloadData:(UITableView *)tableView;
- (void)tableViewDidReloadData:(UITableView *)tableView;
@end

@interface MyTableView : UITableView {
    struct {
        unsigned int delegateWillReloadData:1;
        unsigned int delegateDidReloadData:1;
        unsigned int reloading:1;
    } _flags;
}
@end

@implementation MyTableView
- (id<MyTableViewDelegate>)delegate {
    return (id<MyTableViewDelegate>)[super delegate];
}

- (void)setDelegate:(id<MyTableViewDelegate>)delegate {
    [super setDelegate:delegate];
    _flags.delegateWillReloadData = [delegate respondsToSelector:@selector(tableViewWillReloadData:)];
    _flags.delegateDidReloadData = [delegate    respondsToSelector:@selector(tableViewDidReloadData:)];
}

- (void)reloadData {
    [super reloadData];
    if (_flags.reloading == NO) {
        _flags.reloading = YES;
        if (_flags.delegateWillReloadData) {
            [(id<MyTableViewDelegate>)self.delegate tableViewWillReloadData:self];
        }
        [self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];
    }
}

- (void)finishReload {
    _flags.reloading = NO;
    if (_flags.delegateDidReloadData) {
        [(id<MyTableViewDelegate>)self.delegate tableViewDidReloadData:self];
    }
}

@end

가지 예외를 제외하고는 조쉬 브라운의 솔루션과 유사합니다.performSelector 메서드는 지연이 필요하지 않습니다.시간이 얼마나 걸리든 간에 상관없이. tableViewDidLoadData: 질문이 cellForRowAtIndexPath끝나면 항상 실행됩니다.

하위 클래스를 원하지 않더라도UITableView간단히 전화할 수 있습니다.[performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]테이블을 다시 로드한 후 바로 선택기가 호출됩니다. 한 .reloadData:

[self.tableView reloadData];
[self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];

맛있게 드세요 :)

다음은 약간 다른 질문에 대한 대답입니다.인지 .UITableView또한 전화하는 것을 마쳤습니다.cellForRowAtIndexPath()하위 분류했습니다.layoutSubviews() MORAND) 콜백을 ( @Eric MORAND) 그리인콜추가습니다했을백대리고고워요마▁(다▁a니(eric▁added습▁delegate▁call▁@back.

SD 테이블 보기.h:

@protocol SDTableViewDelegate <NSObject, UITableViewDelegate>
@required
- (void)willReloadData;
- (void)didReloadData;
- (void)willLayoutSubviews;
- (void)didLayoutSubviews;
@end

@interface SDTableView : UITableView

@property(nonatomic,assign) id <SDTableViewDelegate> delegate;

@end;

SDTableView.m:

#import "SDTableView.h"

@implementation SDTableView

@dynamic delegate;

- (void) reloadData {
    [self.delegate willReloadData];

    [super reloadData];

    [self.delegate didReloadData];
}

- (void) layoutSubviews {
    [self.delegate willLayoutSubviews];

    [super layoutSubviews];

    [self.delegate didLayoutSubviews];
}

@end

용도:

내 테이블 보기 컨트롤러입니다.h:

#import "SDTableView.h"
@interface MyTableViewController : UITableViewController <SDTableViewDelegate>
@property (nonatomic) BOOL reloadInProgress;

MyTableViewController.m:

#import "MyTableViewController.h"
@implementation MyTableViewController
@synthesize reloadInProgress;

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    if ( ! reloadInProgress) {
        NSLog(@"---- numberOfSectionsInTableView(): reloadInProgress=TRUE");
        reloadInProgress = TRUE;
    }

    return 1;
}

- (void)willReloadData {}
- (void)didReloadData {}
- (void)willLayoutSubviews {}

- (void)didLayoutSubviews {
    if (reloadInProgress) {
        NSLog(@"---- layoutSubviewsEnd(): reloadInProgress=FALSE");
        reloadInProgress = FALSE;
    }
}

참고: 이 항목은 다음의 하위 클래스이므로UITableView 이미 대리속지있습다니어를 있습니다.MyTableViewController추가할 필요가 없습니다."@dynamic delegate"는 컴파일러에게 이 속성을 사용하도록 지시합니다. (http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/) 을 설명하는 링크는 다음과 같습니다.)

UITableViewMyTableViewController 새항을사변합경니다야를 하려면 .SDTableView클래스. 인터페이스 작성기 ID 검사기에서 수행됩니다.다음을 선택합니다.UITableView의내에의 UITableViewController"사용자 지정 클래스"를 다음으로 설정합니다.SDTableView.

변경 알림을 받기 위해 비슷한 것을 찾았습니다.contentSizeTableView contentSize도 때문에 합니다. contentSize 데 로 딩 따 라 바 뀌 때 여 문 생 다 니 기 도 야 합 해 각 작 동 한 고 다 서 에 기 도 에 이 터 ▁i ▁as content 니 다 합 ▁here ▁should size ▁content size ▁work 생 ▁with각

사용해 보십시오.

viewDidLoad쓰고, 글을 쓰고,

[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:NULL];

다음 방법을 뷰 컨트롤러에 추가합니다.

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"contentSize"]) {
        DLog(@"change = %@", change.description)

        NSValue *new = [change valueForKey:@"new"];
        NSValue *old = [change valueForKey:@"old"];

        if (new && old) {
            if (![old isEqualToValue:new]) {
                // do your stuff
            }
        }


    }
}

변경 사항을 확인하기 위해 약간의 수정이 필요할 수 있습니다.하지만 이것은 저에게 효과가 있었습니다.

건배! :)

해킹이지만 가능한 해결책은 다음과 같습니다.

[self.tableView reloadData];
[self performSelector:@selector(scrollTableView) withObject:nil afterDelay:0.3];

당신의 의너어에가 ?-scrollTableView▁view▁with▁the▁table로 표 보기를 .-scrollRectToVisible:animated:그리고 물론, 위의 코드에서 0.3에서 당신에게 효과가 있는 것처럼 보이는 것으로 지연을 구성할 수 있습니다.도 안되게 촌스럽긴 , 제5와 4S에서 합니다... ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ 5와 4S에서 작동합니다.

제가 믿는 비슷한 것이 있었습니다.하였습니다.-viewWillAppear:복원되지 않은 경우에는 해당 방법으로 복원하고 오프셋을 복구했음을 나타내도록 BOOL을 설정합니다.

이것은 일종의 해킹이고 아마도 더 잘 할 수 있을 것입니다. 하지만 현재로서는 이것이 저에게 효과가 있습니다.

셀 내용을 업데이트하려는 것처럼 들리지만 셀 삽입 및 삭제와 함께 발생할 수 있는 갑작스러운 점프는 없습니다.

그것을 하는 것에 대한 몇 가지 기사들이 있습니다.이거 하나.

스크롤 보기의 픽셀 단위 설정을 위해 스크롤 대신 setContentOffset:animated:를 사용하는 것이 좋습니다.

다음과 같은 논리를 시도할 수 있습니다.

-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"MyIdentifier"];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

    if ( [self chkIfLastCellIndexToCreate:tableView :indexPath]){
        NSLog(@"Created Last Cell. IndexPath = %@", indexPath);
        //[self.activityIndicator hide];
        //Do the task for TableView Loading Finished
    }
    prevIndexPath = indexPath;

    return cell;
}



-(BOOL) chkIfLastCellIndexToCreate:(UITableView*)tableView : (NSIndexPath *)indexPath{

    BOOL bRetVal = NO;
    NSArray *visibleIndices = [tableView indexPathsForVisibleRows];

    if (!visibleIndices || ![visibleIndices count])
        bRetVal = YES;

    NSIndexPath *firstVisibleIP = [visibleIndices objectAtIndex:0];
    NSIndexPath *lastVisibleIP = [visibleIndices objectAtIndex:[visibleIndices count]-1];

    if ((indexPath.row > prevIndexPath.row) && (indexPath.section >= prevIndexPath.section)){
        //Ascending - scrolling up
        if ([indexPath isEqual:lastVisibleIP]) {
            bRetVal = YES;
            //NSLog(@"Last Loading Cell :: %@", indexPath);
        }
    } else if ((indexPath.row < prevIndexPath.row) && (indexPath.section <= prevIndexPath.section)) {
        //Descending - scrolling down
        if ([indexPath isEqual:firstVisibleIP]) {
            bRetVal = YES;
            //NSLog(@"Last Loading Cell :: %@", indexPath);
        }
    }
    return bRetVal;
}

reloadData를 호출하기 전에 prevIndexPath를 0으로 설정합니다.예:

prevIndexPath = nil;
[mainTableView reloadData];

NSLogs로 테스트를 해봤는데, 이 논리는 괜찮은 것 같습니다.필요에 따라 사용자 지정/개선할 수 있습니다.

마침내 나는 이것으로 내 코드를 작동시켰습니다.

[tableView scrollToRowAtIndexPath:scrollToIndex atScrollPosition:UITableViewScrollPositionTop animated:YES];

처리해야 할 것이 몇 가지 있었습니다.

  1. "안에 그것을 부름.- (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath"
  2. "ScrollToRowAt"를 확인하십시오.IndexPath" 메시지가 관련된 UITableView 인스턴스(이 경우 MyTableView)로 전송됩니다.
  3. 내 경우 UIView는 UITableView의 인스턴스를 포함하는 뷰입니다.
  4. 또한 모든 셀 로드에 대해 호출됩니다.따라서 "cellForRowAt" 내부에 논리를 작성합니다."scrollToRowAt" 호출을 피하기 위한 IndexPath"인덱스 경로"가 두 번 이상 있습니다.

모든 데이터가 로드될 때 다음 방법으로 테이블 보기의 크기를 조정하거나 내용 크기를 설정할 수 있습니다.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    tableView.frame =CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y, tableView.frame.size.width, tableView.contentSize.height);
}

반복 예약 타이머를 실행하고 테이블의 contentSize가 테이블일 때 더 클 때만 비활성화합니다.머리글 보기 높이(표에 행 내용이 있음을 의미).코드는 C#(모노터치)이지만, 아이디어가 명확하기를 바랍니다.

    public override void ReloadTableData()
    {
        base.ReloadTableData();

        // don't do anything if there is no data
        if (ItemsSource != null && ItemsSource.Length > 0)
        {
            _timer = NSTimer.CreateRepeatingScheduledTimer(TimeSpan.MinValue, 
                new NSAction(() => 
                {
                    // make sure that table has header view and content size is big enought
                    if (TableView.TableHeaderView != null &&
                        TableView.ContentSize.Height > 
                            TableView.TableHeaderView.Frame.Height)
                    {
                        TableView.SetContentOffset(
                            new PointF(0, TableView.TableHeaderView.Frame.Height), false);
                        _timer.Invalidate();
                        _timer = null;
                    }
                }));
        }
    }

아닌가요?UITableView layoutSubviews테이블 보기에 내용이 표시되기 직전에 호출하시겠습니까?테이블 뷰가 데이터 로드를 완료하면 호출된다는 것을 알게 되었습니다. 해당 방향으로 조사해야 할 것 같습니다.

6 6은UITableview대리자 메서드 호출:

-(void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section

테이블이 다시 로드되면 실행됩니다.이 방법에서 필요에 따라 사용자 지정을 수행할 수 있습니다.

스위프트에서 찾은 최고의 솔루션

extension UITableView {
    func reloadData(completion: ()->()) {
        self.reloadData()
        dispatch_async(dispatch_get_main_queue()) {
            completion()
        }
    }
}

왜 그냥 연장하지 않습니까?

@interface UITableView(reloadComplete)
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock;
@end

@implementation UITableView(reloadComplete)
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
    [self reloadData];
    if(completionBlock) {
        completionBlock();
    }
}
@end

끝까지 스크롤:

[self.table reloadDataWithCompletion:^{
    NSInteger numberOfRows = [self.table numberOfRowsInSection:0];
    if (numberOfRows > 0)
    {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:numberOfRows-1 inSection:0];
        [self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }
}];

많은 데이터로 테스트되지 않음

언급URL : https://stackoverflow.com/questions/1483581/get-notified-when-uitableview-has-finished-asking-for-data

반응형