รับการแจ้งเตือนเมื่อ UITableView ขอข้อมูลเสร็จแล้ว?


108

มีวิธีใดบ้างที่จะทราบได้ว่าเมื่อการUITableViewขอข้อมูลจากแหล่งข้อมูลเสร็จสิ้นแล้วหรือไม่?

ไม่มีการใช้viewDidLoad/ viewWillAppear/ viewDidAppearmethod ของตัวควบคุมมุมมองที่เกี่ยวข้อง ( UITableViewController) ที่นี่เนื่องจากทั้งหมดเริ่มทำงานเร็วเกินไป ไม่มีข้อใดเลย (อย่างที่เข้าใจได้ทั้งหมด) รับประกันได้ว่าการสืบค้นไปยังแหล่งข้อมูลเสร็จสิ้นในขณะนี้ (เช่นจนกว่าจะเลื่อนมุมมอง)

วิธีแก้ปัญหาอย่างหนึ่งที่ฉันพบคือการโทรreloadDataเข้าviewDidAppearเนื่องจากเมื่อreloadDataส่งคืนมุมมองตารางจะได้รับการรับประกันว่าจะค้นหาแหล่งข้อมูลเสร็จสิ้นแล้วเท่าที่จำเป็นในขณะนี้

อย่างไรก็ตามสิ่งนี้ดูเหมือนจะค่อนข้างน่ารังเกียจเนื่องจากฉันคิดว่ามันทำให้แหล่งข้อมูลถูกขอข้อมูลเดียวกันสองครั้ง (หนึ่งครั้งโดยอัตโนมัติและหนึ่งครั้งเนื่องจากการreloadDataโทร) เมื่อโหลดครั้งแรก

เหตุผลที่ฉันต้องการทำเลยก็คือฉันต้องการรักษาตำแหน่งการเลื่อนของUITableView- แต่ลงไปที่ระดับพิกเซลไม่ใช่แค่แถวที่ใกล้ที่สุด

เมื่อคืนเลื่อนตำแหน่ง (ใช้scrollRectToVisible:animated:) ผมต้องดูตารางที่จะมีข้อมูลที่เพียงพอในนั้นหรืออื่น ๆscrollRectToVisible:animated:เรียกวิธีการไม่ทำอะไรเลย (ซึ่งเป็นสิ่งที่เกิดขึ้นถ้าคุณวางสายของตัวเองในการใด ๆviewDidLoad, viewWillAppearหรือviewDidAppear)


ดูเหมือนว่าคุณกำลังมองหาสิ่งที่คล้ายกับstackoverflow.com/a/11672379/2082172นี้
Timur Kuchkarov

จากประสบการณ์ของฉัน UITableView สามารถแคชการเรียกเพื่อโหลดข้อมูลซ้ำได้เช่นเดียวกับที่แคชการโทรเพื่อแทรก / ลบแถวและสิ่งอื่น ๆ UITableView จะเรียกตัวแทนแหล่งข้อมูลเมื่อพร้อมขึ้นอยู่กับการใช้งาน cpu และสิ่งอื่น ๆ
Walt Sellers

คำตอบ:


61

คำตอบนี้ดูเหมือนจะใช้งานไม่ได้อีกต่อไปเนื่องจากมีการเปลี่ยนแปลงบางอย่างกับการใช้งาน UITableView ตั้งแต่ที่เขียนคำตอบ ดูความคิดเห็นนี้: รับการแจ้งเตือนเมื่อ UITableView ขอข้อมูลเสร็จแล้ว?

ฉันได้รับเล่นกับปัญหานี้สำหรับคู่ของวันและคิดว่า subclassing UITableView's reloadDataเป็นวิธีที่ดีที่สุด:

- (void)reloadData {

    NSLog(@"BEGIN reloadData");

    [super reloadData];

    NSLog(@"END reloadData");

}

