2012-12-01 12 views
22

Podczas używania "Dynamicznych prototypów" do określania zawartości UITableView w scenorysie istnieje właściwość "Wysokość wiersza", którą można ustawić jako Niestandardową.Pobrać niestandardową wysokość komórki prototypu z storyboardu?

Podczas tworzenia komórek wysokość tej niestandardowej wiersza nie jest brana pod uwagę. Ma to sens, ponieważ od jakiej prototypowej komórki używam decyduje mój kod aplikacji w momencie, kiedy komórka ma zostać utworzona. Aby utworzyć instancję wszystkich komórek podczas obliczania układu, wprowadziłbym karę wykonania, więc rozumiem, dlaczego nie można tego zrobić.

Pytanie, czy mogę w jakiś sposób pobrać wysokość z identyfikatorem ponownego użycia komórki, np.

[myTableView heightForCellWithReuseIdentifier:@"MyCellPrototype"]; 

coś w tym stylu? Czy muszę powtórzyć jawne wysokości rzędów w kodzie aplikacji, z następującymi obciążeniami konserwacyjnymi?

rozwiązany za pomocą @TimothyMoose:

Wysokości są przechowywane w komórkach siebie, co oznacza, że ​​jedynym sposobem na uzyskanie wyżyny jest instancję prototypy. Jednym ze sposobów jest wstępne usunięcie tych komórek poza normalną metodą odwołań do komórek. Tu jest mój mały POC, który działa:

#import "ViewController.h" 

@interface ViewController() { 
    NSDictionary* heights; 
} 
@end 

@implementation ViewController 

- (NSString*) _reusableIdentifierForIndexPath:(NSIndexPath *)indexPath 
{ 
    return [NSString stringWithFormat:@"C%d", indexPath.row]; 
} 

- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    if(!heights) { 
     NSMutableDictionary* hts = [NSMutableDictionary dictionary]; 
     for(NSString* reusableIdentifier in [NSArray arrayWithObjects:@"C0", @"C1", @"C2", nil]) { 
      CGFloat height = [[tableView dequeueReusableCellWithIdentifier:reusableIdentifier] bounds].size.height; 
      hts[reusableIdentifier] = [NSNumber numberWithFloat:height]; 
     } 
     heights = [hts copy]; 
    } 
    NSString* prototype = [self _reusableIdentifierForIndexPath:indexPath]; 
    return [heights[prototype] floatValue]; 
} 

- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    return 3; 
} 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    return 1; 
} 

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    NSString* prototype = [self _reusableIdentifierForIndexPath:indexPath]; 
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:prototype]; 
    return cell; 
} 

@end 
+0

Używałem tej metody przez jakiś czas, a następnie na nowym scenopisie, otrzymywałem w większości komórek wymiary o zerowej wysokości (i szerokości). Nawet niezerowe były nieoczekiwanymi wartościami. Wyłączenie klas wielkości przywróciło tę funkcję. Rozczarowanie, ponieważ ta "poprawka" wyłączyła ważną funkcję w scenorysie. Jednak działało to na moją bezpośrednią potrzebę. –

+0

Znalazłem bardziej kompletne rozwiązanie dla [rozwiązania tego problemu z włączonymi klasami wielkości] (http://stackoverflow.com/questions/27735341/offscreen-uitableviewcells-for-size-calculations-not-respecting-size-class). –

Odpowiedz

28

Dla statycznym (non-data-driven) wysokości, można po prostu dequeue komórkę raz i przechowywać wysokość:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSNumber *height; 
    if (!height) { 
     UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCustomCell"]; 
     height = @(cell.bounds.size.height); 
    } 
    return [height floatValue]; 
} 

do dynamicznego (dane -driven) height, możesz przechowywać prototypową komórkę w kontrolce widoku i dodać metodę do klasy komórki, która oblicza wysokość, biorąc pod uwagę domyślną zawartość prototypowej instancji, taką jak umieszczenie podekru, czcionki itp .:

- (MyCustomCell *)prototypeCell 
{ 
    if (!_prototypeCell) { 
     _prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCustomCell"]; 
    } 
    return _prototypeCell; 
} 

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    // Data for the cell, e.g. text for label 
    id myData = [self myDataForIndexPath:indexPath]; 

    // Prototype knows how to calculate its height for the given data 
    return [self.prototypeCell myHeightForData:myData]; 
} 

Oczywiście, jeśli używasz niestandardowej wysokości, prawdopodobnie masz wiele prototypów komórek, więc możesz przechowywać je w słowniku lub coś takiego.

