Ustawianie niestandardowej wysokości UITableViewCells

222

Używam niestandardowego UITableViewCell, który ma pewne etykiety, przyciski i widoki obrazów do wyświetlenia. W komórce znajduje się jedna etykieta, której tekst jest NSStringobiektem, a długość łańcucha może być zmienna. Ze względu na to, że nie można ustawić wysokość stałej do komórki w UITableView„s heightForCellAtIndexmetody. Wzrost komórki w zależności od wysokości etykiety, które mogą być określone przy użyciu NSString„S sizeWithFontmetody. Próbowałem go użyć, ale wygląda na to, że gdzieś się mylę. Jak można to naprawić?

Oto kod użyty do zainicjowania komórki.

if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier])
{
    self.selectionStyle = UITableViewCellSelectionStyleNone;
    UIImage *image = [UIImage imageNamed:@"dot.png"];
    imageView = [[UIImageView alloc] initWithImage:image];
    imageView.frame = CGRectMake(45.0,10.0,10,10);

    headingTxt = [[UILabel alloc] initWithFrame:   CGRectMake(60.0,0.0,150.0,post_hdg_ht)];
    [headingTxt setContentMode: UIViewContentModeCenter];
    headingTxt.text = postData.user_f_name;
    headingTxt.font = [UIFont boldSystemFontOfSize:13];
    headingTxt.textAlignment = UITextAlignmentLeft;
    headingTxt.textColor = [UIColor blackColor];

    dateTxt = [[UILabel alloc] initWithFrame:CGRectMake(55.0,23.0,150.0,post_date_ht)];
    dateTxt.text = postData.created_dtm;
    dateTxt.font = [UIFont italicSystemFontOfSize:11];
    dateTxt.textAlignment = UITextAlignmentLeft;
    dateTxt.textColor = [UIColor grayColor];

    NSString * text1 = postData.post_body;
    NSLog(@"text length = %d",[text1 length]);
    CGRect bounds = [UIScreen mainScreen].bounds;
    CGFloat tableViewWidth;
    CGFloat width = 0;
    tableViewWidth = bounds.size.width/2;
    width = tableViewWidth - 40; //fudge factor
    //CGSize textSize = {width, 20000.0f}; //width and height of text area
    CGSize textSize = {245.0, 20000.0f}; //width and height of text area
    CGSize size1 = [text1 sizeWithFont:[UIFont systemFontOfSize:11.0f]
                        constrainedToSize:textSize lineBreakMode:UILineBreakModeWordWrap];

    CGFloat ht = MAX(size1.height, 28);
    textView = [[UILabel alloc] initWithFrame:CGRectMake(55.0,42.0,245.0,ht)];
    textView.text = postData.post_body;
    textView.font = [UIFont systemFontOfSize:11];
    textView.textAlignment = UITextAlignmentLeft;
    textView.textColor = [UIColor blackColor];
    textView.lineBreakMode = UILineBreakModeWordWrap;
    textView.numberOfLines = 3;
    textView.autoresizesSubviews = YES;

    [self.contentView addSubview:imageView];
    [self.contentView addSubview:textView];
    [self.contentView addSubview:webView];
    [self.contentView addSubview:dateTxt];
    [self.contentView addSubview:headingTxt];
    [self.contentView sizeToFit];

    [imageView release];
    [textView release];
    [webView release];
    [dateTxt release];
    [headingTxt release];
}

To jest etykieta, której wysokość i szerokość są nieprawidłowe:

textView = [[UILabel alloc] initWithFrame:CGRectMake(55.0,42.0,245.0,ht)];
Vijayeta
źródło

Odpowiedzi:

499

Twój UITableViewDelegatepowinien wdrożyćtableView:heightForRowAtIndexPath:

Cel C

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [indexPath row] * 20;
}

Szybki 5

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return indexPath.row * 20
}

Prawdopodobnie będzie chciał użyć NSString„s sizeWithFont:constrainedToSize:lineBreakMode:sposób obliczyć wysokość wiersza zamiast po prostu wykonując jakąś głupią matematyki na indexPath :)