reloadDataไม่สิ้นสุดก่อนที่ตารางจะโหลดข้อมูลซ้ำเสร็จสิ้น ดังนั้นเมื่อเริ่มการทำงานครั้งที่สองNSLogมุมมองตารางจึงเสร็จสิ้นการขอข้อมูล

ผมเคย subclassed จะส่งวิธีการเพื่อให้ผู้ร่วมประชุมก่อนและหลังUITableView reloadDataมันใช้งานได้เหมือนมีเสน่ห์


2
ต้องบอกว่านี่เป็นทางออกที่ดีที่สุด เพิ่งใช้งานและอย่างที่คุณพูดมันใช้งานได้เหมือนมีเสน่ห์ ขอบคุณ!
ทฤษฎี

2
@EricMORAND คุณบอกว่า "reloadData ไม่จบก่อนที่ตารางจะโหลดข้อมูลซ้ำ" คุณช่วยชี้แจงได้ไหมว่าคุณหมายถึงอะไร? ฉันพบว่าreloadDataผลตอบแทนทันทีและฉันเห็น "END reloadData" ก่อนที่เซลล์จะถูกโหลดใหม่จริง ๆ (กล่าวคือก่อนที่UITableViewDataSourceจะเรียกวิธีการ) การทดลองของฉันแสดงให้เห็นตรงกันข้ามกับสิ่งที่คุณพูด ฉันต้องเข้าใจผิดในสิ่งที่คุณพยายามจะพูด
Rob

3
คำตอบของ Eric ไม่เหมือนกับการไม่ใช้งาน (อ่านไม่ลบล้าง) reloaddata เรียก reloaddata (ซึ่งโดยพื้นฐานแล้วจะเป็น [super reloaddata] จากนั้นหลังจากเรียกมันแล้วให้ทำสิ่งที่คุณต้องการเมื่อเสร็จสิ้นหรือไม่
Nirav Bhatt

5
กาลครั้งหนึ่ง reloadData ทำกระบวนการโหลดให้เสร็จสิ้นก่อนที่จะสิ้นสุด แต่ Apple เปลี่ยนมันในบางจุด ตอนนี้คลาส UITableView สามารถแคชการเรียก reloadData ด้วยการแทรกแถวทั้งหมดและลบการโทร หากคุณดูการประกาศ @interface สำหรับ UITableView คุณจะพบสมาชิก NSMutableArray _reloadItems อยู่ใต้ _insertItems และ _deleteItems (ฉันต้องทำรหัสใหม่ที่ได้รับมาเนื่องจากการเปลี่ยนแปลงนี้)
Walt Sellers

3
การโพสต์รหัสเสร็จสิ้นในบล็อกบนคิวหลักหลังจากโทร[super reloadData]ใช้ได้ผลสำหรับฉัน: dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"reload complete");});. reloadDataมันเป็นพื้นก้าวกระโดดกบบล็อกที่ได้รับการโพสต์โดยมุมมองตารางใน
Timothy Moose

53

ฉันมีสถานการณ์เดียวกันในแอพของฉันและคิดว่าจะโพสต์คำตอบให้พวกคุณเพราะคำตอบอื่น ๆ ที่กล่าวถึงที่นี่ใช้ไม่ได้กับฉันสำหรับ 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)

})

ดังนั้นวิธีการทำงาน

โดยทั่วไปเมื่อคุณทำการรีโหลดเธรดหลักจะไม่ว่างดังนั้นในขณะนั้นเมื่อเราทำการจัดส่งเธรดแบบ async บล็อกจะรอจนกว่าเธรดหลักจะเสร็จสิ้น ดังนั้นเมื่อโหลด tableview เสร็จสมบูรณ์เธรดหลักจะเสร็จสิ้นและจะส่งบล็อกวิธีการของเรา

ทดสอบใน iOS7 และ iOS8แล้วมันใช้งานได้ยอดเยี่ยม;)

