2015-08-13 12 views
22

Podobnie jak wspomina tytuł pytaniem jest: Jaka jest różnica między "cellForRowAtIndexPath" i "willDisplayCell: forRowAtIndexPath:" `iOS UITableView: jaka jest różnica między "cellForRowAtIndexPath" i "willDisplayCell: forRowAtIndexPath:"

myślę konfiguracja komórka może odbywać się albo w cellForRowAtIndexPath lub willDisplayCell: forRowAtIndexPath: „!

+0

podobna do 'viewWillAppear' i' viewDidlAppear'. –

+0

Dobra odpowiedź! Obaj są tacy podobni! – mayqiyue

+0

Zasadniczo udostępniasz komórkę systemowi w 'cellForRowAtIndexPath' (konfiguracje są do ciebie), ale' willDisplayCell: forRowAtIndexPath: 'jest sposobem, gdy system wysyła komórkę do ciebie, jeśli chcesz dokonać ostatniej zmiany czasu (znowu do ciebie). –

Odpowiedz

16

masz rację, konfiguracja komórka może (teoretycznie) być wykonane w obu metodach.

jednak prawie wszystkie UITableView mieć źródło danych, które implementuje cellForRowAtIndexPath: (jest to wymagana metoda w protokole). Z drugiej strony, willDisplayCell:forRowAtIndexPath: (który jest metodą dla delegata , a nie dla źródła danych) jest opcjonalny.

jak konfigurowanie komórka jest zwykle zależna od danych chcesz pokazać, cellForRowAtIndexPath: jest zdecydowanie najczęstszym miejscem do konfiguracji komórki. (Nie pamiętam nawet korzystania z willDisplayCell:forRowAtIndexPath:).

Jest jednym chlubnym wyjątkiem: gdy używasz storyboard i statycznych komórki (zamiast prototypów komórkowych), nie można coś pożytecznego zrobić w cellForRowAtIndexPath: (bo dequeueReusableCellWithIdentifier: zwrotów nil), więc trzeba zrobić konfigurację w willDisplayCell:forRowAtIndexPath:, viewWillAppear: lub innych metod.

@NSDeveloper: masz rację. Dzięki za podpowiedź.

+5

użyj [super tableView: tableView cellForRowAtIndexPath: indexPath] zamiast usuwać dane ... aby uzyskać statyczną komórkę. – NSDeveloper

+1

Staje się ważny w iOS 10, gdzie collectionView (domyślnie) wstępnie pobiera komórki z góry. Jak mówi Apple: Uwaga: Po włączeniu pobierania wstępnego metoda collectionView: cellForItemAtIndexPath: w widoku kolekcji zostaje wywołana z wyprzedzeniem, gdy komórka jest wymagana. Aby uniknąć niespójności w wyglądzie wizualnym, użyj collectionView: willDisplayCell: forItemAtIndexPath: deleguj metodę do zaktualizuj komórkę, aby odzwierciedlić stan wizualny, taki jak wybór. – Tumata

16

cellForRowAtIndexPath powinien faktycznie zwrócić instancję komórki. Kiedy to możliwe, powinna być ponownie używana komórka. Ta metoda jest wymagana w przypadku protokołu UITableViewDataSource. Zazwyczaj jest to miejsce, w którym wybierasz dane, które będą wyświetlane w komórce. W przypadku komórek dynamicznych często ustawia się właściwości interfejsu użytkownika, takie jak stan wybrany w tym samym czasie, gdy ustawiane są tutaj dane.

willDisplayCell jest opcjonalna i jest wywoływana po. To Twoja ostatnia szansa na dostosowanie komórki przed jej wyświetleniem. W tym momencie instancja komórki została już utworzona. Możesz tutaj zmienić takie rzeczy jak wybrany stan itp. Nie powinieneś zmieniać danych/struktury komórki ani tworzyć niczego nowego, a jedynie zmieniać stan właściwości interfejsu dla komórki. Jest to powszechnie stosowane w przypadku komórek statycznych.

+0

Przykładowy kod Lister jabłek jest dobrym przykładem takiego wykorzystania: https://developer.apple.com/library/ios/samplecode/Lister/Introduction/Intro.html A jest też fajny blog na ten temat http: // blog.lazerwalker.com/objective-c/code/2013/12/01/a-tale-of-two-protocols.html – coco

+0

Po prostu drobna literówka, 'cellForRowAtIndexPath' należy do' UITableViewDataSource', a nie 'UITableViewDelegate'. –

+0

@ShivaHuang, dzięki. Wprowadziłem poprawkę. –

17

myślę, że odpowiedzi na swoje pytanie:

Ale bardzo ważną rzeczą jest nadal istnieje: tableView:cellForRowAtIndexPath: metody, które powinny być realizowany w źródle danych z UITableView, zwany dla każdej komórki i powinny działać szybko. Musisz zatem możliwie jak najszybciej ponownie użyć instancji komórki ponownie jako .

Nie wykonuj wiązania danych w tym miejscu, ponieważ nie ma jeszcze komórki na ekranie. W tym celu można użyć metody tableView:willDisplayCell:forRowAtIndexPath:, która może być zaimplementowana w delegacie UITableView. Metoda zwana dokładnie przed wyświetleniem komórki w granicach UITableView.

From Perfect smooth scrolling in UITableViews

+4

Czy istnieją dane eksperymentalne potwierdzające różnicę między tymi dwoma? –

+1

W iOS 9 nie ma różnicy, ale może być różnica od iOS 10. https://tech.zalando.com/blog/proper-use-of-cellforrowatindexpath-and-willdisplaycell/ – jamesk

+1

Nie sądzę powiązanie danych w 'tableView: willDisplayCell: forRowAtIndexPath:' pomaga. Testowałem, nie widziałem żadnej różnicy. Naprawiłem "warstwę mieszaną w kolorze", odznaczono "czysty kontekst", sprawdziłem "nieprzejrzysty" na wszystkim i żaden z nich nie zadziałał. Myślę, że problem polega na tym, że 'autolayout' jest zbyt wolny. –

1

I w obliczu problemu z konfiguracją komórek w willDisplayCell: stosowany autoLayout i UITableViewAutomaticDimension. Wysokości ponownie użytych komórek nie zostały obliczone poprawnie. Metody drukowania w NSLog pokazuje, że willDisplayCell: nazywa po heightForRowAtIndexPath: (testowane na iOS 10,2 symulator)

cellForRowAtIndexPath: <NSIndexPath: 0x7c10daa0> {length = 2, path = 1 - 0} 
heightForRowAtIndexPath: <NSIndexPath: 0x7c10daa0> {length = 2, path = 1 - 0} 
heightForRowAtIndexPath: <NSIndexPath: 0x7c10daa0> {length = 2, path = 1 - 0} 
willDisplayCell: <NSIndexPath: 0x7c10daa0> {length = 2, path = 1 - 0} 
cellForRowAtIndexPath: <NSIndexPath: 0x7c4516f0> {length = 2, path = 1 - 1} 
heightForRowAtIndexPath: <NSIndexPath: 0x7c4516f0> {length = 2, path = 1 - 1} 
heightForRowAtIndexPath: <NSIndexPath: 0x7c4516f0> {length = 2, path = 1 - 1} 
willDisplayCell: <NSIndexPath: 0x7c4516f0> {length = 2, path = 1 - 1} 

myślę, że to było powodem, ponieważ problem ten zniknął po umieszczeniu kodu konfiguracyjnego w cellForRowAtIndexPath:

PS: Jest przeciwieństwem artykuł do łącza i środki zaksięgowane przez @iTSangar: https://tech.zalando.com/blog/proper-use-of-cellforrowatindexpath-and-willdisplaycell/

0

Wbrew temu, co może się wydawać intuicyjne, komórkę willDisplay: nazywa natychmiast po cellForRowAt indexPath: nazywa.

Posiadam aplikację, w której obrazy i filmy będą ładowane z adresu URLCache lub pobrane i wyświetlone w komórce. Zauważyłem, że za każdym razem, gdy uruchomiłem aplikację, wszystkie filmy i obrazy ładowały się, zanim mogłem je zobaczyć, i zauważyłem to, ponieważ mogłem usłyszeć dźwięk z ostatniego wideo w tableView, patrząc na pierwszy element w tableView. Jest to z 8 pozycji w tableView. Wydrukowałem na konsolę, aby lepiej zrozumieć kolejność wywołań funkcji delegatów.

Wyniki:

  • Utworzono komórki w rzędzie 0
    • Drew komórek pasza rzędu 0
  • Utworzono komórki w rzędzie 1
    • Drew komórek paszy w rzędzie 1
  • Utworzony komórek w rzędzie 2
    • Drew komórek pasza rzędu 2
  • Utworzono komórki w rzędzie 3
    • Drew komórek pasza wiersz 3
  • Utworzono komórka w rzędzie 4
    • Drew komórek pasza rzędu 4
  • Utworzono komórki w rzędzie 5
    • Drew komórek pasza rzędu 5
  • Utworzono komórki w rzędzie 6
    • Drew komórek pasza wiersz 6
  • Utworzono komórkę w rzędzie 7
    • Drew komórek pasza rzędu 7
  • Zatrzymano pokazano komórek w rzędzie 2
  • Zatrzymano pokazano komórki w rzędzie 3
  • Zatrzymano pokazano komórki w rzędzie 4
  • Zatrzymano pokazano komórki w rzędzie 5
  • Zatrzymano wyświetlanie komórki w wierszu 6
  • Zatrzymano wyświetlanie komórki w wierszu 7

Kod:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 
    let cell = tableView.dequeueReusableCell(withIdentifier: "feedCell", for: indexPath) as! FeedCell 
    print("Created cell at row \(indexPath.row)") 
    let post = posts[indexPath.row] 
    cell.post = post 
    cell.viewController = self 
    cell.configureCell() 
    return cell 
} 

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { 
    if let feedCell = cell as? FeedCell{ 
     print("Drew feed cell at row \(indexPath.row)") 
     // only want download task to start when the cell will display 
     if feedCell.post.isVideo { 
      feedCell.loadVideo() 
     } else { 
      feedCell.loadImage() 
     } 
    } 
} 

override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) { 
    print("Stopped showing cell at row \(indexPath.row)") 
    if let feedCell = cell as? FeedCell{ 
     feedCell.player?.pause() 
    } 
} 
Powiązane problemy