Я бился головой об стену этим последние 3 или 4 часа и, кажется, не могу этого понять. У меня есть UIViewController с полноэкранным UITableView внутри него (на экране есть кое-что другое, поэтому я не могу использовать UITableViewController), и я хочу, чтобы мой tableHeaderView изменял размер с помощью автозапуска. Излишне говорить, что это не сотрудничество.
Смотрите скриншот ниже.
Поскольку overviewLabel (например, текст «Список обзорной информации здесь.») Имеет динамическое содержимое, я использую автоматическое размещение, чтобы изменить его размер и его супервизор. У меня все красиво меняется, за исключением tableHeaderView, который находится прямо под представлением таблицы Paralax в иерархии.
Я нашел единственный способ изменить размер этого заголовка - программно, с помощью следующего кода:
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 на основе предоставленных ограничений, как и в любом другом месте. Но хоть убей, я не могу этого понять.
На SO есть несколько сообщений об этом, но ни одна из них не ясна и в конечном итоге меня больше запутала. Вот несколько:
На самом деле не имеет смысла изменять свойство translatesAutoresizingMaskIntoConstraints на NO, поскольку это просто вызывает у меня ошибки и в любом случае концептуально не имеет смысла.
Любая помощь будет очень признательна!
РЕДАКТИРОВАТЬ 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];
Изменить 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;
}
источник
setTableHeaderView
не работает на Xcode6. Проблема в том, что ячейки перекрываются tableHeaderView. Однако он работает на Xcode5Ответы:
Вам нужно использовать этот
UIView systemLayoutSizeFittingSize:
метод, чтобы получить минимальный ограничивающий размер вашего представления заголовка.Я предлагаю дальнейшее обсуждение использования этого API в этом вопросе / ответе:
Как изменить размер супервизора, чтобы он соответствовал всем подпредставлениям с автоматическим раскладкой?
источник
Я действительно боролся с этим, и установка настройки в 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 } }
источник
Я нашел элегантный способ использования автоматического макета для изменения размеров заголовков таблиц с анимацией и без нее.
Просто добавьте это в свой View Controller.
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
источник
@IBOutlet makeThisTaller
и@IBAction fun makeThisTaller
похожий на этот пример. Кроме того, ограничьте все стороны вашей метки tableViewHeader (например, сверху, снизу, слева и справа).lblFeedDescription.preferredMaxLayoutWidth = lblFeedDescription.bounds.width
где label - это тот, размер которого я хочу увеличить. Благодарность !Это решение изменяет размер 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
источник
Ваше решение, использующее 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 }
источник
Он работает как для верхнего, так и для нижнего колонтитула, просто замените верхний колонтитул на нижний.
func sizeHeaderToFit() { if let headerView = tableView.tableHeaderView { headerView.setNeedsLayout() headerView.layoutIfNeeded() let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height var frame = headerView.frame frame.size.height = height headerView.frame = frame tableView.tableHeaderView = headerView } }
источник
Для iOS 12 и более поздних версий следующие шаги обеспечат правильную работу автоматического раскладки как в вашей таблице, так и в заголовке.
источник
В моем случае
viewDidLayoutSubviews
сработало лучше.viewWillLayoutSubviews
вызывает появление белых линий atableView
. Также я добавил проверку, существует ли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]; } }
источник
Вполне возможно использовать общий AutoLayout на основе
UIView
любой внутренней структуры subview 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
А также расширение Swift
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 } }
источник
Это решение отлично работает для меня:
https://spin.atomicobject.com/2017/08/11/swift-exnding-uitableviewcontroller/
Он расширяет UITableViewController. Но если вы просто используете UITableView, он все равно будет работать, просто расширьте UITableView вместо UITableViewController. Вызывайте методы
sizeHeaderToFit()
илиsizeFooterToFit()
всякий раз, когда происходит событие, изменяющееtableViewHeader
высоту.источник
Скопировано из этого сообщения
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if let headerView = tableView.tableHeaderView { let height = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).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 } } }
источник