2 Stimmen

Warum werden die UItableViewCell-Bilder erst geladen, wenn gescrollt wird?

Mein Code lädt Bilder asynchron herunter, indem er InternetImage im tableView:cellForRowAtIndexPath: Methode verwendet. Dabei initialisiert er ein IntentImage mit initWithURL und ruft downloadImage auf. Dieser Code funktioniert einwandfrei, nachdem man zu einer neuen Zelle UITableViewCell in der UITableView gescrollt ist, aber davor nicht, obwohl die URL in beiden Fällen korrekt ist. Keine der NSURLConnection-Delegate-Methoden von InternetImage wird aufgerufen, um mich über Erfolg oder Misserfolg der Verbindung zu informieren, wie es eigentlich sollte. Das Aufrufen von reloadData:, setNeedsDisplay: und setNeedsLayout: bewirkt nichts, da das Bild nicht heruntergeladen wird.

Hier ist der Code aus meiner Unterklasse von UITableViewController:

   - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        Object *object = (Object *)[self.array objectAtIndex:indexPath.row];

        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
        if (cell == nil) {
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"] autorelease];
        }

        cell.textLabel.text = object.attribute;

        if (object.image == nil && object != nil) {
            NSString *URLString = [[MyAppDelegate imageURLFromLink:(object.link) withExtension:@".jpg"]absoluteString];

            InternetImage *asynchImage = [[InternetImage alloc] initWithUrl:URLString object:object];
            [asynchImage downloadImage:self];
        }

        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

        if (object.image != nil && object.thumbnailImage == nil) {

            UIImage *image= (UIImage *) object.image;
            UIImage *thumbnail = [image _imageScaledToSize:CGSizeMake(75.0, 50.0) interpolationQuality:20];
            object.thumbnailImage = thumbnail;
        }
        cell.imageView.image = object.thumbnailImage;

        return cell;
    }

2voto

dmercredi Punkte 2092

Eine Lösung, die für mich funktioniert hat, besteht darin, die Verantwortung für den Download auf das Objekt zu verlagern und die Verantwortung für das Aktualisieren des Bildes auf die Tabellenzelle selbst zu übertragen. Wenn das Objekt auf dem Bildschirm sichtbar ist und eine entsprechende Tabellenzelle hat, wird das Bild sofort aktualisiert, sobald es heruntergeladen wurde.

Dieser Ansatz würde die folgenden Änderungen erfordern:

  • Verlagern Sie die Verantwortung für das Herunterladen des Bildes auf das Objekt.
  • Ändern Sie das Objekt, um ein Ereignis auszulösen/einen Delegierten anzurufen/benachrichtigen, dass sich ein Eigenschaftswert geändert hat, wenn sich das Bild geändert hat.
  • Unterklasse UITableViewCell, um über das Objekt Bescheid zu wissen, das es anzeigen wird, und fügen Sie sich selbst als Änderungsdelegierter zum Objekt hinzu, wenn es zugewiesen wird.

Hier ist eine Skizze, wie dies funktionieren könnte:

// in der UITableViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  Object *object = (Object *)[self.array objectAtIndex:indexPath.row];

  ObjectTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"objectcell"];
  if (nil == cell) {
    cell = [[[ObjectTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"objectcell"] autorelease];
  }

  cell.object = object;

  return cell;
}

// in Object

- (void) downloadImageIfNeeded {
  if (nil == image && nil == internetImage) {
     NSString *URLString = [[MyAppDelegate imageURLFromLink:(object.link) withExtension:@".jpg"]absoluteString];

     internetImage = [[InternetImage alloc] initWithUrl:URLString object:object];
     [internetImage downloadImage:self];
  }
} 

- (void) internetImageReady:(InternetImage*)downloadedImage {
  self.image = downloadedImage.image;
  self.thumbnailImage = [image _imageScaledToSize:CGSizeMake(75.0, 50.0) interpolationQuality:20];

  // notify the delegate
  [imageDelegate imageChanged: self];
}

// in ObjectTableViewCell.h
@property (nonatomic, retain) Object *object;

