2012-03-31 15 views
37

Ich habe eine nicht scrollende UITableView in einem UIScrollView. Ich habe die Framegröße des UITableView auf seine Inhaltsgröße festgelegt.Wann wird die contentSize eines UITableView gesetzt?

Wenn ich eine Zeile zum UITableView hinzufüge, rufe ich insertRowsAtIndexPaths:withRowAnimation an: auf dem UITableView. Dann rufe ich eine Methode den Rahmen der UITableView, um die Größe:

- (void)resizeTableViewFrameHeight 
{ 
    // Table view does not scroll, so its frame height should be equal to its contentSize height 
    CGRect frame = self.tableView.frame; 
    frame.size = self.tableView.contentSize; 
    self.tableView.frame = frame; 
} 

es aber scheint, dass die contentSize hat an dieser Stelle nicht aktualisiert worden. Wenn ich den Rahmen in der obigen Methode basierend auf der Anzahl der Zeilen und Abschnitte manuell berechne, funktioniert die Methode ordnungsgemäß.

Meine Frage ist, wie kann ich die UITableView erhalten, um seine contentSize zu aktualisieren? Ich nehme an, ich könnte reloadData anrufen und das würde es wahrscheinlich tun, aber es scheint ineffizient, die gesamte Tabelle neu zu laden, wenn ich gerade eine Zelle einfüge.

Antwort

61

Sie können die UITableView zwingen, die Größe des Inhalts zu berechnen, indem layoutIfNeeded auf der UITableView aufrufen.

Beispiel für eine UITableViewController Unterklasse, die in einem Behälter Ansicht mit variabler Größe sein sollten:

- (CGSize)preferredContentSize 
{ 
    // Force the table view to calculate its height 
    [self.tableView layoutIfNeeded]; 
    return self.tableView.contentSize; 
} 

aktualisiert 2016.07.28 Dies ist ein nicht getestet Swift Beispiel für die gleiche Sache. Es sollte funktionieren, aber wenn es nicht bitte einen Kommentar hinterlassen. Es könnte nettere oder mehr idiomatische Wege geben, dies zu tun. Leider bin ich mit Swift nicht sehr vertraut.

override var preferredContentSize: CGSize { 
    get { 
     self.tableView.layoutIfNeeded() 
     return self.tableView.contentSize 
    } 
    set {} 
} 
+0

Ich habe das versucht, aber es hat nicht für mich funktioniert. Aber ich konnte mir eine andere Lösung einfallen lassen, die ich hier veröffentlicht habe: http://StackOverflow.com/questions/17634617/When-does-guideview-content-size-update-with-row-insert-delete-animation/18665064 # 18665064 – Daren

+1

Oh, das ist nur für die anfängliche contentSize. Ihr Kilometerstand kann in den anderen Fällen variieren, wenn die Größe später geändert wird. – Farthen

+7

Hinweis für diejenigen, für die dies nicht funktioniert - tableView: decuredHeightForRowAtIndexPath versagt mit contentSize, also können Sie Glück haben, indem Sie einfach Ihre Implementierung entfernen. – weienw

13

Versuchen Sie folgendes:

- (void)resizeTableViewFrameHeight 
{ 
    UITableView *tableView = self.tableView; 
    CGRect frame = tableView.frame; 
    frame.size.height = [tableView sizeThatFits:CGSizeMake(frame.size.width, HUGE_VALF)].height; 
    tableView.frame = frame; 
} 
+1

Ich habe es versucht und es hat funktioniert, danke! Aber obwohl es mein Problem löst, beantwortet es nicht wirklich meine Frage, wann die Inhaltsgröße eingestellt wird. – Darren

+1

Ich vermute, dass es in 'layoutSubviews' gesetzt wird, aber ich habe keine Lust das im Moment zu überprüfen. –

19

Während ich nicht lieben KVO (Key-Wert Observing), es ist ein ziemlich guter Weg, um genau zu wissen, wann Sie Ihre Tabelle der contentSize geändert hat (und nicht nur in einem Bündel von zufälligen Stellen Sie Ihre Update-Methoden aufrufen) . Es ist ziemlich einfach zu benutzen (wenn auch etwas kryptisch und implizit). Zu beobachten Änderungen auf dem Tisch des contentSize, gehen Sie wie folgt vor:

1) Werden Beobachter der Tabelle der contentSize Eigenschaft wie folgt:

[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:NULL]; 

Dies wird in der Regel im View-Controller durchgeführt, die die tableView hält (wie zB in viewDidLoad:).

2) Implementieren Sie die KVO-Methode zu beobachten und die Änderungen, die Sie brauchen:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { 
    if(object == self.tableView && [keyPath isEqualToString:@"contentSize"]) { 
     // perform your updates here 
    } 
} 

3) Entfernen Sie die View-Controller als Beobachter an einem gewissen logischen Punkt (ich es in dealloc nennen).Sie tun dies, wie so:

- (void)dealloc { 
    [self.tableView removeObserver:self forKeyPath:@"contentSize"]; 
} 
+1

Danke für die Erinnerung an KVO Mann. Ich fand dies die einzige Lösung, die auf meinem ziemlich komplexen Layout funktioniert. – preams

+0

Lassen Sie mich hier eine Notiz machen. Ich benutzte KVO zum Scrollen, wie du erwähnt hast. Nach iOS 11 wird es jedoch nicht ordnungsgemäß funktionieren, wenn Sie beginUpdates und endUpdates verwenden. In iOS 11 ändert sich contentSize während der Aktualisierungen, d. H. Wenn Sie scrollen, sobald sich contentSize ändert, ändern Sie die Positionierung der Tabellenansicht während der Aktualisierungen, was Sie zu sehr seltsamen Fehlern bringt. Überprüfen Sie meine Frage [hier] (https://stackoverflow.com/questions/46587878/uitableview-header-not-disappearing/46854828#46854828), mit der KVO war der Grund für meinen Fehler. –

+0

Hier ist eine Notiz erforderlich: Verwenden von Runloop, um rekursive Ausführung von Fehlern zu vermeiden. – 0xDatou

0

Das ist für mich gearbeitet, wenn die Bewältigung der Probleme des Tableview nach der richtigen Größe erhalten Hinzufügen/Entfernen von Reihen

tableView.layoutIfNeeded() 

Verwendung und Einstellung

3
  1. In Beobachter (in meinem Beispiel in viewDidLoad

    tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil) 
    
  2. beachten Wert

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 
        if let obj = object as? UITableView { 
         if obj == self.tableView && keyPath == "contentSize" { 
          if let newSize = change?[NSKeyValueChangeKey.newKey] as? CGSize { 
           //do stuff here 
          } 
         } 
        } 
    } 
    
  3. Beobachter entfernen, wenn sie nicht

    benötigt
    deinit { 
        self.tableView.removeObserver(self, forKeyPath: "contentSize") 
    } 
    
0

Sie können den Rahmen der Fußzeile ändern. Ich rufe vorher animierte Fußzeile an.

if self.newTableView.contentSize.height < self.newTableView.frame.height { 
     let additionalHeight: CGFloat = self.newTableView.frame.height - self.newTableView.contentSize.height 

     let tableFooterView = self.newTableView.tableFooterView! 

     let x = tableFooterView.frame.origin.x 
     let y = tableFooterView.frame.origin.y + additionalHeight 
     let width = tableFooterView.frame.width 
     let height = tableFooterView.frame.height 

     tableFooterView.frame = CGRect(x: x, y: y, width: width, height: height) 
    } 
Verwandte Themen