อัปเดตสำหรับ iOS9: ใช้งานได้ดีคือ iOS9 ด้วย ฉันได้สร้างโครงการตัวอย่างใน github เป็น POC https://github.com/ipraba/TableReloadingNotifier

ฉันแนบภาพหน้าจอการทดสอบของฉันที่นี่

สภาพแวดล้อมที่ทดสอบ: iOS9 iPhone6 ​​จำลองจาก Xcode7

ใส่คำอธิบายภาพที่นี่


8
ชุดที่ดีที่สุดที่ฉันพบ!
Nikolay Shubenkov

@ ฉันได้ทำการทดสอบใน iOS9 แล้วและมันก็ใช้งานได้ดี โปรดดูที่github.com/ipraba/TableReloadingNotifier
ipraba

มันใช้งานได้ดีกับโปรแกรมจำลองของฉัน แต่ดูเหมือนจะไม่ทำงานบนอุปกรณ์จริง มีใครมีปัญหานี้อีกไหม
sosale151

1
ในที่สุดโซลูชันนี้ก็ใช้ได้ผลสำหรับฉัน! ใช้งานได้ดีบน iOS 9
แข็งแกร่ง

1
ฉันได้ทดสอบกับอุปกรณ์จริง iPhone 6s มันทำงานได้ดีเช่นกัน
Strong

25

แก้ไข: คำตอบนี้ไม่ใช่วิธีแก้ปัญหา ดูเหมือนว่าจะใช้งานได้ในตอนแรกเนื่องจากการโหลดซ้ำอาจเกิดขึ้นได้ค่อนข้างเร็ว แต่ในความเป็นจริงแล้วบล็อกการเสร็จสมบูรณ์ไม่จำเป็นต้องถูกเรียกใช้หลังจากโหลดข้อมูลเสร็จสมบูรณ์แล้ว - เนื่องจาก 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
                                        }];

2
ฉันไม่สามารถรับบล็อกได้เพียงพอ ใครต้องการผู้รับมอบสิทธิ์เมื่อคุณมีบล็อกที่ดี!
bandejapaisa

ฉันชอบสิ่งนี้ แต่เวลาที่ระบบเรียก reloadData () เช่นเมื่อตารางแสดงครั้งแรกล่ะ?
Symmetric

12
นี่ไม่ใช่โซลูชันบล็อกเสร็จสิ้นจะเริ่มดำเนินการก่อนที่ cellForRowAtIndexPath
zvjerka24

1
สิ่งนี้จะไม่ทำงาน reloadData ไม่ใช่วิธีการบล็อก รหัสนี้ถูกเรียกทันทีหลังจากเรียก reloadData แม้ว่าเซลล์จะยังไม่โหลดซ้ำก็ตาม นอกจากดูที่รหัสนี้และคุณจะเห็นว่าคุณอาจมีเพียงใส่รหัสของคุณหลังจาก reloadData
colinta

1
สิ่งนี้ใช้ได้ผลกับฉันด้วยการเปลี่ยนแปลงเล็กน้อย dispatch_async(dispatch_get_main_queue(), ^{completionBlock();});แทนที่จะเรียกบล็อกเสร็จโดยตรงเรียกว่าในบล็อกโพสต์บนคิวหลัก: reloadDataนี้โดยทั่วไปกระโดดกบบล็อกโพสต์โดยมุมมองตารางใน
Timothy Moose

11

reloadData เพียงแค่ขอข้อมูลสำหรับเซลล์ที่มองเห็นได้ กล่าวว่าจะได้รับการแจ้งเตือนเมื่อโหลดส่วนที่ระบุของตารางของคุณโปรดขอ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
}

1
งานนี้ผมไม่แน่ใจว่า ดัชนีสุดท้ายคือส่วนท้ายของข้อมูลตาราง (เช่น 100 ระเบียน) แต่ตารางจะแสดงเฉพาะสิ่งที่มองเห็นได้บนหน้าจอ (เช่น 8 ระเบียน)
Travis M.

