• Apfeltalk ändert einen Teil seiner Allgemeinen Geschäftsbedingungen (AGB), das Löschen von Useraccounts betreffend.
    Näheres könnt Ihr hier nachlesen: AGB-Änderung
  • Viele hassen ihn, manche schwören auf ihn, wir aber möchten unbedingt sehen, welche Bilder Ihr vor Eurem geistigen Auge bzw. vor der Linse Eures iPhone oder iPad sehen könnt, wenn Ihr dieses Wort hört oder lest. Macht mit und beteiligt Euch an unserem Frühjahrsputz ---> Klick

iPhone UITableViewCell: Problem mit dequeueReusableCellWithIdentifier?

effzehn

Luxemburger Triumph
Registriert
07.02.05
Beiträge
510
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:


attachment.php


(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?
 

sheep

Fießers Erstling
Registriert
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];
 

effzehn

Luxemburger Triumph
Registriert
07.02.05
Beiträge
510
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.
 

sheep

Fießers Erstling
Registriert
09.11.07
Beiträge
126

effzehn

Luxemburger Triumph
Registriert
07.02.05
Beiträge
510
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:


attachment.php


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

  • Bildschirmfoto 2009-10-01 um 13.47.08.png
    Bildschirmfoto 2009-10-01 um 13.47.08.png
    160,5 KB · Aufrufe: 1.253

sheep

Fießers Erstling
Registriert
09.11.07
Beiträge
126
Welche Höhe hast du denn hier angegeben:
Code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

?
 

effzehn

Luxemburger Triumph
Registriert
07.02.05
Beiträge
510
Welche Höhe hast du denn hier angegeben:
Code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

?

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).
 

MacApple

Schöner von Bath
Registriert
05.01.04
Beiträge
3.652
Ü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?
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
 

sheep

Fießers Erstling
Registriert
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.
 

MacApple

Schöner von Bath
Registriert
05.01.04
Beiträge
3.652
Man sieht, dass die Berechnung der CellView korrekt ist. Dagegen sind die Labels zu hoch und überschreiben die danach gezeichneten Labels.
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
 
Zuletzt bearbeitet:

sheep

Fießers Erstling
Registriert
09.11.07
Beiträge
126
Klar geht das:

z.B.:

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

MacApple

Schöner von Bath
Registriert
05.01.04
Beiträge
3.652
Klar geht das:

z.B.:

Code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
	 
	tableView.rowHeight=40;
	
	
    return 5;
}
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
 

effzehn

Luxemburger Triumph
Registriert
07.02.05
Beiträge
510
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.

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:.
 

MacApple

Schöner von Bath
Registriert
05.01.04
Beiträge
3.652
Bin ich auf dem richtigen Weg?
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;

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?
Apple wird sich schon was dabei gedacht haben, die Wiederverwendung der Zellen vorgesehen zu haben. Performance ist ein Aspekt, effektive Speichernutzung ein Anderer.

MacApple