Nhận thông báo khi UITableView hoàn tất yêu cầu dữ liệu?


108

Có cách nào để biết khi nào a UITableViewđã hoàn tất yêu cầu dữ liệu từ nguồn dữ liệu của nó không?

Không có viewDidLoad/ viewWillAppear/ viewDidAppearphương thức nào của bộ điều khiển chế độ xem được liên kết ( UITableViewController) được sử dụng ở đây, vì chúng đều hoạt động quá sớm. Không ai trong số họ (hoàn toàn có thể hiểu được) đảm bảo rằng các truy vấn đến nguồn dữ liệu đã kết thúc trong thời gian này (ví dụ: cho đến khi chế độ xem được cuộn).

Một giải pháp mà tôi đã tìm thấy là gọi reloadDatavào viewDidAppear, vì, khi reloadDatatrả về, chế độ xem bảng được đảm bảo đã hoàn thành truy vấn nguồn dữ liệu nhiều như nó cần vào lúc này.

Tuy nhiên, điều này có vẻ khá khó chịu, vì tôi cho rằng nó đang khiến nguồn dữ liệu được yêu cầu cung cấp cùng một thông tin hai lần (một lần tự động và một lần do lệnh reloadDatagọi) khi nó được tải lần đầu tiên.

Lý do tôi muốn làm điều này là tôi muốn giữ nguyên vị trí cuộn của UITableView- nhưng ngay xuống cấp pixel, không chỉ đến hàng gần nhất.

Khi khôi phục lại vị trí cuộn (sử dụng scrollRectToVisible:animated:), tôi cần xem bảng để đã có đủ dữ liệu trong nó, hoặc người nào khác scrollRectToVisible:animated:gọi phương thức không làm gì (đó là những gì sẽ xảy ra nếu bạn đặt cuộc gọi ngày của riêng mình trong bất kỳ viewDidLoad, viewWillAppearhoặc viewDidAppear).


Có vẻ như bạn đang tìm kiếm thứ gì đó tương tự như stackoverflow.com/a/11672379/2082172 này .
Timur Kuchkarov

Theo kinh nghiệm của tôi, UITableView có thể lưu vào bộ nhớ cache các cuộc gọi để tải lại dữ liệu, giống như nó lưu vào bộ nhớ cache các lệnh gọi chèn / xóa hàng và những thứ khác. UITableView sẽ gọi đại biểu nguồn dữ liệu khi nó sẵn sàng, tùy thuộc vào việc sử dụng cpu và những thứ khác.
Người bán Walt

Câu trả lời:


61

Câu trả lời này dường như không hoạt động nữa, do một số thay đổi được thực hiện đối với việc triển khai UITableView kể từ khi câu trả lời được viết. Xem bình luận này: Nhận thông báo khi UITableView đã hoàn thành yêu cầu dữ liệu?

Tôi đã chơi với vấn đề này trong một vài ngày và nghĩ rằng subclassing UITableView's reloadDatalà cách tiếp cận tốt nhất:

- (void)reloadData {

    NSLog(@"BEGIN reloadData");

    [super reloadData];

    NSLog(@"END reloadData");

}

reloadDatakhông kết thúc trước khi bảng hoàn tất tải lại dữ liệu của nó. Vì vậy, khi thứ hai NSLogđược kích hoạt, chế độ xem bảng đã thực sự hoàn thành việc yêu cầu dữ liệu.

Tôi đã phân lớp con UITableViewđể gửi các phương thức tới ủy quyền trước và sau đó reloadData. Nó hoạt động như một say mê.


2
Phải nói, đây có vẻ là giải pháp tốt nhất. Chỉ cần thực hiện nó và, như bạn nói, nó hoạt động như một sự quyến rũ. Cảm ơn!
lý thuyết

