1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen

iPhone UITableViewCell: Problem mit dequeueReusableCellWithIdentifier?

Dieses Thema im Forum "iOS-Developer" wurde erstellt von effzehn, 30.09.09.

  1. effzehn

    effzehn Adams Apfel

    Dabei seit:
    07.02.05
    Beiträge:
    511
    Hi Leute,

    ich beobachte bei mir das seltsame Problem, dass sich UILabels, eingebettet in einer TableViewCell beim herumscrollen wiederholen und andere Felder überlagern. Das sieht dann im Simulator so aus:


    [​IMG]

    (Ja, ich mache CS193P ;))

    Kommt nicht vor bei TableViews, die nicht länger als der Bildschirm sind.
    Ich denke daher, dass es etwas mit dem Cell Queuing zu tun hat. Jedenfalls bin ich nach mehreren Stunden Suche codeblind geworden:


    Code:
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        
        static NSString *CellIdentifier = @"Cell";
        
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil) {
            cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
        }
        
    	cell.selectionStyle = UITableViewCellSelectionStyleNone;
    	
    	NSString *text = [[NSString alloc] initWithString:[[[person timeline] objectAtIndex:indexPath.row] objectForKey:@"text"]];
    	
    	CGSize withinSize = CGSizeMake(tableView.bounds.size.width-40, 1000.0);
    	CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:12] constrainedToSize:withinSize lineBreakMode:UILineBreakModeWordWrap];
    	CGSize box = CGSizeMake(tableView.bounds.size.width-40 , size.height);
    	CGRect contentRect = CGRectMake(10.0, 10.0, box.width, box.height);
    	UILabel *textView = [[UILabel alloc] initWithFrame:contentRect];
    	
    	textView.lineBreakMode = UILineBreakModeWordWrap;
    	textView.text = text;
    	textView.numberOfLines = 0;
    	textView.font = [UIFont systemFontOfSize:12];
    	[cell.contentView addSubview:textView];
    	
    	[textView release];
    	[text release];
    	
        return cell;
    }
    
    Was habe ich übersehen?
     
  2. sheep

    sheep Fießers Erstling

    Dabei seit:
    09.11.07
    Beiträge:
    126
    Du machst die Allocation bei jedem Aufruf. Du könntest die Allocation im conditional statement machen (cell==nil):

    z.B.:

    Code:
    if (cell == nil) {
            cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    		
    		
    		
    	UILabel *aLabel=[[UILabel alloc] initWithFrame:CGRectMake(5, 10,120,40)];
    				
            aLabel.tag = 56;
    		
            
            [cell.contentView addSubview:aLabel];
    		
            [aLabel release];
    		
    		}
    
    UILabel *aLabel = (UILabel *)[cell.contentView viewWithTag:56];
    
    
     
  3. effzehn

    effzehn Adams Apfel

    Dabei seit:
    07.02.05
    Beiträge:
    511
    Hallo sheep,

    erstmal vielen Dank. Ich habe die Zuweisungen jetzt im if-statement. Was jedoch bewirkt diese Zeile?

    Code:
    UILabel *aLabel = (UILabel *)[cell.contentView viewWithTag:56];
    Ich bin noch neu in ObjC, bitte verzeihe mir daher diese Frage.
     
  4. sheep

    sheep Fießers Erstling

    Dabei seit:
    09.11.07
    Beiträge:
    126
  5. effzehn

    effzehn Adams Apfel

    Dabei seit:
    07.02.05
    Beiträge:
    511
    Okay, ich habe jetzt mit folgendem cellForRowAtIndexPath delegate hinbekommen, dass sich die Inhalte zumindest nicht am falschen Ort wiederholen:

    Code:
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        
        static NSString *CellIdentifier = @"Cell";
        
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    	
    	UILabel *textView;
    	
    	NSString *text = [[NSString alloc] initWithString:[[[person timeline] objectAtIndex:indexPath.row] objectForKey:@"text"]];
    	CGSize withinSize = CGSizeMake(tableView.bounds.size.width-40, 1000.0);
    	CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:12] constrainedToSize:withinSize lineBreakMode:UILineBreakModeWordWrap];
    	CGSize box = CGSizeMake(tableView.bounds.size.width-40 , size.height);
    	CGRect contentRect = CGRectMake(10.0, 10.0, box.width, box.height);
    		
        if (cell == nil) {
            cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    		cell.selectionStyle = UITableViewCellSelectionStyleNone;
    		
    		textView = [[[UILabel alloc] initWithFrame:contentRect] autorelease];
    		textView.lineBreakMode = UILineBreakModeWordWrap;
    		textView.numberOfLines = 0;
    		textView.tag = 123;
    		textView.font = [UIFont systemFontOfSize:12];
    		
    		[cell.contentView addSubview:textView];
    		
        } else {
    		textView = (UILabel *)[cell.contentView viewWithTag:123];
    	}
    	
    	textView.text = text;
    	
        return cell;
    }
    Übrigens: was genau ist hier der Unterschied zwischen

    Code:
    textView = (UILabel *)[cell.contentView viewWithTag:123];
    und

    Code:
    [cell.contentView addSubview:textView];
    wenn beide Zeilen das Subview dem Superview mitteilen?



    Allerdings scheint die Methode immernoch die Labelhöhe falsch zu berechnen:


    [​IMG]

    Man sieht, dass die Berechnung der CellView korrekt ist. Dagegen sind die Labels zu hoch und überschreiben die danach gezeichneten Labels.

    Hier der Vollständigkeit halber das delegate heightForRowAtIndexPath:

    Code:
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    	
    	CGSize withinSize = CGSizeMake(tableView.bounds.size.width-40, 1000.0);
    	NSString *text = [[[NSString alloc] initWithString:[[[person timeline] objectAtIndex:indexPath.row] objectForKey:@"text"]] autorelease];
    	
    	CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:12] constrainedToSize:withinSize lineBreakMode:UILineBreakModeWordWrap];
    	
    	return size.height+20.0;
    }
    Danke.
     

    Anhänge:

  6. sheep

    sheep Fießers Erstling

    Dabei seit:
    09.11.07
    Beiträge:
    126
    Welche Höhe hast du denn hier angegeben:
    Code:
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    
    ?
     
  7. effzehn

    effzehn Adams Apfel

    Dabei seit:
    07.02.05
    Beiträge:
    511
    Code:
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
        return [[person timeline] count];
    }
    Das ist die Anzahl der Einträge im timline-Array und sollte der Anzahl an Zellen entsprechen (tut es auch).
     
  8. MacApple

    MacApple Lord Grosvenor

    Dabei seit:
    05.01.04
    Beiträge:
    3.470
    Die beiden Zeilen tun nicht das geiche. Die erste Zeile liefert Dir den Subview vom contentView zurück, dessen Tag den Wert 123 hat. Die zweite Zeile fügt den textView dem contentView als Subview hinzu.

    Übrigens gibt es auch so etwas, das nennt sich Dokumentation. Da wird beschrieben, was die Methoden so machen.

    MacApple
     
  9. sheep

    sheep Fießers Erstling

    Dabei seit:
    09.11.07
    Beiträge:
    126
    Dort kannst du auch die ".rowHeight" angeben. Wie auf dem Screenshot zu sehen ist ist diese zu klein bzw. deine Labels zu groß.
    Weiterhin generierst du ein Label und benutzt auch die Text-Property vom "Cell Content". Dadurch kommt das seltsame Aussehen.
     
  10. MacApple

    MacApple Lord Grosvenor

    Dabei seit:
    05.01.04
    Beiträge:
    3.470
    Das könnte daran liegen, dass Du weder die Größe des "textView" der aktuellen Zeile anpasst, noch bei der Erzeugung des "textView" eine passende Autoresizingmask setzt. Dein Code setzt die Höhe der Cell nur richtig, wenn keine Cell "for reuse" zur Verfügung steht und deshalb eine neue Instanz erzeugt wird.

    Deine Tabelle sieht, schätze ich, beim ersten Anzeigen noch gut aus, aber sobald Du zu scrollen anfängst, läuft das aus dem Ruder, richtig? Der Grund ist genau der, den ich beschrieben habe.

    MacApple
     
    #10 MacApple, 01.10.09
    Zuletzt bearbeitet: 01.10.09
  11. MacApple

    MacApple Lord Grosvenor

    Dabei seit:
    05.01.04
    Beiträge:
    3.470
    Wo kann er auch die ".rowHeight" angeben? In tableView:numberOfRowsInSection: sicher nicht.

    MacApple
     
  12. sheep

    sheep Fießers Erstling

    Dabei seit:
    09.11.07
    Beiträge:
    126
    Klar geht das:

    z.B.:

    Code:
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    	 
    	tableView.rowHeight=40;
    	
    	
        return 5;
    }
    
     
  13. MacApple

    MacApple Lord Grosvenor

    Dabei seit:
    05.01.04
    Beiträge:
    3.470
    Damit bekommst Du aber höchstens unterschiedliche Zeilenhöhen je Sektion hin. Das ist aber nicht das Ziel von effzehn. Er will nämlich die Höhe jeder Zeile am Inhalt anpassen. Da hilft die globale Zeilenhöhe nicht weiter.

    MacApple
     
  14. effzehn

    effzehn Adams Apfel

    Dabei seit:
    07.02.05
    Beiträge:
    511
    Okay, daraus habe ich geschlossen, dass ich das conditional-statement einfach rauslasse, da jede cell aufgrund der unterschiedlichen Maße individuell behandelt werden muss. Das Ergebnis: es funktioniert, der Fehler tritt nicht mehr auf. Dementsprechend benötige ich auch keinen Cell Identifier:

    Code:
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
    Bin ich auf dem richtigen Weg?


    Aus Neugier frage ich mich noch: da die cells jetzt aber mit jedem Erscheinen neu initialisiert und gezeichnet werden, habe ich damit nicht ab einer bestimmten Länge mit Performanceeinbußen zu rechnen? (Sicher nicht bei dem von mir eingesetzten Datensatz, aber, sagen wir, bei 1000 Einträgen? )

    Vielen Dank für eure Hilfe.


    P.S.: Ich lese gerade: Die cell-Initialisierung mit initWithFrame: ist deprecated seit 3.0. Die konforme Verwendung geht nun nur über initWithStyle:reuiseIdentifier:.
     
  15. MacApple

    MacApple Lord Grosvenor

    Dabei seit:
    05.01.04
    Beiträge:
    3.470
    Nein, entweder Du passt im "else" Zweig die Höhe der Cell an oder Du setzt bei der Erzeugung der Cell die autoresizingMask passend. Zum Beispiel so:
    Code:
    textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    Apple wird sich schon was dabei gedacht haben, die Wiederverwendung der Zellen vorgesehen zu haben. Performance ist ein Aspekt, effektive Speichernutzung ein Anderer.

    MacApple
     

Diese Seite empfehlen