2012-03-31 2 views
37

У меня есть прокрутка UITableView в UIScrollView. Я установил размер кадра UITableView в размере его содержимого.Когда устанавливается контент contentSize UITableView?

Когда я добавляю строку в UITableView, я звоню insertRowsAtIndexPaths:withRowAnimation: на UITableView. Тогда я вызываю метод, чтобы изменить размер кадра 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; 
} 

Похоже, однако, что contentSize не была обновлена ​​в данный момент. Если я вручную вычисляю фрейм в указанном выше методе на основе количества строк и разделов, то метод работает правильно.

Мой вопрос: как я могу получить UITableView, чтобы обновить его contentSize? Полагаю, я мог бы позвонить reloadData, и это, вероятно, сделало бы это, но кажется неэффективным перезагрузить всю таблицу, когда я просто вставляю одну ячейку.

ответ

61

Вы можете заставить UITableView рассчитать размер содержимого, вызвав layoutIfNeeded на UITableView.

Пример для подкласса UITableViewController, который должен быть в представлении контейнера с переменным размером:

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

Обновление 2016-07-28 Это непроверенный Свифта пример того же. Он должен работать, но если он не оставит комментария. Там могут быть более приятные или более идиоматические способы сделать это. К сожалению, я не очень хорошо знаком с Свифт.

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

Я пробовал это, но это не сработало для меня. Но я смог придумать другое решение, которое я разместил здесь: http://stackoverflow.com/questions/17634617/when-does-uitableview-content-size-update-with-row-insert-delete-animation/18665064 # 18665064 – Daren

+1

О, это только для исходного contentSize. Ваш пробег может отличаться для других случаев, когда размер изменяется после этого. – Farthen

+7

Примечание для тех, для кого это не работает - tableView: оцененныйHeightForRowAtIndexPath messes with contentSize, так что вам может повезти, просто удалив его реализацию. – weienw

13

Попробуйте это:

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

Я пробовал это, и он действительно работал, спасибо! Но хотя он и решает мою проблему, на самом деле он не отвечает на мой вопрос о том, когда размер контента будет установлен. – Darren

+1

Я подозреваю, что он установлен в 'layoutSubviews', но мне не хочется проверять это на данный момент. –

19

В то время как я не люблю КВО (Key-Value наблюдения), это довольно хороший способ, чтобы точно знать, когда изменились ваши таблицы contentSize (а не просто вызова ваших методов обновления в куче случайных мест) , Это довольно просто использовать (хотя и несколько загадочно и неявно). Для того, чтобы наблюдать за изменениями на вашей таблицы contentSize, выполните следующие действия:

1) Стать наблюдателем contentSize собственности вашей таблицы следующим образом:

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

Это обычно делается в контроллере представления, который содержит tableView (например, в viewDidLoad:, например).

2) осуществлять КВО метода наблюдения и сделать необходимые изменения:

- (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) Снимите контроллер представления в качестве наблюдателя в какой-то логической точке (я называю это в dealloc).Вы можете сделать это следующим образом:

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

Спасибо, что напомнил мне человека КВО. Я нашел это единственное решение, которое работает на моем довольно сложном макете. – preams

+0

Позвольте мне сделать заметку здесь. Раньше я использовал KVO для прокрутки, как вы упомянули. Однако после iOS 11 он будет работать неправильно, если вы используете beginUpdates и endUpdates. В iOS 11, contentSize изменяется во время обновлений, то есть, если вы прокручиваете, как только изменяется ContentSize, вы будете изменять расположение представления таблицы во время обновлений, что приведет к очень странным ошибкам. Проверьте мой вопрос [здесь] (https://stackoverflow.com/questions/46587878/uitableview-header-not-disappearing/46854828#46854828), использование KVO стало причиной моей ошибки. –

+0

Здесь требуется уведомление: использование Runloop, чтобы избежать рекурсивного запуска, вызывающего исключение. – 0xDatou

0

Это работает для меня, при решении вопросов о Tableview получения нужного размера после добавления/удаления строк

Использования

tableView.layoutIfNeeded() 

и установки

tableView.estimatedRowHeight = UITableViewAutomaticDimension 
3
  1. Добавить наблюдатель (в моем примере в viewDidLoad

    tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil) 
    
  2. Наблюдайте значение

    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. Удалить наблюдателя, когда не требуется

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

Вы можете изменить рамку колонтитула. Я вызываю перед анимированным появлением нижнего колонтитула.

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) 
    } 
Смежные вопросы