2013-06-05 12 views
17

Trochę utknąłem z tym ... każda pomoc jest bardzo doceniana. Sporo już czasu poświęciłem na debugowanie tego.UITableViewCell słaba wydajność z AutoLayout

Mam UITableView ze źródłem danych dostarczonym przez NSFetchedResultsController. W oddzielnym kontrolerze widoku wstawiam nowe rekordy do CoreData przy użyciu [NSEntityDescription insertNewObjectForEntityForName:inManagedObjectContext:], zapisz kontekst obiektu zarządzanego i odrzuć ten kontroler. Bardzo standardowe rzeczy.

Zmiany w kontekście zarządzanego obiektu są następnie odbierane przez NSFetchedResultsController:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { 
     [self.tableView beginUpdates]; 
} 

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { 
     [self.tableView endUpdates]; 
} 

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { 
    switch (type) { 
     case NSFetchedResultsChangeInsert: 
      [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationNone]; 

      break; 

     case NSFetchedResultsChangeDelete: 
      [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; 

      break; 

     case NSFetchedResultsChangeUpdate: 
      [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; 

      break; 

     case NSFetchedResultsChangeMove: 
      [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; 
      [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationNone]; 

      break; 
    } 
} 

I tu pojawia się problem - to trwa zbyt długo (około 3-4 sekund na iPhone 4), aby to zrobić. Wydaje się, że czas poświęcony jest na obliczanie układu komórek.

Usunąłem wszystko z komórki (w tym niestandardową podklasę) i zostawiłem tylko z UILabel, ale nic się nie zmieniło. Potem zmieniłem styl komórki na Basic (lub cokolwiek oprócz Custom), a problem zniknął - nowe komórki są dodawane natychmiastowo.

Podwajałem się, sprawdzając i wywołania zwrotne NSFetchedResultsControllerDelegate są wywoływane tylko raz. Jeśli je zignoruje i zrobię [UITableView reloadSections:withRowAnimation:] nic się nie zmieni - nadal jest bardzo powolny.

Wygląda na to, że automatyczne układanie jest wyłączone dla domyślnych stylów komórek, co czyni je bardzo szybkimi. Ale jeśli tak jest - dlaczego wszystko ładuje się szybko, gdy naciskam na UITableViewController?

Oto ślad wezwanie do tego problemu: stack trace

Więc pytanie jest - co tu się dzieje? Dlaczego komórki są renderowane tak wolno?

UPDATE 1

I został zbudowany bardzo prostą aplikację demo, który ilustruje problem mam. tutaj źródło - https://github.com/antstorm/UITableViewCellPerformanceProblem

Spróbuj dodać co najmniej ekran komórek, aby poczuć problemy z wydajnością.

Należy również pamiętać, że bezpośrednie dodanie wiersza (przycisk "Wstaw teraz!") Nie powoduje spowolnienia.

+2

Wystarczy, że potwierdzisz - Czy przypisujesz komórkom niestandardowym unikalny identyfikator i piszecie je zamiast ponownie tworzyć, prawda? – James

+0

i jakie reguły automatycznego układu są określone w komórce? – Wain

+0

Tak, przypisuję unikalny identyfikator i piszę komórki za pomocą '[UITableView dequeueReusableCellWithIdentifier: forIndexPath:]'. –

Odpowiedz

2

OK, w końcu rozwiązałem ten problem bez poświęcania animacji. Moim rozwiązaniem jest implementacja interfejsu UITableViewCell w osobnym pliku Nib z wyłączonym AutoLayout. Załadowanie trwa nieco dłużej i musisz sam ustawić pozycje subskrybentów.

Oto kod, aby umożliwić:

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    ...   

    UINib *rowCellNib = [UINib nibWithNibName:@"RowCell" bundle:nil]; 
    [self.tableView registerNib:rowCellNib forCellReuseIdentifier:@"ROW_CELL"]; 
} 

Oczywiście trzeba plik RowCell.nib z widokiem na swojej komórki.

Chociaż nie ma rozwiązania pierwotnego problemu (co najwyraźniej wydaje mi się błędem), używam tego.

+0

Nie rozumiem tego rozwiązania. Czy układ automatyczny jest wyłączony, czy też w jakiś sposób oszukujesz system, aby zwiększyć wydajność? –

+0

Sztuką jest oddzielny plik Xib (z wyłączonym automatycznym układem) dla każdej komórki. W ten sposób ogólny interfejs ma włączony układ automatyczny, ale każda komórka - nie. –

+0

Ale komórki nie są ładowane od stalówki przez stół za każdym razem. Widok tabeli zapisuje komórki z pamięci do ponownego użycia i są one już ładowane ze stalówki. Zasadniczo wyłączyłeś automatyczny układ na komórkach. Jak to rozwiązuje problem? –

16

To prawda, że ​​Auto Layout może przedstawiać działanie wydajności. Jednak w większości przypadków nie jest to naprawdę zauważalne. Są pewne skrajne przypadki ze skomplikowanymi układami, w których pozbycie się ich spowoduje znaczącą różnicę, ale to nie jest problem tutaj.

Nie mam dobrego wyjaśnienia, dlaczego aplikacja zachowuje się tak, jak jest, ale przynajmniej mam rozwiązanie: Nie rób aktualizacji widoku tabeli, jeśli widok tabeli nie jest widoczny na ekranie. Powoduje to dziwne zachowanie.

W tym celu można np. sprawdź dla self.tableview.window != nil w metodach delegatów pobranego kontrolera wyników. Następnie wystarczy dodać [self.tableview reloadData] do viewWillAppear, aby widok tabeli aktualizował swoje dane przed pojawieniem się na ekranie.

Nadzieję, że pomaga.I proszę, jeśli ktoś ma dobre wytłumaczenie tego dziwnego zachowania, proszę daj mi znać :)

+0

Tak, '[UITableView reloadData]' naprawdę rozwiązuje problem, ale nie dostaniesz animacji i musisz ponownie skonfigurować wszystkie komórki na ekranie. Użycie '[UITableView reloadSections: withRowAnimation:]' może ponownie załadować dane tabeli z animacją, ale jest tak wolne, jak metoda oryginalna. –

+1

To działało cuda, dzięki. Otworzyłem rdar: // 15175803 dla tego problemu. –

+1

To rozwiązało mój podobny problem. Zapisałem kontekst obiektu zarządzanego w 'viewWillAppear'. Przejście do "viewDidAppear" rozwiązało problem z wydajnością: coś sprawia, że ​​układ jest znacznie wolniejszy, jeśli widok nie jest wyświetlany na ekranie. –

0

Jeśli nadal chcesz korzystać z automatycznego układu w komórkach widoku tabeli, istnieją sposoby, aby go wykonać. Jest to proces wieloetapowy, ale gdy to zrobisz, możesz ustawić mechanizm pionowy komórki oprócz układu wszystkich pozostałych elementów w komórce Auto Layout.

można znaleźć więcej szczegółów tutaj: Using Auto Layout in UITableView for dynamic cell layouts & variable row heights który zawiera pewne skróty, które można podjąć, jeśli jesteś na iOS 8.

0

miałem podobne problemy z użyciem automatycznego układu złożonego w podklasie UITableViewCell. Mój układ komórek zależy od danych pochodzących z serwera, ale liczba stanów komórek jest ograniczona do sześciu. Za każdym razem, gdy otrzymuję zero z dequeueReusableCellWithIdentifier (tworzona jest nowa komórka), ustawiam dynamiczny/niestandardowy reuseIdentifier dla właśnie utworzonej komórki (self.dynamicReusableIdentifier). ReuseIdentifier pochłaniacza nie jest uwzględniana w podklasie UITableViewCell (ACellSubclass)

- (NSString*) reuseIdentifier { 

    return self.dynamicReusableIdentifier; 
} 

Wytwarzanie łańcucha self.dynamicReusableIdentifier opiera się na metody statycznej (configureDynamicReuseIdentifier) ​​według klasy komórek (ACellSubclass). tableView: cellForRowAtIndexPath następnie wybiera prawidłowy wielokrotnego identyfikatora zwrócony od

komórek = [Tableview dequeueReusableCellWithIdentifier [ACellSubclass configureDynamicReuseIdentifier: someData]];

i statyczne subdyski ponownie użytej komórki (etykiety, obrazy) są aktualizowane bez żadnych zmian w układzie automatycznym.

2

I ma takie same w wydajno [Tabela reloadData] w iOS 7. w moim przypadku, mam rozwiązać ten problem, aby zastąpić kod konfiguracyjny komórka z [cell layoutIfNeeded] do kodu następująco:

[cell setNeedsUpdateConstraints]; 
[cell setNeedsLayout]; 

a następnie powrót komórka. Wszystko wydaje się w porządku. Mam nadzieję, że to pomoże innym ludziom mieć ten sam problem.

+0

Pracował również dla mnie. Łatwo! Dzięki, –

Powiązane problemy