10

นั่นคือทางออกของฉัน ใช้งานได้ 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

คล้ายกับวิธีแก้ปัญหาของ Josh Brownโดยมีข้อยกเว้นหนึ่งข้อ ไม่จำเป็นต้องมีการหน่วงเวลาในวิธีการ performSelector ไม่ว่าreloadDataจะใช้เวลานานแค่ไหน. tableViewDidLoadData:มักจะยิงเมื่อเสร็จสิ้นการถามtableViewdataSource cellForRowAtIndexPath

แม้ว่าคุณจะไม่ต้องการคลาสย่อยUITableViewคุณก็สามารถโทรได้[performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]และตัวเลือกของคุณจะถูกเรียกทันทีหลังจากที่ตารางโหลดซ้ำเสร็จสิ้น แต่คุณควรตรวจสอบให้แน่ใจว่ามีการเรียกตัวเลือกเพียงครั้งเดียวต่อการโทรเพื่อreloadData:

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

สนุก. :)


1
การใช้การโทรแบบ performSelector นั้นยอดเยี่ยม เรียบง่ายและใช้งานได้ดี
Julia

นี่มันสุดยอดมาก ฉันทะเลาะกับเรื่องนี้มาหลายวันแล้ว ขอบคุณ!
David Carrico

@MarkKryzhanouski คุณทดสอบบน iOS 9 แล้วหรือยัง?
วิกเตอร์

@MarkKryzhanouski ขอบคุณสำหรับข้อเสนอแนะ! ใช้งานได้ดีบน iOS 7 และ 8
Victor

การแก้ปัญหาperformSelectorหรือการดำเนินการในหัวข้อหลักที่มีdispatch_asynchไม่ทำงานบนiOS 9
Manuel

8

นี่คือคำตอบของคำถามที่แตกต่างกันเล็กน้อย: ฉันต้องการที่จะรู้ว่าเมื่อมีการโทรยังสำเร็จรูปUITableView cellForRowAtIndexPath()ฉันซับคลาสlayoutSubviews()(ขอบคุณ @Eric MORAND) และเพิ่มการโทรกลับของผู้ร่วมประชุม:

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

การใช้งาน:

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

หมายเหตุ: เนื่องจากนี่เป็นคลาสย่อยUITableViewที่มีคุณสมบัติของผู้รับมอบสิทธิ์อยู่แล้วจึงMyTableViewControllerไม่จำเป็นต้องเพิ่มอีก "@dynamic delegate" บอกให้คอมไพเลอร์ใช้คุณสมบัตินี้ (นี่คือลิงค์ที่อธิบายสิ่งนี้: http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/ )

UITableViewคุณสมบัติในMyTableViewControllerต้องมีการเปลี่ยนแปลงที่จะใช้ใหม่SDTableViewระดับ สิ่งนี้ทำได้ใน Interface Builder Identity Inspector เลือกUITableViewภายในUITableViewControllerและการตั้งค่าของมัน "ชั้นเอง" SDTableViewเพื่อ


คุณตั้งค่า MyTableViewController ของคุณให้เป็นผู้รับมอบสิทธิ์สำหรับ SDTableView ที่ไหน เป็นไปได้อย่างไรที่จะตั้งค่าคุณสมบัติของชื่อ "delegate" บน SDTableView เมื่อ superclass มีคุณสมบัติของชื่อนั้นอยู่แล้ว (UITableView.delegate) คุณจะเปลี่ยน SDTableView ที่กำหนดเองของคุณไปยังคุณสมบัติ MyTableViewController.tableView ได้อย่างไรเมื่อคุณสมบัติเป็นประเภท "UITablewView" และวัตถุ (อินสแตนซ์ของ SDTableView) เป็นประเภท "SDTableView" ฉันกำลังต่อสู้กับปัญหาเดียวกันดังนั้นฉันหวังว่าจะมีวิธีแก้ไขปัญหาเหล่านี้ :)
Earl Grey