rpetrich
źródło
1
Nie masz na myśli [indexPath row] + 20?
fulvio
11
Fulvio: nie, chodziło o to, aby rzędy miały oczywiście różne wysokości.
rpetrich
5
Aby ustawić określoną wysokość (np. 44 piksele): zwróć 44;
mpemburn
Czy możesz podać kilka dodatkowych informacji na temat korzystania z tego sizeWithFont: constrainedToSize: lineBreakMode:? Mam textView w mojej komórce i chcę uzyskać jego wysokość w heightForRowAtIndexPath: w celu ustawienia wysokości wiersza, ale nie wiem, jak ją uzyskać! Dzięki
rdurand
Jeśli zwrócisz „[wiersz indeksu] * (pewna wartość)”, wówczas pierwsza komórka może nie być widoczna, ponieważ pierwsza zwracana wartość wynosi zero. Dlatego lepiej jest obliczyć potrzebną wysokość komórki i zwrócić tę obliczoną wartość tutaj, aby wszystkie komórki były widoczne
Vinayak GH
134

Jeśli wszystkie wiersze mają tę samą wysokość, po prostu ustaw rowHeightwłaściwość UITableView zamiast implementować heightForRowAtIndexPath. Dokumenty Apple:

Istnieją konsekwencje dotyczące wydajności korzystania z tableView: heightForRowAtIndexPath: zamiast rowHeight. Za każdym razem, gdy wyświetlany jest widok tabeli, wywołuje tableView: heightForRowAtIndexPath: na delegacie dla każdego z jego wierszy, co może powodować znaczny problem z wydajnością w widokach tabeli z dużą liczbą wierszy (około 1000 lub więcej).

Jokkedk
źródło
5
Działa to doskonale, gdy wszystkie rzędy mają tę samą wysokość. Oryginalny plakat nie ma jednak takiej sytuacji, więc odpowiedź sama w sobie nie jest niepoprawna, ale nie odpowiada na pierwotne pytanie. JEDNAK ważne jest, aby zauważyć, że ustawienie rowHeight najprawdopodobniej daje lepszą wydajność niż przesłonięcie heightForRowAtIndexPath, ponieważ w takim przypadku obliczenia wymiarów paska przewijania to O (1) zamiast O (n).
Daniel Albuschat
2
Zauważ też (z oczywistych powodów), że jeśli tableView.rowHeightjest ustawiony, heightForRowAtIndexPathnie będzie wywoływany, na wypadek, gdybyś próbował dowiedzieć się, dlaczego (tak jak ja).
devios1
25

dodaj niestandardowy kontroler UITableViewCell

-(void)layoutSubviews {  

    CGRect newCellSubViewsFrame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
    CGRect newCellViewFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height);

    self.contentView.frame = self.contentView.bounds = self.backgroundView.frame = self.accessoryView.frame = newCellSubViewsFrame;
    self.frame = newCellViewFrame;

    [super layoutSubviews];
}

Dodaj to w sterowniku UITableView

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [indexPath row] * 1.5; // your dynamic height...
}
hfossli
źródło
CGRect newCellSubViewsFrame = self.bounds; CGRect newCellViewFrame = self.frame;
Pizzaiola Gorgonzola
17
#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 300.0f
#define CELL_CONTENT_MARGIN 10.0f

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath      *)indexPath;
{
   /// Here you can set also height according to your section and row
   if(indexPath.section==0 && indexPath.row==0)
   {
     text=@"pass here your dynamic data";

     CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);

     CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE]      constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];

     CGFloat height = MAX(size.height, 44.0f);

     return height + (CELL_CONTENT_MARGIN * 2);
   }
   else
   {
      return 44;
   }
}

- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell;
    UILabel *label = nil;

    cell = [tv dequeueReusableCellWithIdentifier:@"Cell"];
    if (cell == nil)
    {
       cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Cell"];
    }
    ********Here you can set also height according to your section and row*********
    if(indexPath.section==0 && indexPath.row==0)
    {
        label = [[UILabel alloc] initWithFrame:CGRectZero];
        [label setLineBreakMode:UILineBreakModeWordWrap];
        [label setMinimumFontSize:FONT_SIZE];
        [label setNumberOfLines:0];
        label.backgroundColor=[UIColor clearColor];
        [label setFont:[UIFont systemFontOfSize:FONT_SIZE]];
        [label setTag:1];

        // NSString *text1 =[NSString stringWithFormat:@"%@",text];

        CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);

        CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];

        if (!label)
        label = (UILabel*)[cell viewWithTag:1];


        label.text=[NSString stringWithFormat:@"%@",text];
        [label setFrame:CGRectMake(CELL_CONTENT_MARGIN, CELL_CONTENT_MARGIN, CELL_CONTENT_WIDTH          - (CELL_CONTENT_MARGIN * 2), MAX(size.height, 44.0f))];
        [cell.contentView addSubview:label];
    }
