2014-01-07 8 views
76

Я воткнула головой о стену в течение последних 3 или 4 часов, и я не могу понять это. У меня есть UIViewController с полноэкранным UITableView внутри него (на экране есть что-то другое, поэтому я не могу использовать UITableViewController), и я хочу, чтобы мой tableHeaderView изменил размер с автозапуском. Излишне говорить, что он не сотрудничает.Как установить высоту таблицыHeaderView (UITableView) с автозапуском?

См. Снимок экрана ниже.

enter image description here

Поскольку overviewLabel (например, в «Список обзора информации здесь.» Текст) имеет динамическое содержание, я использую autolayout, чтобы изменить это, и это SuperView. У меня есть все изменения размера, за исключением таблицыHeaderView, которая находится прямо под Paralax Table View в hiearchy.

Единственный способ я нашел, чтобы изменить это представление заголовка является программно, с помощью следующего кода:

CGRect headerFrame = self.headerView.frame; 
headerFrame.size.height = headerFrameHeight; 
self.headerView.frame = headerFrame; 
[self.listTableView setTableHeaderView:self.headerView]; 

В этом случае headerFrameHeight является ручное вычисление высоты tableViewHeader следующим (innerHeaderView является белая область, или второй «View», headerView является tableHeaderView):

CGFloat startingY = self.innerHeaderView.frame.origin.y + self.overviewLabel.frame.origin.y; 
CGRect overviewSize = [self.overviewLabel.text 
         boundingRectWithSize:CGSizeMake(290.f, CGFLOAT_MAX) 
         options:NSStringDrawingUsesLineFragmentOrigin 
         attributes:@{NSFontAttributeName: self.overviewLabel.font} 
         context:nil]; 
CGFloat overviewHeight = overviewSize.size.height; 
CGFloat overviewPadding = ([self.overviewLabel.text length] > 0) ? 10 : 0; // If there's no overviewText, eliminate the padding in the overall height. 
CGFloat headerFrameHeight = ceilf(startingY + overviewHeight + overviewPadding + 21.f + 10.f); 

ручного расчёта работы, но это неуклюжее и подвержен ошибкам, если ситуация не изменится в будущем. То, что я хочу, чтобы иметь возможность сделать, - это сделать автоматическое изменение размера таблицыHeaderView на основе предоставленных ограничений, например, в любом другом месте. Но для жизни я не могу понять.

В этом отношении есть несколько сообщений, но ни один из них не ясен и в конечном итоге запутывает меня больше. Вот некоторые из них:

Это действительно не Сенс e, чтобы изменить свойство translatesAutoresizingMaskIntoConstraints на NO, поскольку это просто вызывает ошибки для меня и не имеет смысла концептуально в любом случае.

Любая помощь действительно оценена!

EDIT 1: Благодаря предложению TomSwift, я смог понять это. Вместо того, чтобы вручную вычислять высоту обзора, я могу рассчитать его для меня следующим образом, а затем снова установить tableHeaderView, как и раньше.

