2012-06-04 13 views
51

Próbuję załadować przeanalizowane dane w komórkach, ale problem polega na tym, że dzieje się to synchronicznie, a usługa UitableView nie jest wyświetlana do momentu zakończenia ładowania danych. Próbowałem rozwiązać problem za pomocą performSelectorInBackground, ale teraz dane nie są ładowane w komórkach, dopóki nie zacznę przewijania. Tu jest mój kodu:Dane nie ładują się w widoku UITableView do czasu przewinięcia

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view, typically from a nib. 

    [self performSelectorInBackground:@selector(fethchData) withObject:nil]; 


} 


- (void)viewDidUnload 
{ 
    [super viewDidUnload]; 
    // Release any retained subviews of the main view. 
self.listData = nil; 
    self.plot=nil; 
} 


-(void) fethchData 

{ 
    NSError *error = nil; 
    NSURL *url=[[NSURL alloc] initWithString:@"http://www.website.com/"]; 
    NSString *strin=[[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; 

    HTMLParser *parser = [[HTMLParser alloc] initWithString:strin error:&error]; 

    if (error) { 
     NSLog(@"Error: %@", error); 
     return; 
    } 

    listData =[[NSMutableArray alloc] init]; 
    plot=[[NSMutableArray alloc] init]; 

    HTMLNode *bodyNode = [parser body]; 
    NSArray *contentNodes = [bodyNode findChildTags:@"p"]; 


    for (HTMLNode *inputNode in contentNodes) { 
      [plot addObject:[[inputNode allContents] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];  
     } 


    NSArray *divNodes = [bodyNode findChildTags:@"h2"]; 

    for (HTMLNode *inputNode in divNodes) { 

      [listData addObject:[[inputNode allContents] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];   

     } 
    } 


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

    static NSString *CellIdentifier = @"Cell"; 
    //here you check for PreCreated cell. 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (cell == nil) { 
     cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; 
     cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 

    } 

    //Fill the cells... 


    cell.textLabel.text = [listData objectAtIndex:indexPath.row]; 
    cell.textLabel.font = [UIFont boldSystemFontOfSize:14]; 
    cell.textLabel.numberOfLines=6; 
    cell.textLabel.textColor=[UIColor colorWithHue:0.7 saturation:1 brightness:0.4 alpha:1]; 


    cell.detailTextLabel.text=[plot objectAtIndex:indexPath.row]; 
    cell.detailTextLabel.font=[UIFont systemFontOfSize:11]; 
    cell.detailTextLabel.numberOfLines=6; 

    return cell; 


} 
+0

tylko widoczne komórki są ładowane komórki reszta zostanie ładowane, gdy pojawią się po przewinięciu. – rishi

Odpowiedz

123

Put to gdzieś po dane są ładowane powodzeniem:

dispatch_async(dispatch_get_main_queue(), ^{ 
    [self.tableView reloadData]; 
}); 

to rozwiązać problem nazywając aktualizację GUI gdy nie jesteś w głównym wątku.

Ten kod wykorzystuje technologię GCD firmy Apple, aby wymusić działanie funkcji ponownego ładowania w głównym wątku. Przeczytaj więcej o Concurrency Programming Guide dla lepszego zrozumienia (to dość duże pole, więc trudno to wyjaśnić w komentarzu). W każdym razie nie jest to zalecane, jeśli nie rozumiesz tego dobrze, ponieważ powoduje awarię programu w rzadkich przypadkach.

+0

Dzięki. Pośpiesznie przeniosłem niektóre rzeczy dookoła i nie zdawałem sobie sprawy, że moje ładowanie danych było w innym wątku, a tym samym również moje dane ponownego ładowania. Dziękuję, że pomogłeś mi wrócić i przeanalizować rzeczy. – chadbag

+0

Dziękujemy! To właśnie rozwiązało naprawdę irytujący błąd, który miałem. –

+1

OGROMNA POMOC! dzięki – Greg

9

Wszystko, co naprawdę trzeba zrobić, to za każdym razem masz aktualizację danych back-end, zadzwoń

[tableView reloadData]; 

Ponieważ dzieje się synchronicznie, powinieneś mieć funkcja jak

-(void) updateTable 
{ 
    [tableView reloadData]; 
} 

i po dodaniu danych w wywołaniu pobierania

[self performSelectorOnMainThread:@selector(updateTable) withObject:nil waitUntilDone:NO]; 
+0

mówi „nieznane odbiornik tableView” – Benjamen

+0

Kod ten służy jako przykład, nie wiem dokładnie strukturę swojej klasy lub widoku układu. Chodzi o to, trzeba się trzymać z odniesieniem do widoku tabeli (jeśli jest instancją klasy lub podklasy UITableViewController, należy po prostu mieć self.tableView) i wywołać metodę 'reloadData' na nim –

+0

W UITabbleviewController będzie ** self.tableView ** – LavaSlider

1

Miałem dokładnie ten sam problem! Chciałem, aby UITableView był w pełni wypełniony przed pojawieniem się kontrolera widoku. Stanowisko Envila dało mi potrzebne informacje, ale moje rozwiązanie okazało się inne.

Oto, co zrobiłem (przebudowano, aby pasował do kontekstu pytającego).

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    [self performSelectorInBackground:@selector(fethchData) withObject:nil]; 
} 

- (void)viewWillAppear { 
    [tableView reloadData]; 
} 
2

U można użyć [cell setNeedsDisplay]; na przykład:

dispatch_async(dispatch_get_main_queue(), ^{ 
      [cell setNeedsDisplay]; 
      [cell.contentView addSubview:yourView]; 
     }); 
1

Miałem ten problem i miałem do czynienia z tym przez cały dzień.

Używam komórek statycznych i ponowne ładowanieData powoduje nieprawidłowe ładowanie, wyświetla tylko widoczne komórki i usuwa pozostałe. Zauważyłem, że kiedy przewijałem w dół (y w wartości ujemnej) komórki, które zostały poprawnie załadowane, napisałem ten kod i zadziałało, nawet jeśli nie lubię tego w ten sposób.

Strzelaj, jeśli znajdziesz lepsze rozwiązanie.

-(void)reloadTableView{ 
     CGPoint point = self.tableSettings.tableView.contentOffset; 
     [self.tableSettings.tableView reloadData]; 

     [UIView animateWithDuration:.001f animations:^{ 
      [self.tableSettings.tableView setContentOffset:CGPointMake(point.x, -10)]; 
     } completion:^(BOOL finished) { 
      [UIView animateWithDuration:.001f animations:^{ 
       [self.tableSettings.tableView setContentOffset:point]; 
      } completion:^(BOOL finished) { 

      }]; 
     }]; 
} 
+0

To zadziałało dla mnie. Daj mi znać, jeśli zorientowali się rzeczywisty problem –

13

Dla szybkiej 3:

DispatchQueue.main.async(execute: {() -> Void in 
       self.tableView.reloadData() 
      }) 

Dla Swift 2:

dispatch_async(dispatch_get_main_queue(), {() -> Void in 
       self.tableView.reloadData() 
      }) 
+0

bro Swift 3 działa dobrze !! ... Dzięki –

+0

@MayPhyu zapraszamy :) – Hamid

0

Po pierwsze, to pół-rozwiązany mój związany problem. Chcę zaokrąglić rogi obrazu w komórce tabeli. Dispatching asynchronicznie naprawił problem dla niektórych, ale nie wszystkich obrazów. Jakieś pomysły?

Po drugie, myślę, że powinni unikać tworzenia silnego cyklu odniesienia za pomocą listy przechwytywania zamknięcia tak:

DispatchQueue.main.async(execute: { [weak weakSelf = self]() -> Void in 
      weakSelf!.tableView.reloadData() 
     }) 

Patrz: https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID52

+0

to ustalone mój problem: DispatchQueue.main.async (wykonanie: {[słaby weakCell = komórka]() -> Void w weakCell! .imageView! .layer.cornerRadius = cell.imageView! .frame.width/4.0 weakCell! .imageView! .clipsToBounds = true }) – user3590685

Powiązane problemy