Ustawianie stylu UITableViewCell podczas korzystania z systemu iOS 6 UITableView dequeueReusableCellWithIdentifier: forIndexPath:

82

Próbuję dowiedzieć się, jak ustawić, UITableViewCellStylegdy używam nowych metod w iOS 6 dla UITableView.

Wcześniej podczas tworzenia wyliczenia UITableViewCellzmieniałem UITableViewCellStylewyliczenie, aby podczas wywoływania utworzyć różne typy komórek domyślnych, initWithStyle:ale z tego, co mogę zebrać, już tak nie jest.

Dokumentacja Apple dla UITableViewstanów:

Wartość zwracana: obiekt UITableViewCell ze skojarzonym identyfikatorem ponownego wykorzystania. Ta metoda zawsze zwraca prawidłową komórkę.

Dyskusja : ze względu na wydajność źródło danych widoku tabeli powinno zasadniczo ponownie wykorzystywać obiekty UITableViewCell, gdy przypisuje komórki do wierszy w swojej metodzie tableView: cellForRowAtIndexPath:. Widok tabeli przechowuje kolejkę lub listę obiektów UITableViewCell, które źródło danych oznaczyło do ponownego użycia. Wywołaj tę metodę z obiektu źródła danych, gdy zostaniesz poproszony o podanie nowej komórki dla widoku tabeli. Ta metoda usuwa z kolejki istniejącą komórkę, jeśli jest dostępna, lub tworzy nową na podstawie poprzednio zarejestrowanej klasy lub pliku nib.

Ważne : Przed wywołaniem tej metody należy zarejestrować klasę lub plik nib przy użyciu metody registerNib: forCellReuseIdentifier: lub registerClass: forCellReuseIdentifier:.

Jeśli zarejestrowano klasę dla określonego identyfikatora i trzeba utworzyć nową komórkę, ta metoda inicjuje komórkę, wywołując jej metodę initWithStyle: reuseIdentifier:. W przypadku komórek opartych na końcówkach ta metoda ładuje obiekt komórki z dostarczonego pliku nib. Jeśli istniejąca komórka była dostępna do ponownego użycia, ta metoda wywołuje zamiast niej metodę prepaid komórki.

Oto jak cellForRowAtIndexPathwygląda mój nowy po wdrożeniu nowych metod:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"cell_identifier";

    [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:cellIdentifier];

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    return cell;
}

Kod, który mam do tej pory, działa dobrze, ale zawsze zwraca domyślny styl. Jak mogę to zmienić, więc mogę utworzyć komórki z innych stylów, takich jak UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2i UITableViewCellStyleSubtitle?

Nie chcę podklasy UITableViewCell, chcę po prostu zmienić domyślny typ, tak jak mogłem to zrobić przed iOS 6. Wydaje się dziwne, że Apple zapewnia ulepszone metody, ale z minimalną dokumentacją wspierającą ich implementację.

Czy ktoś to opanował lub napotkał podobny problem? W ogóle staram się znaleźć jakiekolwiek rozsądne informacje.

Kapitanie Redmuff
źródło

Odpowiedzi:

106

Wiem, że powiedziałeś, że nie chcesz tworzyć podklasy, ale wydaje się to nieuniknione. Na podstawie kodu assemblera podczas testowania w symulatorze iOS 6.0 UITableViewtworzy nowe wystąpienia UITableViewCell(lub jej podklasy), wykonując

[[<RegisteredClass> alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:<ReuseIdentifier>]

Innymi słowy, styl sent ( UITableViewCellStyleDefault) wydaje się być zakodowany na stałe. Aby obejść ten problem, musisz utworzyć podklasę, która zastępuje domyślny inicjator initWithStyle:reuseIdentifier:i przekazuje styl, którego chcesz użyć:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    // ignore the style argument, use our own to override
    self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier];
    if (self) {
        // If you need any further customization
    }
    return self;
}

Ponadto, może lepiej byłoby wysłać registerClass:forCellReuseIdentifier:w viewDidLoad, zamiast robić to za każdym razem, gdy komórka jest wniosek:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.tableView registerClass:<RegisteredClass> forCellReuseIdentifier:<ReuseIdentifier>];
}
bolot
źródło
4
Zaczynałem zakładać, że rzeczywiście tak będzie. Nie jest to poważny problem, ale konieczność podklasy w UITableViewCellcelu uzyskania innych domyślnych stylów jest uciążliwa, ponieważ tworzy po prostu niepotrzebne pliki. Dziękuję za komentarz i potwierdzenie moich podejrzeń.
CaptainRedmuff
11
Nie zapominaj, że zamiast podklasy można użyć starej metody iOS5, która nadal obowiązuje. W ten sposób możesz samodzielnie zainicjować dowolny typ stylu komórki. Zobacz inną odpowiedź.
SpacyRicochet
60