[self.headerView setNeedsLayout]; 
[self.headerView layoutIfNeeded]; 
CGFloat height = [self.innerHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + self.innerHeaderView.frame.origin.y; // adding the origin because innerHeaderView starts partway down headerView. 

CGRect headerFrame = self.headerView.frame; 
headerFrame.size.height = height; 
self.headerView.frame = headerFrame; 
[self.listTableView setTableHeaderView:self.headerView]; 

Edit 2: Как уже отмечалось, решение Отвечали в Edit 1, кажется, не работает в viewDidLoad. Однако, похоже, он работает в viewWillLayoutSubviews.Пример кода ниже:

// Note 1: The variable names below don't match the variables above - this is intended to be a simplified "final" answer. 
// Note 2: _headerView was previously assigned to tableViewHeader (in loadView in my case since I now do everything programatically). 
// Note 3: autoLayout code can be setup programatically in updateViewConstraints. 
- (void)viewWillLayoutSubviews { 
    [super viewWillLayoutSubviews]; 

    [_headerWrapper setNeedsLayout]; 
    [_headerWrapper layoutIfNeeded]; 
    CGFloat height = [_headerWrapper systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; 

    CGRect headerFrame = _headerWrapper.frame; 
    headerFrame.size.height = height; 
    _headerWrapper.frame = headerFrame; 
    _tableView.tableHeaderView = _headerWrapper; 
} 
+1

'setTableHeaderView' не работает на Xcode6. Проблема в том, что ячейки перекрываются таблицейHeaderView. Однако он работает на Xcode5 –

+0

@DawnSong действительно знает решение для Xcode6, чтобы я мог обновить ответ? –

+1

После большого количества проб, я использую UITableViewCell вместо tableHeaderView, и он работает. –

ответ

33

Вы должны использовать метод UIView systemLayoutSizeFittingSize:, чтобы получить минимальный размер ограничивающего вашего зрения заголовка.

я обеспечиваю дальнейшее обсуждение использования этого API в этом Q/A:

How to resize superview to fit all subviews with autolayout?

+0

Он возвращается с высотой = 0, хотя я не уверен на 100%, что я настроил его правильно, так как это не UITableViewCell, поэтому ContentView не будет вызывать «systemLayoutSizeFittingSize». Что я пытался: [self.headerView setNeedsLayout]; [self.headerView layoutIfNeeded]; CGFloat height = [self.headerView systemLayoutSizeFittingSize: UILayoutFittingCompressedSize] .height; CGRect headerFrame = self.headerView.frame; headerFrame.size.height = height; self.headerView.frame = headerFrame; [self.listTableView setTableHeaderView: self.headerView]; Я также подтвердил, что preferredMax ... действителен. –

+0

Aha! Выяснилось, мне нужно было сделать [self.innerHeaderView systemLayoutSizeFittingSize ...] вместо self.headerView, так как это то, что имеет фактический динамический контент. Благодаря тонну! –

3

Ваше решение с использованием systemLayoutSizeFittingSize: работает, если вид заголовка просто обновляется один раз на каждом представлении вида. В моем случае представление заголовка обновлялось несколько раз, чтобы отразить изменения состояния. Но systemLayoutSizeFittingSize: всегда сообщается о том же размере. То есть размер, соответствующий первому обновлению.

Чтобы получить systemLayoutSizeFittingSize: вернуть правильный размер после каждого обновления я должен был сначала удалить вид заголовка таблицы перед обновлением его и повторно добавить его:

self.listTableView.tableHeaderView = nil; 
[self.headerView removeFromSuperview]; 
20

Я действительно боролся с этим и plonking установка в viewDidLoad не сработала для меня, так как кадр не установлен в viewDidLoad, я также столкнулся с множеством грязных предупреждений, когда инкапсулированная высота макета заголовка была уменьшена до 0. Я только заметил проблему на iPad, когда представление tableView в представлении формы.

Что решило проблему для меня, было установить tableViewHeader в viewWillLayoutSubviews, а не в viewDidLoad.

func viewWillLayoutSubviews() { 
     super.viewWillLayoutSubviews() 
     if tableView.tableViewHeaderView == nil { 
      let header: MyHeaderView = MyHeaderView.createHeaderView() 
      header.setNeedsUpdateConstraints() 
      header.updateConstraintsIfNeeded() 
      header.frame = CGRectMake(0, 0, CGRectGetWidth(tableView.bounds), CGFloat.max) 
      var newFrame = header.frame 
      header.setNeedsLayout() 
      header.layoutIfNeeded() 
      let newSize = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) 
      newFrame.size.height = newSize.height 
      header.frame = newFrame 
      self.tableView.tableHeaderView = header 
     } 
    } 
+0

Это действительно отлично работает! – Vishnuvardhan

+0

Наконец-то работающее решение для меня! Спасибо – konradowy

11

Я нашел элегантный способ использования автоматического макета для изменения размеров заголовков таблиц с анимацией и без нее.

Просто добавьте это в свой контроллер просмотра.

func sizeHeaderToFit(tableView: UITableView) { 
    if let headerView = tableView.tableHeaderView { 
     let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height 
     var frame = headerView.frame 
     frame.size.height = height 
     headerView.frame = frame 
     tableView.tableHeaderView = headerView 
     headerView.setNeedsLayout() 
     headerView.layoutIfNeeded() 
    } 
} 

Чтобы изменить размер в соответствии с динамически изменяющейся ярлыком:

@IBAction func addMoreText(sender: AnyObject) { 
    self.label.text = self.label.text! + "\nThis header can dynamically resize according to its contents." 
} 

override func viewDidLayoutSubviews() { 
    // viewDidLayoutSubviews is called when labels change. 
    super.viewDidLayoutSubviews() 
    sizeHeaderToFit(tableView) 
} 

Чтобы анимировать изменение размера в соответствии с изменениями в ограничении:

@IBOutlet weak var makeThisTallerHeight: NSLayoutConstraint! 

@IBAction func makeThisTaller(sender: AnyObject) { 

    UIView.animateWithDuration(0.3) { 
     self.tableView.beginUpdates() 
     self.makeThisTallerHeight.constant += 20 
     self.sizeHeaderToFit(self.tableView) 
     self.tableView.endUpdates() 
    } 
} 

Посмотреть проект AutoResizingHeader увидеть это действие. https://github.com/p-sun/Swift2-iOS9-UI

AutoResizingHeader

+0

Эй, п-солнце. Спасибо за отличный пример. Кажется, у меня проблема с tableViewHeader. Я пытался использовать те же ограничения, что и в вашем примере, но не работал. Как именно я должен добавить ограничения для метки, которые я хочу увеличить? Спасибо за любое предложение. – Bonnke

+1

Убедитесь, что вы наклеили ярлык, который вы хотите сделать выше, на '@IBOutlet makeThisTaller' и' @IBAction fun makeThisTaller', как в примере. Кроме того, ограничьте все стороны вашей метки таблицей TableHeader (например, сверху, снизу, слева и справа). –

+0

Большое спасибо! Наконец, решив добавить эту строку: 'lblFeedDescription.preferredMaxLayoutWidth = lblFeedDescription.bounds.width' где ярлык - это тот, который я хочу увеличить. Благодаря ! – Bonnke

1

Это работает для меня на ios10 и Xcode 8

func layoutTableHeaderView() { 

    guard let headerView = tableView.tableHeaderView else { return } 
    headerView.translatesAutoresizingMaskIntoConstraints = false 

    let headerWidth = headerView.bounds.size.width; 
    let temporaryWidthConstraints = NSLayoutConstraint.constraintsWithVisualFormat("[headerView(width)]", options: NSLayoutFormatOptions(rawValue: UInt(0)), metrics: ["width": headerWidth], views: ["headerView": headerView]) 

    headerView.addConstraints(temporaryWidthConstraints) 

    headerView.setNeedsLayout() 
    headerView.layoutIfNeeded() 

    let headerSize = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) 
    let height = headerSize.height 
    var frame = headerView.frame 

    frame.size.height = height 
    headerView.frame = frame 

    self.tableView.tableHeaderView = headerView 

    headerView.removeConstraints(temporaryWidthConstraints) 
    headerView.translatesAutoresizingMaskIntoConstraints = true 

} 
0