2
@EricMORAND Bạn nói rằng "reloadData không kết thúc trước khi bảng hoàn thành tải lại dữ liệu của nó." Bạn có thể làm rõ ý của bạn về điều đó không? Tôi thấy rằng reloadDatatrả về ngay lập tức và tôi thấy "END reloadData" trước khi các ô thực sự được tải lại (tức là trước khi các UITableViewDataSourcephương thức được gọi). Thử nghiệm của tôi chứng minh điều ngược lại chính xác với những gì bạn nói. Tôi phải hiểu lầm những gì bạn đang cố gắng nói.
Rob

3
Không phải là câu trả lời của Eric giống như không thực hiện (đọc không trọng) reloaddata, gọi reloaddata (mà sẽ được về cơ bản [siêu reloaddata], và sau đó sau khi gọi nó, làm những thứ bạn muốn tại hoàn thành?
Nirav Bhatt

5
Ngày xưa, reloadData đã hoàn tất quá trình tải trước khi quá trình này kết thúc. Nhưng Apple đã thay đổi nó ở một số điểm. Bây giờ lớp UITableView có thể lưu vào bộ nhớ cache cuộc gọi reloadData với tất cả các lệnh gọi chèn và xóa hàng. Nếu bạn nhìn vào khai báo @interface cho UITableView, bạn sẽ tìm thấy thành viên NSMutableArray _reloadItems ngay dưới _insertItems và _deleteItems. (Tôi đã phải làm lại mã mà tôi đã thừa kế do thay đổi này.)
Walt Sellers

3
Gửi bài mã hoàn thành trong một khối trên hàng đợi chính sau khi gọi [super reloadData]tác phẩm cho tôi: dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"reload complete");});. Về cơ bản, nó nhảy cóc các khối mà chế độ xem bảng đưa lên reloadData.
Timothy Moose,

53

Tôi đã gặp trường hợp tương tự trong ứng dụng của mình và tôi nghĩ sẽ đăng câu trả lời của mình cho các bạn vì các câu trả lời khác được đề cập ở đây không phù hợp với tôi cho iOS7 trở lên

Cuối cùng đây là điều duy nhất phù hợp với tôi.

[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];
 });

Cập nhật Swift:

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)

})

Vì vậy, làm thế nào điều này hoạt động.

Về cơ bản khi bạn tải lại luồng chính sẽ trở nên bận rộn vì vậy tại thời điểm đó khi chúng ta thực hiện một luồng không đồng bộ gửi đi, khối sẽ đợi cho đến khi luồng chính kết thúc. Vì vậy, khi chế độ xem bảng đã được tải hoàn toàn, luồng chính sẽ kết thúc và do đó, nó sẽ gửi khối phương thức của chúng ta

Đã thử nghiệm trong iOS7 và iOS8 và nó hoạt động tuyệt vời;)

Cập nhật cho iOS9: Điều này chỉ hoạt động tốt còn iOS9 cũng vậy. Tôi đã tạo một dự án mẫu trong github dưới dạng POC. https://github.com/ipraba/TableReloadingNotifier

Tôi đính kèm ảnh chụp màn hình bài kiểm tra của mình ở đây.

Môi trường được thử nghiệm: Trình mô phỏng iOS9 iPhone6 ​​từ Xcode7

nhập mô tả hình ảnh ở đây


8
câu trả lời tốt nhất tôi đã tìm thấy!
Nikolay Shubenkov

@Gon, tôi đã làm một bài kiểm tra trong iOS9 và nó hoạt động tốt. Bạn có thể vui lòng tham khảo github.com/ipraba/TableReloadingNotifier
iPrabu

Nó hoạt động tốt trên trình giả lập của tôi nhưng dường như không hoạt động trên thiết bị thực tế. Bất cứ ai khác có vấn đề này?
sosale151

1
Giải pháp này cuối cùng đã làm việc cho tôi! nó hoạt động tốt trên iOS 9
Mạnh mẽ

1
Tôi đã thử nghiệm trên Thiết bị thực, iPhone 6s. Nó cũng hoạt động tốt.
Mạnh mẽ