return cell;
}
ravinder521986
źródło
8
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
    CGSize constraintSize = {245.0, 20000}
    CGSize neededSize = [ yourText sizeWithFont:[UIfont systemFontOfSize:14.0f] constrainedToSize:constraintSize  lineBreakMode:UILineBreakModeCharacterWrap]
if ( neededSize.height <= 18) 

   return 45
else return neededSize.height + 45 
//18 is the size of your text with the requested font (systemFontOfSize 14). if you change fonts you have a different number to use  
// 45 is what is required to have a nice cell as the neededSize.height is the "text"'s height only
//not the cell.

}
Alice
źródło
8

Widziałem wiele rozwiązań, ale wszystko było złe lub niepełne. Możesz rozwiązać wszystkie problemy za pomocą 5 linii w viewDidLoad i autolayout. To dla objetive C:

_tableView.delegate = self;
_tableView.dataSource = self;
self.tableView.estimatedRowHeight = 80;//the estimatedRowHeight but if is more this autoincremented with autolayout
self.tableView.rowHeight = UITableViewAutomaticDimension;
[self.tableView setNeedsLayout];
[self.tableView layoutIfNeeded];
self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0) ;

W przypadku szybkiego 2.0:

 self.tableView.estimatedRowHeight = 80
 self.tableView.rowHeight = UITableViewAutomaticDimension      
 self.tableView.setNeedsLayout()
 self.tableView.layoutIfNeeded()
 self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0)

Teraz stwórz swoją komórkę za pomocą xib lub w widoku tabeli w Storyboard Dzięki temu nie musisz nic więcej implementować ani zastępować. (Nie zapomnij o numerach linii 0) i obniżenie dolnej etykiety (ograniczenie) „Priorytet przytulania treści - w pionie do 250”

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Możesz załadować kod w następnym adresie URL: https://github.com/jposes22/exampleTableCellCustomHeight

Jose Pose S.
źródło
1
Za 2 dni dodam więcej obrazów z tą samą komórką z znakami ImageView, a inny widok podtabeli ta metoda zawsze działa. Dzięki za +1 naprawdę.
Jose Pose S
To wspaniale!! Pomoże również innym. Dzięki :)
jagdish
Tak jak obiecano, masz tutaj nowy kod i nowy obraz.
Jose Pose S
Wielkie
To rozwiązanie nie działa, jeśli programowo tworzysz UITableViewCell, np. Nie używasz Konstruktora interfejsów
MB_iOSDeveloper
6

Aby ustawić automatyczny wymiar dla wysokości wiersza i szacowanej wysokości wiersza, wykonaj następujące kroki, aby automatyczny wymiar był skuteczny dla układu wysokości komórki / wiersza.

  • Przypisywanie i wdrażanie danych widoku tabeli Źródło i delegowanie
  • Przypisz UITableViewAutomaticDimensiondo rowHeight & oszacowanyRowHeight
  • Wdrażanie metod delegate / dataSource (tj. heightForRowAtI zwracanie wartości UITableViewAutomaticDimensiondo niego)

-

Cel C:

// in ViewController.h
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>

  @property IBOutlet UITableView * table;

@end

// in ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    self.table.dataSource = self;
    self.table.delegate = self;

    self.table.rowHeight = UITableViewAutomaticDimension;
    self.table.estimatedRowHeight = UITableViewAutomaticDimension;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    return UITableViewAutomaticDimension;
}

Szybki:

@IBOutlet weak var table: UITableView!

override func viewDidLoad() {
    super.viewDidLoad()

    // Don't forget to set dataSource and delegate for table
    table.dataSource = self
    table.delegate = self

    // Set automatic dimensions for row height
    table.rowHeight = UITableViewAutomaticDimension
    table.estimatedRowHeight = UITableViewAutomaticDimension
}



// UITableViewAutomaticDimension calculates height of label contents/text
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableViewAutomaticDimension
}

Dla instancji etykiety w UITableviewCell

  • Ustaw liczbę linii = 0 (tryb przerwania linii = obetnij ogon)
  • Ustaw wszystkie ograniczenia (górny, dolny, prawy lewy) w odniesieniu do jego kontenera podglądu / komórki.
  • Opcjonalne : Ustaw minimalną wysokość etykiety, jeśli chcesz, aby etykieta obejmowała minimalny obszar pionowy, nawet jeśli nie ma danych.