O ile widzę, widok tabeli nie próbuje ponownie użyć prototypu, prawdopodobnie dlatego, że został usunięty poza numerem cellForRowAtIndexPath:. To podejście zadziałało bardzo dobrze, ponieważ pozwala projektantowi modyfikować układy komórek w scenorysie bez potrzeby wprowadzania zmian w kodzie.

Edytuj: wyjaśniono znaczenie przykładowego kodu i dodano przykład dla przypadku statycznej wysokości.

+0

Nie rozpoznaję metody 'heightForData:'. Co to robi? – Krumelur

+0

'heightForData:' to metoda, którą napiszesz, aby obliczyć wysokość dla danych komórki. –

+0

Rozumiem. Problem polega na tym, że chcę użyć wysokości zapisanej w samym pliku storyboardu (wysokość statyczna). Twoje wstępne usuwanie komórek może również zadziałać. Wypróbuję to. – Krumelur

5

Stworzyłem kategorię dla UITableView jakiś czas temu, która może być przydatna do tego. Przechowuje on "prototypowe" komórki przy użyciu asociated objects do ponownego użycia prototypów i zapewnia wygodną metodę uzyskiwania wysokości rzędu przypisanego w scenorysie. Prototypy są zwalniane, gdy widok tabeli jest zwolniony.

UITableView + PrototypeCells.h

#import <UIKit/UIKit.h> 

@interface UITableView (PrototypeCells) 

- (CGFloat)heightForRowWithReuseIdentifier:(NSString*)reuseIdentifier; 
- (UITableViewCell*)prototypeCellWithReuseIdentifier:(NSString*)reuseIdentifier; 

@end 

UITableView + PrototypeCells.m

#import "UITableView+PrototypeCells.h" 
#import <objc/runtime.h> 

static char const * const key = "prototypeCells"; 

@implementation UITableView (PrototypeCells) 
- (void)setPrototypeCells:(NSMutableDictionary *)prototypeCells { 
    objc_setAssociatedObject(self, key, prototypeCells, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
} 

- (NSMutableDictionary *)prototypeCells { 
    return objc_getAssociatedObject(self, key); 
} 

- (CGFloat)heightForRowWithReuseIdentifier:(NSString*)reuseIdentifier { 
    return [self prototypeCellWithReuseIdentifier:reuseIdentifier].frame.size.height; 
} 

- (UITableViewCell*)prototypeCellWithReuseIdentifier:(NSString*)reuseIdentifier { 
    if (self.prototypeCells == nil) { 
     self.prototypeCells = [[NSMutableDictionary alloc] init]; 
    } 

    UITableViewCell* cell = self.prototypeCells[reuseIdentifier]; 
    if (cell == nil) { 
     cell = [self dequeueReusableCellWithIdentifier:reuseIdentifier]; 
     self.prototypeCells[reuseIdentifier] = cell; 
    } 
    return cell; 
} 

@end 

Zastosowanie

Uzyskanie wysokość statyczną ustawiony w ujęć jest tak prosta jak to:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
    return [tableView heightForRowWithReuseIdentifier:@"cellIdentifier"]; 
} 

Zakładając wielosekcyjne widok tabeli:

enum { 
    kFirstSection = 0, 
    kSecondSection 
}; 

static NSString* const kFirstSectionRowId = @"section1Id"; 
static NSString* const kSecondSectionRowId = @"section2Id"; 

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
    CGFloat height = tableView.rowHeight; // Default UITableView row height 
    switch (indexPath.section) { 
     case kFirstSection: 
      height = [tableView heightForRowWithReuseIdentifier:kFirstSectionRowId]; 
      break; 
     case kSecondSection: 
      height = [tableView heightForRowWithReuseIdentifier:kSecondSectionRowId]; 
    } 
    return height; 
} 

I na koniec, jeśli wysokość wiersza jest dynamiczna:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
    id thisRowData = self.allData[indexPath.row]; // Obtain the data for this row 

    // Obtain the prototype cell 
    MyTableViewCell* cell = (MyTableViewCell*)[self prototypeCellWithReuseIdentifier:@"cellIdentifier"]; 

    // Ask the prototype cell for its own height when showing the specified data 
    return [cell heightForData:thisRowData]; 
} 
+0

To wygląda na bardzo przydatne. Dzięki! – Krumelur

+0

Świetna implementacja, powinieneś umieścić to na github & pot –

Powiązane problemy