25

CHỈNH SỬA: Câu trả lời này thực sự không phải là một giải pháp. Lúc đầu, nó có vẻ hoạt động vì quá trình tải lại có thể diễn ra khá nhanh, nhưng trên thực tế, khối hoàn thành không nhất thiết phải được gọi sau khi dữ liệu đã hoàn thành tải lại hoàn toàn - bởi vì reloadData không chặn. Bạn có lẽ nên tìm kiếm một giải pháp tốt hơn.

Để mở rộng câu trả lời của @Eric MORAND, hãy đặt một khối hoàn thành vào. Ai lại không yêu thích một khối?

@interface DUTableView : UITableView

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

@end

và ...

#import "DUTableView.h"

@implementation DUTableView

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

@end

Sử dụng:

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

2
Tôi không thể nhận đủ khối. Ai cần một đại biểu khi bạn có một khối đẹp!
bandejapaisa

Tôi thích điều này, nhưng những khi hệ thống gọi reloadData (), ví dụ: khi bảng được hiển thị lần đầu tiên thì sao?
xứng

12
Đây không phải là khối giải pháp completition bắt đầu thực hiện trước khi cellForRowAtIndexPath
zvjerka24

1
Điều này sẽ không hoạt động; reloadData không phải là một phương pháp chặn. Mã này được gọi ngay sau khi gọi reloadData, ngay cả khi các ô chưa được tải lại. Bên cạnh đó, hãy nhìn vào đoạn mã này, và bạn sẽ thấy rằng bạn cũng có thể chỉ cần đặt mã của mình sau khi reloadData .
colinta,

1
Điều này phù hợp với tôi với một thay đổi nhỏ. Thay vì kêu gọi khối hoàn trực tiếp, gọi nó là trong một khối được đăng trên hàng đợi chính: dispatch_async(dispatch_get_main_queue(), ^{completionBlock();});. Về cơ bản điều này nhảy cóc các khối được đăng bởi chế độ xem bảng trong reloadData.
Timothy Moose

11

reloadData chỉ yêu cầu dữ liệu cho các ô hiển thị. Nói rằng, để được thông báo khi một phần cụ thể của bảng của bạn được tải, vui lòng nối tableView: willDisplayCell:phương thức.

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

1
Tôi không chắc liệu điều này có hiệu quả không. Chỉ mục cuối là phần cuối của dữ liệu bảng (ví dụ: 100 bản ghi) nhưng bảng sẽ chỉ hiển thị những gì hiển thị trên màn hình (ví dụ: 8 bản ghi).
Travis M.

10

Đó là giải pháp của tôi. 100% hoạt động và sử dụng trong nhiều công trình. Đó là một lớp con UITableView đơn giản.

@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

Nó tương tự như giải pháp của Josh Brown với một ngoại lệ. Không cần chậm trễ trong phương thức PerformSelector. Không có vấn đề bao lâu reloadDatamất. tableViewDidLoadData:luôn cháy khi tableViewhỏi xong dataSource cellForRowAtIndexPath.

Ngay cả khi bạn không muốn phân lớp, UITableViewbạn có thể chỉ cần gọi [performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]và bộ chọn của bạn sẽ được gọi ngay sau khi bảng hoàn thành tải lại. Nhưng bạn nên đảm bảo rằng bộ chọn chỉ được gọi một lần cho mỗi lần gọi tới reloadData:

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

Thưởng thức. :)


1
Sử dụng cuộc gọi PerformSelector là tuyệt vời. Đơn giản và hiệu quả, thanx
Julia

Điều này là hoàn toàn tuyệt vời. Tôi đã đấu tranh với điều này trong nhiều ngày. Cảm ơn bạn!
David Carrico

@MarkKryzhanouski, bạn đã thử nghiệm trên iOS 9 chưa?
Victor