wprowadź opis zdjęcia tutaj

Uwaga : Jeśli masz więcej niż jedną etykietę (UIElements) o długości dynamicznej, którą należy dostosować do jej rozmiaru: Dostosuj „Priorytet przytulania zawartości i priorytetu odporności na ściskanie” dla etykiet, które chcesz rozwinąć / skompresować z wyższym priorytetem.

W tym przykładzie ustawiłem niski priorytet przytulania i wysoki priorytet odporności na kompresję, co prowadzi do ustawienia większego priorytetu / ważności dla zawartości drugiej (żółtej) etykiety.

wprowadź opis zdjęcia tutaj

Krunal
źródło
5

Dzięki wszystkim postom na ten temat istnieje kilka naprawdę pomocnych sposobów dostosowania rowHeight UITableViewCell.

Oto kompilacja niektórych koncepcji od wszystkich innych, która naprawdę pomaga w budowaniu dla iPhone'a i iPada. Możesz także uzyskać dostęp do różnych sekcji i dostosować je do różnych rozmiarów widoków.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
    int cellHeight = 0;

    if ([indexPath section] == 0) 
    {
        cellHeight = 16;
        settingsTable.rowHeight = cellHeight;
    }
    else if ([indexPath section] == 1)
    {
        cellHeight = 20;
        settingsTable.rowHeight = cellHeight;
    }

    return cellHeight;
}
else
{
    int cellHeight = 0;

    if ([indexPath section] == 0) 
    {
        cellHeight = 24;
        settingsTable.rowHeight = cellHeight;
    }
    else if ([indexPath section] == 1)
    {
        cellHeight = 40;
        settingsTable.rowHeight = cellHeight;
    }

    return cellHeight;
}
return 0;
} 
dlaczego?
źródło
Podczas analizy w XCode wiersz „int cellHeight;” utworzono błąd logiczny „Nieokreślona lub wartość śmieci została zwrócona do osoby dzwoniącej”. Przez ustawienie cellHeight na 0, naprawiono błąd. Ten błąd pojawi się również, jeśli nie ustawisz NSString na zero w instrukcji switch podobnej do instrukcji if / else if powyżej.
dlaczegooz
3

Aby dynamiczna wysokość komórki rosła wraz ze wzrostem tekstu Etykiety, musisz najpierw obliczyć wysokość, którą tekst będzie używał w -heightForRowAtIndexPathmetodzie delegowania i zwrócić ją z dodanymi wysokościami innych etykiet, obrazów (maksymalna wysokość tekstu + wysokość innej wartości statycznej komponenty) i używaj tej samej wysokości do tworzenia komórek.

#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 300.0f  
#define CELL_CONTENT_MARGIN 10.0f

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{

    if (indexPath.row == 2) {  // the cell you want to be dynamic

        NSString *text = dynamic text for your label;

        CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);
        CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];

        CGFloat height = MAX(size.height, 44.0f);

        return height + (CELL_CONTENT_MARGIN * 2);
    }
    else {
        return 44; // return normal cell height
    }
}

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

    static NSString *CellIdentifier = @"Cell";

    UILabel *label;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] ;
    }

    label = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, 280, 34)];

    [label setNumberOfLines:2];

    label.backgroundColor = [UIColor clearColor];

    [label setFont:[UIFont systemFontOfSize:FONT_SIZE]];

    label.adjustsFontSizeToFitWidth = NO;

    [[cell contentView] addSubview:label];


    NSString *text = dynamic text fro your label;

    [label setText:text];

    if (indexPath.row == 2) {// the cell which needs to be dynamic 

        [label setNumberOfLines:0];

        CGSize constraint = CGSizeMake(CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), 20000.0f);

        CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];

        [label setFrame:CGRectMake(CELL_CONTENT_MARGIN, CELL_CONTENT_MARGIN, CELL_CONTENT_WIDTH - (CELL_CONTENT_MARGIN * 2), MAX(size.height, 44.0f))];

    }
    return  cell;
}
Samir Jwarchan
źródło
@ Tisho Czy przetestowałeś to, bracie Tisho, aby wykonać głosowanie w dół. Przetestuj dokładnie, działa brachu !!!!
Samir Jwarchan
1
Nie zanegowałem twojej odpowiedzi. Właśnie poprawiłem formatowanie.
Tisho,