В моем случае viewDidLayoutSubviews работал лучше. viewWillLayoutSubviews вызывает появление белых линий tableView. Также я добавил, проверяет, существует ли мой объект headerView.

- (void)viewDidLayoutSubviews 
{ 
    [super viewDidLayoutSubviews]; 

    if (! self.userHeaderView) { 
     // Setup HeaderView 
     self.userHeaderView = [[[NSBundle mainBundle] loadNibNamed:@"SSUserHeaderView" owner:self options:nil] objectAtIndex:0]; 
     [self.userHeaderView setNeedsLayout]; 
     [self.userHeaderView layoutIfNeeded]; 
     CGFloat height = [self.userHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; 
     CGRect headerFrame = self.userHeaderView.frame; 
     headerFrame.size.height = height; 
     self.userHeaderView.frame = headerFrame; 
     self.tableView.tableHeaderView = self.userHeaderView; 

     // Update HeaderView with data 
     [self.userHeaderView updateWithProfileData]; 
    } 
} 
0

Это решение изменяет размер tableHeaderView и позволяет избежать бесконечного цикла в viewDidLayoutSubviews() метод, который я имел с некоторыми из других ответов здесь:

override func viewDidLayoutSubviews() { 
    super.viewDidLayoutSubviews() 

    if let headerView = tableView.tableHeaderView { 
     let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height 
     var headerFrame = headerView.frame 

     // comparison necessary to avoid infinite loop 
     if height != headerFrame.size.height { 
      headerFrame.size.height = height 
      headerView.frame = headerFrame 
      tableView.tableHeaderView = headerView 
     } 
    } 
} 

Смотрите также этот пост: https://stackoverflow.com/a/34689293/1245231

Смежные вопросы