@MarkKryzhanouski, cảm ơn vì phản hồi nhanh chóng! Hoạt động tốt trên iOS 7 và 8.
Victor

Các giải pháp performSelectorhoặc thực thi trên chuỗi chính với dispatch_asynchkhông hoạt động trên iOS 9 .
Manuel

8

Đây là câu trả lời cho một câu hỏi hơi khác: Tôi cần biết khi nào tôi UITableViewcũng đã kết thúc cuộc gọi cellForRowAtIndexPath(). Tôi đã phân loại phụ layoutSubviews()(cảm ơn @Eric MORAND) và thêm một lệnh gọi lại dành cho đại biểu:

SDTableView.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

Sử dụng:

MyTableViewController.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;
    }
}

LƯU Ý: Vì đây là một lớp con UITableViewđã có thuộc tính ủy quyền trỏ đến MyTableViewControllernên không cần thêm một lớp khác. "Đại biểu @dynamic" yêu cầu trình biên dịch sử dụng thuộc tính này. (Đây là liên kết mô tả điều này: http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/ )

Các UITableViewbất động sản trong MyTableViewControllerphải được thay đổi để sử dụng mới SDTableViewlớp. Điều này được thực hiện trong Trình kiểm tra nhận dạng trình tạo giao diện. Chọn UITableViewbên trong UITableViewControllervà đặt "Lớp tùy chỉnh" của nó thành SDTableView.


Bạn đặt MyTableViewController của mình làm đại biểu cho SDTableView ở đâu? Làm cách nào để có thể đặt thuộc tính tên "ủy nhiệm" trên SDTableView, khi lớp cha của nó đã có thuộc tính tên đó (UITableView.delegate)? Làm cách nào để bạn gắn SDTableView tùy chỉnh của mình vào thuộc tính MyTableViewController.tableView khi thuộc tính thuộc loại "UITablewView" và đối tượng (một bản sao của SDTableView) thuộc loại "SDTableView"? Tôi đang chiến đấu cùng một vấn đề, vì vậy tôi hy vọng có một giải pháp cho những vấn đề này :)
Earl Grey

Trong mã của Symmetric (SDTableView), không nên đặt reloadInProgress thành FALSE trong didReloadData thay vì didLayoutSubviews? Vì reloadData được gọi sau khi layoutSubviews và quá trình tải không được coi là hoàn thành trước khi reloadData kết thúc.
tzuchien.chiu

Điều này không hoạt động cho iOS 8, phải kết hợp câu trả lời của iPrabu .
Stunner

6

Tôi đã tìm thấy một cái gì đó tương tự để có được thông báo cho sự thay đổi trong contentSizecác TableView. Tôi nghĩ rằng điều đó cũng sẽ hoạt động ở đây vì contentSize cũng thay đổi khi tải dữ liệu.

Thử cái này:

Bằng viewDidLoadvăn bản,

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

và thêm phương thức này vào viewController của bạn:

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


    }
}

Bạn có thể cần sửa đổi nhỏ trong kiểm tra sự thay đổi. Điều này đã làm việc cho tôi mặc dù.

Chúc mừng! :)


1
Rất thông minh. Hoạt động hoàn hảo cho tôi. Cám ơn vì đã chia sẻ!
DZenBot

2
Thanh lịch! Điều duy nhất tôi muốn thêm là bạn cần loại bỏ bản thân và một người quan sát (có lẽ trong phương thức -dealloc). Hoặc thêm chính bạn làm người quan sát trong -viewWillAppearvà loại bỏ chính mình trong -viewWillDisapearphương pháp.
Loozie

2

Đây là một giải pháp khả thi, mặc dù đó là một cuộc tấn công:

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

Nơi -scrollTableViewphương thức của bạn cuộn chế độ xem bảng với -scrollRectToVisible:animated:. Và, tất nhiên, bạn có thể định cấu hình độ trễ trong đoạn mã trên từ 0,3 đến bất kỳ thứ gì có vẻ phù hợp với bạn. Vâng, nó cực kỳ hack, nhưng nó hoạt động với tôi trên iPhone 5 và 4S của tôi ...


