2012-03-31 19 views
37

Ho uno UITableView non scorrevole in un UIScrollView. Ho impostato la dimensione del fotogramma di UITableView in base alle dimensioni del suo contenuto.Quando viene impostato un contentSize di UITableView?

Quando aggiungo una riga a UITableView, chiamo insertRowsAtIndexPaths:withRowAnimation: su UITableView. Poi io chiamo un metodo per ridimensionare la cornice del UITableView:

- (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; 
} 

Sembra però che il contentSize non è stato aggiornato a questo punto. Se calcoli manualmente il frame nel metodo precedente in base al numero di righe e sezioni, il metodo funziona correttamente.

La mia domanda è, come posso ottenere il UITableView per aggiornare il suo contentSize? Suppongo che potrei chiamare reloadData e che probabilmente lo farebbe, ma sembra inefficiente ricaricare l'intera tabella quando sto solo inserendo una cella.

risposta

61

È possibile forzare l'UITableView per calcolare la dimensione del contenuto chiamando layoutIfNeeded sul UITableView.

Esempio per sottoclasse UITableViewController che dovrebbe essere in una vista contenitore con dimensioni variabili:

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

Aggiorna 2016/07/28 Questo è un esempio Swift testata della stessa cosa. Dovrebbe funzionare ma se non si prega di lasciare un commento. Ci potrebbero essere modi più belli o più idiomatici per farlo. Sfortunatamente non ho molta familiarità con Swift.

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

Ho provato questo, ma non ha funzionato per me. Ma sono riuscito a trovare un'altra soluzione che ho postato qui: http://stackoverflow.com/questions/17634617/when-does-uitableview-content-size-update-with-row-insert-delete-animation/18665064 # 18665064 – Daren

+1

Oh, questo è solo per il contenuto iniziale. Il tuo chilometraggio può variare per gli altri casi quando la dimensione viene modificata in seguito. – Farthen

+7

Nota per coloro per i quali questo non funziona - tableView: estimatedHeightForRowAtIndexPath crea problemi con contentSize, quindi potresti avere fortuna semplicemente rimuovendo la tua implementazione. – weienw

13

Prova questo:

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

Ho provato questo e ha funzionato, grazie! Ma sebbene risolva il mio problema, in realtà non risponde alla mia domanda su quando viene impostata la dimensione del contenuto. – Darren

+1

Sospetto che venga impostato in 'layoutSubviews', ma non ho voglia di verificarlo al momento. –

19

Mentre io non amo KVO (valore-chiave Observing), è un abbastanza buon modo per sapere esattamente quando è cambiato del contentSize vostro tavolo (e non solo chiamare i tuoi metodi di aggiornamento in un mucchio di luoghi casuali) . È piuttosto semplice da usare (anche se un po 'criptico e implicito). Per osservare i cambiamenti sul vostro tavolo di contentSize, effettuare le seguenti operazioni:

1) Diventa un osservatore della proprietà della vostra tabella contentSize in questo modo:

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

Questo è di solito fatto nel controller della vista che contiene l'tableView (come in viewDidLoad:, ad esempio).

2) Implementare il metodo di osservazione KVO e apportare le modifiche necessarie:

- (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) Rimuovere il controller vista come osservatore ad un certo punto logico (io lo chiamo in dealloc).A tale scopo, in questo modo:

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

Grazie per avermi ricordato dell'uomo KVO. Ho trovato questa l'unica soluzione che funziona sul mio layout abbastanza complesso. – preams

+0

Lasciatemi fare un appunto qui. Usavo KVO per lo scorrimento come hai detto. Tuttavia, non funzionerà correttamente dopo iOS 11 se stai usando beginUpdates e endUpdates. In iOS 11, il contenutoSize cambia durante gli aggiornamenti, ad esempio se si scorre non appena contentSize cambia, si modificherà il posizionamento della vista tabella durante gli aggiornamenti, che porteranno a bug molto strani. Controlla la mia domanda [qui] (https://stackoverflow.com/questions/46587878/uitableview-header-not-disappearing/46854828#46854828), l'utilizzo del KVO è stato il motivo del mio errore. –

+0

Qui deve essere notato: utilizzando Runloop per evitare che l'esecuzione ricorsiva porti a una cattiva eccezione. – 0xDatou

0

Questo ha funzionato per me, quando si affrontano i temi del Tableview ottenere la giusta dimensione dopo l'aggiunta/rimozione di file

Utilizzando

tableView.layoutIfNeeded() 

e l'impostazione

tableView.estimatedRowHeight = UITableViewAutomaticDimension 
3
  1. Add osservatore (nel mio campione viewDidLoad

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

    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. Rimuovere osservatore quando non serve

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

È possibile modificare la cornice del piè di pagina. Io chiamo prima di apparire animate di piè di pagina.

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) 
    } 
Problemi correlati