ในโค้ดของ Symmetric (SDTableView) ไม่ควรตั้งค่า reloadInProgress เป็น FALSE ใน didReloadData แทน didLayoutSubviews? เนื่องจาก reloadData ถูกเรียกใช้หลังจาก layoutSubviews และไม่ควรพิจารณาการโหลดเสร็จก่อน reloadData จะเสร็จสิ้น
tzuchien.chiu

นี้ไม่ได้ทำงานสำหรับ iOS 8 มีการรวมคำตอบของ iPrabu
Stunner

6

ฉันได้พบสิ่งที่คล้ายกันที่จะได้รับการแจ้งเตือนสำหรับการเปลี่ยนแปลงในของcontentSize TableViewฉันคิดว่าควรใช้งานได้ที่นี่เช่นกันเนื่องจาก contentSize เปลี่ยนไปพร้อมกับการโหลดข้อมูล

ลองสิ่งนี้:

ในการviewDidLoadเขียน

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

และเพิ่มวิธีนี้ใน viewController ของคุณ:

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


    }
}

คุณอาจต้องปรับเปลี่ยนเล็กน้อยในการตรวจสอบการเปลี่ยนแปลง สิ่งนี้ได้ผลสำหรับฉันแม้ว่า

ไชโย! :)


1
ฉลาดมาก. ทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน ขอบคุณสำหรับการแบ่งปัน!
DZenBot

2
สวยหรู! สิ่งเดียวที่ฉันจะเพิ่มคือคุณต้องลบตัวคุณเองและผู้สังเกตการณ์ออก (อาจจะใช้วิธี -dealloc) หรือเพิ่มตัวเองในฐานะผู้สังเกตการณ์ใน-viewWillAppearและเอาตัวเองใน-viewWillDisapearวิธีการ
Loozie

2

นี่เป็นวิธีแก้ปัญหาที่เป็นไปได้แม้ว่าจะเป็นการแฮ็ก:

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

ไหนคุณวิธีการเลื่อนมุมมองตารางที่มี-scrollTableView -scrollRectToVisible:animated:และแน่นอนคุณสามารถกำหนดค่าความล่าช้าในรหัสด้านบนจาก 0.3 เป็นอะไรก็ได้ที่ดูเหมือนจะเหมาะกับคุณ ใช่มันแฮ็คอย่างน่าขัน แต่มันใช้ได้กับ iPhone 5 และ 4S ของฉัน ...


2
อาจดูเหมือน "แฮ็ก" แต่นี่เป็นแนวทางมาตรฐานสำหรับการเลื่อน: เลื่อนไปที่รอบการรันถัดไป ฉันจะเปลี่ยนการหน่วงเวลาเป็น 0 เนื่องจากมันใช้งานได้ดีและมีเวลาแฝงน้อยกว่า
phatmann

ไม่ได้ผลสำหรับฉันเมื่อตั้งค่าความล่าช้าเป็น 0; 0.3 ต่ำที่สุดที่ฉันทำได้และยังได้รับข้อมูล
Josh Brown

@phatmann สิ่งนี้ได้ผลสำหรับฉัน ฉันยังสามารถใช้ 0 สำหรับความล่าช้าของฉัน ขอบคุณทั้งสองท่าน
mcphersonjr

1

ฉันมีบางอย่างที่คล้ายกันฉันเชื่อ ฉันเพิ่ม BOOL -viewWillAppear:เป็นตัวแปรเช่นที่บอกฉันว่าชดเชยได้รับการบูรณะและตรวจสอบว่าใน เมื่อยังไม่ได้รับการกู้คืนฉันจะคืนค่าด้วยวิธีการนั้นและตั้งค่า BOOL เพื่อระบุว่าฉันกู้คืนค่าชดเชยแล้ว