dequeueReusableCellWithIdentifiernie jest przestarzały, więc nie musisz używać nowego dequeueReusableCellWithIdentifier:forIndexPath:.

Użyj nowego sposobu wraz z odpowiednią metodą rejestru (w viewDidLoad), jeśli używasz niestandardowej klasy komórek, ale użyj starego sposobu, jeśli chcesz użyć jednego z wyliczeń UITableViewCellStyle.

Murray Sagal
źródło
1
Głos za wskazaniem, że nie musisz używać nowych, wymyślnych metod. Tylko jeśli odpowiadają Twoim celom lub jeśli alternatywy są przestarzałe.
SpacyRicochet
Jeśli jesteś szczególnie zainteresowany, możesz zastąpić dequeueReusableCellWithIdentifier:forIndexPath:niektóre identyfikatory budujące komórki w stary sposób (i zwracające je). Inne identyfikatory będą wywoływać super i zwracać to. Miałoby sens posiadanie NSDictionaryidentyfikatorów of do konstruowania bloków dla tego rodzaju identyfikatorów.
Benjohn
11

Możesz uniknąć zbędnych podklas, używając konstruktora interfejsu scenorysu:

  1. W widoku Storyboard wybierz komórkę prototypu komórki widoku tabeli (w widoku tabeli)
  2. W widoku Narzędzia, w Inspektorze atrybutów, zmień wartość Styl
  3. (Opcjonalnie) Zmodyfikuj inne wartości, takie jak Wybór i Akcesoria

Nowy system iOS 6.0 dequeueReusableCellWithIdentifier:forIndexPath:używa tych wartości podczas przydzielania nowych komórek i zwracania ich. (Testowane na kompilacji systemu iOS 6.0 przy użyciu Xcode 4.5.2)

Jeff Collier
źródło
7

Inną alternatywą, która zapisuje jeden plik, jest utworzenie końcówki i użycie registerNib:forCellReuseIdentifier:zamiast niej.

Tworzenie końcówki jest łatwe: utwórz nowy plik .xib w programie Interface Builder. Usuń widok domyślny. Dodaj obiekt komórki widoku tabeli. Korzystając z Inspektora atrybutów, zmień styl komórki. (Tutaj masz również możliwość dalszego dostosowania komórki, dostosowując inne atrybuty).

Następnie w viewDidLoadmetodzie kontrolera widoku tabeli wywołaj coś takiego:

[self.tableView registerNib:[UINib nibWithNibName:@"StyleSubtitleTableCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"Cell"];
Panie Berna
źródło
initWithStyle: reuseIdentifier nie jest wywoływana.
thierryb
0

Odpowiedź Bolot jest poprawna. Proste i nie musisz tworzyć żadnego pliku XIB.

Chciałem tylko zaktualizować jego odpowiedź dla każdego, kto robi to za pomocą Swift zamiast Objective-C:

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    super.init(style: .value1, reuseIdentifier: reuseIdentifier)
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}
Gabriel Oliva
źródło
-5

Moim rozwiązaniem jest zadzwonienie initWithStyle: reuseIdentifier:po uzyskaniu go za pomocą [self.tableView dequeueReusableCellWithIdentifier:@"cellId" forIndexPath:indexPath]. W końcu initto tylko kolejny selektor, a kompilator nie nakłada żadnych ograniczeń na wywoływanie go na już zainicjowanym obiekcie. Będzie jednak narzekać, że nie używa wyniku wywołania init, więc robię:

UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:@"cellId" forIndexPath:indexPath];
cell = [cell initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cellId"];

Wyobrażam sobie, że to nie zadziała w Swift ...

Chris
źródło
Najbardziej eleganckie rozwiązanie imo. Miejmy nadzieję, że wnętrzności initWithStyle nie odtworzą wszystkiego ponownie.
Scott Birksted,
2
Cóż, myślę, że jeśli robisz to w ten sposób, możesz całkowicie porzucić dekolejację ...
stk
1
Ponowne użycie komórek jest kluczem do wydajnego widoku UITableView. Twoje rozwiązanie sugeruje, aby nie używać ponownie komórek.
Berik
2
Kiedy dzwonisz initWithStyle: reuseIdentifierpo raz drugi, faktycznie nadpisujesz komórkę nowo utworzonym obiektem. Tak, alokacja pamięci została już wykonana, a nowa komórka nadpisze tę samą lokalizację pamięci, ale w rzeczywistości tworzysz całkowicie nowy obiekt, gdy ponownie go zainicjujesz. To neguje całą optymalizację, która jest głównym celem ponownego wykorzystania komórek.
AnthonyMDev
1
Nie jest to dobre rozwiązanie do naśladowania - jeśli korzystasz z tego rozwiązania, myślę, że przynajmniej lepiej jest całkowicie porzucić dekolejację komórek
Sudara