// in ObjectTableViewCell.m
- (Object *) object {
  return object;
}

- (void) setObject: (Object *) obj {
  if (obj != object) {
    object.imageDelegate = nil;
    [object release];
    object = nil;

    if (nil != object) {
      object = [obj retain];
      object.imageDelegate = self;

      [object downloadImageIfNeeded];
      self.textLabel.text = object.attribute;
      self.imageView.image = object.thumbnailImage;

      self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
   }
}

- (void) imageChanged: (Object *) o {
  self.imageView.image = object.thumbnailImage;
}

1voto

firstresponder Punkte 4830

Wenn Sie sich fragen, warum Ihre Bilder nicht im Hintergrund heruntergeladen werden, sobald Ihre Tabelle geladen ist, liegt das daran, dass tableView:cellForRowAtIndexPath: nur mit indexPaths von sichtbaren Zellen aufgerufen wird.

Wenn Sie möchten, dass alle Ihre Bilder auf einmal heruntergeladen werden und nicht erst, wenn eine Zelle auf dem Bildschirm erscheint, schreiben Sie eine Methode, die durch Ihre array Eigenschaft geht und dort InternetImage Objekte instanziiert. Ein geeigneter Ort hierfür könnte in viewDidLoad sein.

Implementieren Sie dann tableView:willDisplayCell:forRowAtIndexPath: in Ihrem UITableView Delegate und machen Sie es dafür verantwortlich, das richtige InternetImage Objekt der imageView Eigenschaft jeder Zelle zuzuweisen.

1voto

Zargony Punkte 8975

Es scheint tatsächlich so zu sein, dass NSURLConnections keine Daten abrufen, solange die Tabelle gescrollt wird. Sobald das Scrollen stoppt, wird sofort fortgesetzt, Daten abzurufen. Dieses Verhalten kann jedoch in jeder iPhone-Anwendung beobachtet werden (Bilder werden nicht geladen, solange die Tabelle gescrollt wird) und am Ende habe ich mich entschieden, das auch in meiner Anwendung zu akzeptieren. Ich denke, Apple hat das mit Absicht gemacht. Vielleicht um zu verhindern, dass das Gerät den Server mit einer großen Anzahl von Bildanfragen belastet, wenn der Benutzer sehr schnell scrollt; oder vielleicht sogar um das Scrollen jederzeit für den Benutzer geschmeidig zu halten.

Es wäre jedoch schön, wenn es zumindest funktionieren würde, während langsam gescrollt wird und nur das Herunterladen blockiert, wenn schnell gescrollt wird.

0voto

John Ballinger Punkte 7184

Ich habe tatsächlich einige Zeit damit verbracht, den Code anzusehen.

In InternetImage habe ich bemerkt, dass es diese Funktion gibt. Was du tun musst, ist diese Funktion dazu zu bringen, deinem TableView mitzuteilen, dass es "aktualisiert" werden soll. Dies könntest du erreichen, indem du eine Delegatenfunktion an deine Klasse übergibst, die später aufgerufen wird, aber jetzt wird es wirklich knifflig. Denn wenn du die Ansicht verlässt, musst du die Delegatenfunktion für jedes Bild, das du herunterladen möchtest, auf "nil" setzen.

-(void) internetImageReady:(InternetImage*)downloadedImage
{   
    // Das Bild wurde heruntergeladen. Setze das Bild in den UIImageView
    [imageView setImage:downloadedImage.Image];
}

Bitte poste die Antwort hier, wenn du sie hast. Ich bin gespannt, ob dies das Problem gelöst hat oder nicht.

0voto

Sandeep Kumar Punkte 1585

Gute Herangehensweise? Erste Kontrolle, wo Sie das ImageView zuweisen? 2. Wenn Ihr Bild fest ist, bedeutet das spezifisch, als wäre der gesamte Code in

if (cell == nil)
{

}

Neben dieser Bedingung. Ich hatte ein ähnliches Problem und habe es so gelöst. Lass mich wissen, ob das funktioniert oder nicht.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X