มันเป็นการแฮ็กและมันอาจจะทำได้ดีกว่านี้ แต่มันใช้ได้กับฉันในขณะนี้


ใช่ปัญหาคือ viewWillAppear ดูเหมือนจะเร็วเกินไป (อย่างน้อยก็ในสถานการณ์ของฉัน) การพยายามกู้คืนออฟเซ็ตใน viewWillAppear จะไม่ทำอะไรเลยเว้นแต่ฉันจะเพิ่มแฮ็คของการเรียก reloadData ก่อน ตามที่ฉันเข้าใจแล้ว viewWillAppear / viewDidAppear อ้างถึงเฉพาะมุมมองตารางที่ปรากฏขึ้นจริง - พวกเขาไม่ได้ทำการอ้างสิทธิ์ใด ๆ เกี่ยวกับว่าเซลล์ตารางในมุมมองได้รับการแจกแจงหรือเติมข้อมูล ... และสิ่งนี้จะต้องเกิดขึ้นก่อนที่คุณจะสามารถกู้คืนได้ ออฟเซ็ตมิฉะนั้นคุณจะกู้คืนออฟเซ็ตในมุมมองที่ว่างเปล่า (และฉันเข้าใจว่าทำไมถึงไม่ได้ผล!)
kennethmac2000

แต่บางทีคุณอาจหมายความว่าคุณกำลังบังคับให้โหลดเซลล์ตารางก่อนโดยเรียก reloadData ภายใน viewWillAppear?
kennethmac2000

ไม่ฉันไม่ได้บังคับให้โหลดซ้ำ เมื่อฉันมีปัญหาฉันพยายามเรียกคืนค่าชดเชยใน-viewDidLoad(ซึ่งมันควรจะเกิดขึ้นแน่นอน) แต่จะได้ผลก็ต่อเมื่อฉันตั้งค่าออฟเซ็ตอนิเมชั่น โดยการย้ายการตั้งค่าออฟเซ็ตไป-viewWillAppear:ใช้งานได้ แต่ฉันต้องคงค่าสถานะไว้เพื่อตั้งค่าเพียงครั้งเดียว -loadViewฉันคิดว่ามุมมองตารางโหลดข้อมูลครั้งเดียวเพิ่มไปยังมุมมองเพื่อให้มีอยู่แล้วใน คุณแน่ใจหรือไม่ว่าคุณมีข้อมูลของคุณในการโหลดการดู หรือมันถูกโหลดในเธรดแยกต่างหากหรืออะไร?
Joost

โอเคบางทีคุณอาจช่วยฉันเข้าใจที่นี่ นี่คือวิธีที่ฉันเข้าใจลำดับของการโทรเมื่อสร้าง UITableView 1) viewDidLoad เริ่มทำงานก่อน สิ่งนี้บ่งชี้ว่า UITableView ถูกโหลดลงในหน่วยความจำ 2) viewWillAppear อยู่ถัดจากไฟ สิ่งนี้บ่งชี้ว่า UITableView จะปรากฏขึ้น แต่ไม่จำเป็นว่าออบเจ็กต์ UITableViewCell ที่มองเห็นได้ทั้งหมดจะถูกสร้างอินสแตนซ์ / เรนเดอร์เสร็จสิ้น
kennethmac2000

2
และทั้งหมดข้างต้นเป็นความจริงคำถามก็คือเรามีตัวเลือกใดบ้างที่ไม่แฮ็กกี้ในการค้นหาว่าเมื่อใดที่ UITableView ได้รับข้อมูลเพียงพอจากแหล่งข้อมูลเพื่อรับประกันว่าคำขอเลื่อน (เช่น scrollRectToVisible: animated: call ) จะใช้งานได้จริง (ไม่ใช่แค่ไม่ทำอะไรเลย)?
kennethmac2000

1

ดูเหมือนว่าคุณต้องการอัปเดตเนื้อหาของเซลล์ แต่หากไม่มีการกระโดดอย่างกะทันหันซึ่งอาจมาพร้อมกับการแทรกและการลบเซลล์

