Как установить высоту tableHeaderView (UITableView) с autolayout?
Я разбил голову об стену с этим в течение последних 3 или 4 часов, и я не могу понять это. У меня есть UIViewController с полноэкранным UITableView внутри него (есть некоторые другие вещи на экране, поэтому я не могу использовать UITableViewController), и я хочу, чтобы мой tableHeaderView изменял размер с помощью autolayout. Излишне говорить, что он не сотрудничает.
см. скриншот ниже.
потому что overviewLabel (например, "Список обзорная информация здесь."текст) имеет динамический контент, я использую autolayout, чтобы изменить его размер, и это superview. У меня все изменяется красиво, за исключением tableHeaderView, который находится прямо под представлением таблицы Paralax в hiearchy.
единственный способ, которым я нашел, чтобы изменить размер этого представления заголовка является программно, со следующим кодом:
CGRect headerFrame = self.headerView.frame;
headerFrame.size.height = headerFrameHeight;
self.headerView.frame = headerFrame;
[self.listTableView setTableHeaderView:self.headerView];
в этом случае headerFrameHeight представляет собой ручной Расчет высоты tableViewHeader следующим образом (innerHeaderView-это белая область, или второй "вид", 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);
ручной расчет работает, но он неуклюж и склонен к ошибке, если что-то изменится в будущем. То, что я хочу быть в состоянии сделать, это иметь автоматическое изменение размера tableHeaderView на основе предоставленных ограничений, как вы можете в любом другом месте. Но, хоть убей, я не могу этого понять.
есть несколько сообщений об этом, но ни один из них не ясен и не закончился еще больше сбивает меня с толку. Вот несколько:
авто макет на UITableViewHeader
авто макет для tableHeaderView
можно ли использовать AutoLayout с UITableView tableHeaderView?
высота представления заголовка таблицы неверна при использовании автоматической компоновки, IB и размеров шрифта
Это действительно не имеет смысла менять translatesAutoresizingMaskIntoConstraints свойство нет, так как это просто вызывает ошибки для меня и не имеет смысла концептуально в любом случае.
любая помощь будет действительно оценили!
EDIT 1: Благодаря предложению Томсвифта, я смог понять это. Вместо того, чтобы вручную вычислять высоту обзора, я могу рассчитать его для меня следующим образом, а затем повторно установить 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];
изменить 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;
}
8 ответов:
вы должны использовать
UIView systemLayoutSizeFittingSize:
метод для получения минимального размера ограничения вашего представления заголовка.Я предлагаю дальнейшее обсуждение использования этого API в этом Q / A:
как изменить размер супервизора, чтобы он соответствовал всем подвидам с автозапуском?
Я действительно сражался с этим, и установка установки в 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 } }
Я нашел элегантный способ использовать автоматический макет для изменения размера заголовков таблиц, с анимацией и без нее.
просто добавьте это в ваш контроллер представления.
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
ваше решение с помощью systemLayoutSizeFittingSize: работает, если представление заголовка обновляется только один раз при каждом появлении представления. В моем случае представление заголовка обновляется несколько раз, чтобы отразить изменения состояния. Но systemLayoutSizeFittingSize: всегда сообщал один и тот же размер. То есть размер, соответствующий первому обновлению.
чтобы получить systemLayoutSizeFittingSize: чтобы вернуть правильный размер после каждого обновления, мне пришлось сначала удалить представление заголовка таблицы перед его обновлением и повторное добавление:
self.listTableView.tableHeaderView = nil; [self.headerView removeFromSuperview];
это работало для меня на 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 }
это решение изменяет размер 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
в моем случае
viewDidLayoutSubviews
лучше работал.viewWillLayoutSubviews
вызывает белые линии atableView
появится. Также я добавил проверку, если мой
вполне можно использовать общий AutoLayout-based
UIView
С любой внутренней структурой подвида AL какtableHeaderView
.единственное, что нужно, это установить простой
tableFooterView
раньше!пусть
self.headerView
это некоторые ограничения на основеUIView
.- (void)viewDidLoad { ........................ self.tableView.tableFooterView = [UIView new]; [self.headerView layoutIfNeeded]; // to set initial size self.tableView.tableHeaderView = self.headerView; [self.tableView.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor].active = YES; [self.tableView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES; [self.tableView.topAnchor constraintEqualToAnchor:self.headerView.topAnchor].active = YES; // and the key constraint [self.tableFooterView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES; }
если
self.headerView
изменяет высоту при вращении пользовательского интерфейса нужно реализовать- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; [coordinator animateAlongsideTransition: ^(id<UIViewControllerTransitionCoordinatorContext> context) { // needed to resize header height self.tableView.tableHeaderView = self.headerView; } completion: NULL]; }
можно использовать ObjC категория для этой цели
@interface UITableView (AMHeaderView) - (void)am_insertHeaderView:(UIView *)headerView; @end @implementation UITableView (AMHeaderView) - (void)am_insertHeaderView:(UIView *)headerView { NSAssert(self.tableFooterView, @"Need to define tableFooterView first!"); [headerView layoutIfNeeded]; self.tableHeaderView = headerView; [self.leadingAnchor constraintEqualToAnchor:headerView.leadingAnchor].active = YES; [self.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor].active = YES; [self.topAnchor constraintEqualToAnchor:headerView.topAnchor].active = YES; [self.tableFooterView.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor].active = YES; } @end
, а также Свифт расширение
extension UITableView { func am_insertHeaderView2(_ headerView: UIView) { assert(tableFooterView != nil, "Need to define tableFooterView first!") headerView.layoutIfNeeded() tableHeaderView = headerView leadingAnchor.constraint(equalTo: headerView.leadingAnchor).isActive = true trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true tableFooterView?.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true } }