2
Nó có vẻ "hacky", nhưng đây thực sự là một cách tiếp cận tiêu chuẩn để cuộn: hoãn nó đến chu kỳ chạy tiếp theo. Tôi sẽ thay đổi độ trễ thành 0, vì nó hoạt động tốt và ít độ trễ hơn.
phatmann

Không hoạt động với tôi với độ trễ được đặt thành 0; 0,3 là mức thấp nhất mà tôi có thể đi mà vẫn nhận được dữ liệu.
Josh Brown

@phatmann Điều này đã làm việc cho tôi. Tôi cũng có thể sử dụng 0 cho sự chậm trễ của mình. Cám ơn hai bạn.
mcphersonjr

1

Tôi tin rằng tôi đã có một cái gì đó tương tự. Tôi đã thêm một BOOL làm biến thể hiện cho tôi biết liệu phần bù đã được khôi phục hay chưa và hãy kiểm tra điều đó -viewWillAppear:. Khi nó chưa được khôi phục, tôi khôi phục nó theo phương pháp đó và đặt BOOL để chỉ ra rằng tôi đã khôi phục phần bù.

Đó là một loại hack và nó có thể được thực hiện tốt hơn, nhưng điều này phù hợp với tôi vào lúc này.


Vâng, vấn đề là viewWillAppear dường như quá sớm (ít nhất là trong kịch bản của tôi). Cố gắng khôi phục phần bù trong viewWillAppear không có tác dụng gì - trừ khi tôi thêm bản hack gọi reloadData trước. Theo tôi hiểu, viewWillAppear / viewDidAppear chỉ tham chiếu đến bản thân chế độ xem bảng thực sự xuất hiện - chúng không đưa ra bất kỳ tuyên bố nào về việc các ô bảng trong chế độ xem đã được liệt kê hay được điền chưa ... và điều này cần xảy ra trước khi bạn có thể khôi phục một phần bù, nếu không thì bạn sẽ khôi phục một phần bù trên một khung nhìn trống (và tôi hiểu tại sao điều đó sẽ không hoạt động!).
kennethmac2000 27/09/09

Nhưng có thể ý bạn là bạn cũng đang buộc tải các ô trong bảng trước bằng cách gọi reloadData trong viewWillAppear?
kennethmac2000 27/09/09

Không, tôi không buộc phải tải lại. Khi tôi gặp sự cố, tôi đã cố gắng khôi phục độ lệch trong -viewDidLoad(tất nhiên là sẽ xảy ra) nhưng điều đó chỉ hoạt động khi tôi đặt hoạt ảnh bù đắp. Bằng cách di chuyển cài đặt của độ lệch sang -viewWillAppear:nó đã hoạt động, nhưng tôi phải duy trì một cờ để chỉ đặt nó một lần. Tôi cho rằng chế độ xem bảng tải lại dữ liệu của nó sau khi được thêm vào một chế độ xem, vì vậy nó đã ở trong -loadView. Bạn có chắc rằng bạn có dữ liệu của mình khi tải chế độ xem không? Hay nó đang được tải trong một chuỗi riêng biệt hoặc một cái gì đó?
Joost 27/09/09

OK, có lẽ bạn có thể giúp tôi hiểu ở đây. Đây là cách tôi hiểu trình tự các lệnh gọi khi một UITableView được xây dựng. 1) viewDidLoad được kích hoạt trước. Điều này cho biết UITableView được tải vào bộ nhớ. 2) viewWillAppear bên cạnh lửa. Điều này chỉ ra rằng UITableView sẽ được hiển thị, nhưng không nhất thiết là tất cả các đối tượng UITableViewCell hiển thị sẽ được khởi tạo hoàn toàn / kết xuất hoàn tất.
kennethmac2000 29/09/09