มีบทความมากมายเกี่ยวกับการทำเช่นนั้น นี่คือหนึ่ง

ฉันขอแนะนำให้ใช้ setContentOffset: animated: แทน scrollRectToVisible: animated: สำหรับการตั้งค่าพิกเซลที่สมบูรณ์แบบของมุมมองเลื่อน


1

คุณสามารถลองใช้ตรรกะต่อไปนี้:

-(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 เป็นศูนย์ ชอบ:

prevIndexPath = nil;
[mainTableView reloadData];

ฉันทดสอบด้วย NSLogs และตรรกะนี้ก็โอเค คุณสามารถปรับแต่ง / ปรับปรุงได้ตามต้องการ


0

ในที่สุดฉันก็ทำให้รหัสของฉันใช้งานได้ -

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

มีบางสิ่งที่ต้องดูแล -

  1. โทรภายใน " - (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath"
  2. เพียงตรวจสอบให้แน่ใจว่าข้อความ "scrollToRowAtIndexPath" ถูกส่งไปยังอินสแตนซ์ที่เกี่ยวข้องของ UITableView ซึ่งแน่นอนว่าเป็น MyTableview ในกรณีนี้
  3. ในกรณีของฉัน UIView คือมุมมองที่มีอินสแตนซ์ของ UITableView
  4. นอกจากนี้สิ่งนี้จะถูกเรียกสำหรับการโหลดเซลล์ทุกครั้ง ดังนั้นให้สร้างตรรกะภายใน "cellForRowAtIndexPath" เพื่อหลีกเลี่ยงการเรียก "scrollToRowAtIndexPath" มากกว่าหนึ่งครั้ง

และตรวจสอบให้แน่ใจว่าไม่ได้เรียกชิ้นส่วนนี้มากกว่าหนึ่งครั้ง หากไม่ล็อกการเลื่อน
smile.al.d.way

วิธีนี้อาจใช้งานได้ แต่ก็ไม่สวยหรูและไม่ใช่วิธีแก้ปัญหาที่ฉันกำลังมองหา น่าเสียดายที่ดูเหมือนจะไม่มีวิธีที่ดีกว่าในการตรวจสอบว่า -reloadData รับข้อมูลเสร็จแล้วหรือไม่ ...
Josh Brown

0

คุณสามารถปรับขนาด tableview ของคุณหรือกำหนดขนาดเนื้อหาด้วยวิธีนี้เมื่อโหลดข้อมูลทั้งหมด:

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

ฉันเพิ่งเรียกใช้ตัวจับเวลาที่กำหนดเวลาซ้ำและทำให้ไม่ถูกต้องก็ต่อเมื่อ contentSize ของตารางใหญ่ขึ้นเมื่อความสูงของ tableHeaderView (หมายถึงมีเนื้อหาเป็นแถวในตาราง) รหัสใน 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;
                    }
                }));
        }
    }

0

ไม่ได้ถูกUITableView layoutSubviewsเรียกก่อนที่มุมมองตารางจะแสดงเนื้อหาใช่หรือไม่ ฉันสังเกตเห็นว่ามีการเรียกใช้เมื่อมุมมองตารางโหลดข้อมูลเสร็จแล้วบางทีคุณควรตรวจสอบในทิศทางนั้น


0

ตั้งแต่ iOS 6 เป็นต้นไปUITableviewวิธีการมอบหมายที่เรียกว่า:

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

จะดำเนินการเมื่อตารางของคุณรีโหลดสำเร็จ คุณสามารถปรับแต่งได้ตามต้องการในวิธีนี้


0

ทางออกที่ดีที่สุดที่ฉันพบใน Swift

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

-1

ทำไมไม่ขยาย?

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

ไม่ได้ทดสอบกับข้อมูลจำนวนมาก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.