2
Và tất cả những điều trên đều đúng, câu hỏi đặt ra là: chúng ta có những tùy chọn không hack nào để tìm ra khi UITableView đã thu được đủ thông tin từ nguồn dữ liệu của nó để đảm bảo rằng một yêu cầu cuộn (tức là scrollRectToVible: animation: call ) sẽ thực sự hoạt động (và không chỉ là không làm gì)?
kennethmac2000 29/09/09

1

Có vẻ như bạn muốn cập nhật nội dung ô, nhưng không có các bước nhảy đột ngột có thể đi kèm với việc chèn và xóa ô.

Có một số bài báo về việc làm đó. Đây là một.

Tôi khuyên bạn nên sử dụng setContentOffset: animation: thay vì scrollRectToVosystem: animation: cho các cài đặt pixel hoàn hảo của chế độ xem cuộn.


1

Bạn có thể thử logic sau:

-(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;
}

Và trước khi bạn gọi reloadData, hãy đặt presIndexPath thành nil. Giống:

prevIndexPath = nil;
[mainTableView reloadData];

Tôi đã thử nghiệm với NSLogs và logic này có vẻ ổn. Bạn có thể tùy chỉnh / cải tiến nếu cần.


0

cuối cùng tôi đã làm cho mã của tôi hoạt động với cái này -

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

có một số thứ cần được quan tâm -

  1. gọi nó trong " - (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath"
  2. chỉ cần đảm bảo rằng thông báo "scrollToRowAtIndexPath" được gửi đến phiên bản có liên quan của UITableView, chắc chắn là MyTableview trong trường hợp này.
  3. Trong trường hợp của tôi, UIView là dạng xem có chứa phiên bản của UITableView
  4. Ngoài ra, điều này sẽ được gọi cho mỗi lần tải tế bào. Do đó, hãy thiết lập một logic bên trong "cellForRowAtIndexPath" để tránh gọi "scrollToRowAtIndexPath" nhiều lần.

và chỉ cần đảm bảo rằng phần này không được gọi nhiều hơn một lần. nếu không, nó sẽ khóa cuộn.
smile.al.d.way

Điều này có thể hiệu quả, nhưng nó chắc chắn không thanh lịch và không phải là giải pháp tôi đang tìm kiếm. Thật không may, dường như không có cách nào tốt hơn để xác định liệu -reloadData đã thực sự hoàn thành việc lấy dữ liệu hay chưa ...
Josh Brown

0

Bạn có thể thay đổi kích thước chế độ xem bảng của mình hoặc đặt kích thước nội dung trong phương pháp này khi tất cả dữ liệu được tải:

- (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);
}

0

Tôi chỉ chạy bộ đếm thời gian được lập lịch lặp lại và chỉ làm mất hiệu lực khi kích thước nội dung của bảng lớn hơn khi chiều cao tableHeaderView (có nghĩa là có nội dung hàng trong bảng). Mã trong C # (monotouch), nhưng tôi hy vọng ý tưởng rõ ràng:

    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;
                    }
                }));
        }
    }

0

Không UITableView layoutSubviewsđược gọi ngay trước khi chế độ xem bảng hiển thị nội dung của nó? Tôi nhận thấy rằng nó được gọi khi chế độ xem bảng đã tải xong dữ liệu của nó, có lẽ bạn nên điều tra theo hướng đó.


0

Kể từ iOS 6 trở đi, UITableviewphương thức ủy quyền được gọi là:

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

sẽ thực thi khi bảng của bạn tải lại thành công. Bạn có thể tùy chỉnh theo yêu cầu trong phương pháp này.


0

Giải pháp tốt nhất mà tôi đã tìm thấy trong Swift

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

-1

Tại sao không chỉ cần gia hạn?

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

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

cuộn đến cuối:

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

Không được thử nghiệm với nhiều dữ liệu

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.