Rozbijam się tym głową o ścianę przez ostatnie 3 lub 4 godziny i nie mogę tego rozgryźć. Mam UIViewController z pełnoekranowym UITableView wewnątrz niego (na ekranie jest kilka innych rzeczy, dlatego nie mogę użyć UITableViewController) i chcę, aby moja tableHeaderView zmieniła rozmiar za pomocą autolayout. Nie trzeba dodawać, że nie współpracuje.
Zobacz zrzut ekranu poniżej.
Ponieważ OverviewLabel (np. Tekst „List Overview information here”) ma dynamiczną zawartość, używam autolayout, aby zmienić jego rozmiar i jego superview. Mam wszystko ładnie zmieniające rozmiar, z wyjątkiem tableHeaderView, który znajduje się tuż poniżej widoku tabeli Paralax w hierarchii.
Jedynym sposobem, w jaki udało mi się zmienić rozmiar tego widoku nagłówka, jest programowe użycie następującego kodu:
CGRect headerFrame = self.headerView.frame;
headerFrame.size.height = headerFrameHeight;
self.headerView.frame = headerFrame;
[self.listTableView setTableHeaderView:self.headerView];
W tym przypadku headerFrameHeight jest ręcznym obliczeniem wysokości tableViewHeader w następujący sposób (innerHeaderView to biały obszar lub drugi „View”, headerView to 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);
Ręczne obliczanie działa, ale jest niezgrabne i podatne na błędy, jeśli coś się zmieni w przyszłości. Chcę mieć możliwość automatycznej zmiany rozmiaru tableHeaderView na podstawie podanych ograniczeń, tak jak w każdym innym miejscu. Ale na całe życie nie mogę tego rozgryźć.
Jest kilka postów na SO na ten temat, ale żaden nie jest jasny i ostatecznie bardziej mnie zdezorientował. Oto kilka:
Naprawdę nie ma sensu zmieniać właściwości translatesAutoresizingMaskIntoConstraints na NO, ponieważ powoduje to po prostu błędy i nie ma sensu koncepcyjnego.
Każda pomoc byłaby naprawdę doceniona!
EDYCJA 1: Dzięki sugestii TomSwift udało mi się to rozgryźć. Zamiast ręcznie obliczać wysokość przeglądu, mogę go obliczyć dla mnie w następujący sposób, a następnie ponownie ustawić tableHeaderView jak poprzednio.
[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];
Edycja 2: Jak zauważyli inni, rozwiązanie opublikowane w Edit 1 nie działa w viewDidLoad. Wydaje się jednak, że działa w viewWillLayoutSubviews. Przykładowy kod poniżej:
// 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;
}
źródło
setTableHeaderView
nie działa na Xcode6. Problem polega na tym, że na komórki nakłada się tableHeaderView. Jednak działa na Xcode5Odpowiedzi:
Musisz użyć tej
UIView systemLayoutSizeFittingSize:
metody, aby uzyskać minimalny rozmiar obwiedni widoku nagłówka.Dalszą dyskusję na temat korzystania z tego interfejsu API przedstawię w tym pytaniu / odpowiedzi:
Jak zmienić rozmiar Superview, aby dopasować wszystkie podglądy do autoukładu?
źródło
Naprawdę walczyłem z tym i podłączenie konfiguracji do widoku DidLoad nie działało dla mnie, ponieważ ramka nie jest ustawiona w viewDidLoad, skończyło się też na tonach niechlujnych ostrzeżeń, w których zamknięta wysokość automatycznego układu nagłówka została zmniejszona do 0 Zauważyłem problem tylko na iPadzie podczas prezentowania tableView w prezentacji formularza.
Rozwiązaniem dla mnie było ustawienie tableViewHeader w viewWillLayoutSubviews, a nie w 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 } }
źródło
Znalazłem elegancki sposób wykorzystania automatycznego układu do zmiany rozmiaru nagłówków tabeli, z animacją i bez niej.
Po prostu dodaj to do kontrolera widoku.
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() } }
Aby zmienić rozmiar zgodnie z dynamicznie zmieniającą się etykietą:
@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) }
Aby animować zmianę rozmiaru zgodnie ze zmianami w wiązaniu:
@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() } }
Zobacz projekt AutoResizingHeader, aby zobaczyć, jak to działa. https://github.com/p-sun/Swift2-iOS9-UI
źródło
@IBOutlet makeThisTaller
i@IBAction fun makeThisTaller
jak w przykładzie. Ponadto ogranicz wszystkie boki etykiety do elementu tableViewHeader (np. Góra, dół, lewa i prawa).lblFeedDescription.preferredMaxLayoutWidth = lblFeedDescription.bounds.width
gdzie etykieta to ta, którą chcę zwiększyć rozmiar. Dzięki !To rozwiązanie zmienia rozmiar tableHeaderView i pozwala uniknąć nieskończonej pętli w
viewDidLayoutSubviews()
metodzie, którą miałem z niektórymi innymi odpowiedziami tutaj: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 } } }
Zobacz także ten post: https://stackoverflow.com/a/34689293/1245231
źródło
Twoje rozwiązanie wykorzystujące systemLayoutSizeFittingSize: działa, jeśli widok nagłówka jest aktualizowany tylko raz przy każdym pojawieniu się widoku. W moim przypadku widok nagłówka był wielokrotnie aktualizowany, aby odzwierciedlić zmiany stanu. Ale systemLayoutSizeFittingSize: zawsze zgłaszał ten sam rozmiar. Oznacza to, że rozmiar odpowiadający pierwszej aktualizacji.
Aby uzyskać systemLayoutSizeFittingSize: aby zwrócić prawidłowy rozmiar po każdej aktualizacji, musiałem najpierw usunąć widok nagłówka tabeli przed aktualizacją i ponownym dodaniem:
self.listTableView.tableHeaderView = nil; [self.headerView removeFromSuperview];
źródło
To zadziałało dla mnie na ios10 i 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 }
źródło
Działa zarówno w widoku nagłówka, jak i stopce, po prostu zastąp nagłówek stopką
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 } }
źródło
W przypadku systemu iOS 12 i nowszych poniższe kroki zapewnią, że autoukład działa poprawnie zarówno w tabeli, jak iw nagłówku.
źródło
W moim przypadku
viewDidLayoutSubviews
zadziałało lepiej.viewWillLayoutSubviews
powoduje pojawienie się białych linii litery atableView
. Dodałem również sprawdzenie, czy mójheaderView
obiekt już istnieje.- (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]; } }
źródło
Całkiem możliwe jest użycie ogólnego układu opartego na autoukładzie
UIView
z dowolną wewnętrzną strukturą widoku podrzędnego AL jako plikutableHeaderView
.Jedyne, czego potrzebujesz, to ustawić
tableFooterView
wcześniej proste !Niech
self.headerView
jest oparta na ograniczeniachUIView
.- (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; }
W przypadku
self.headerView
zmiany wysokości w ramach rotacji UI należy zaimplementować- (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]; }
W tym celu można wykorzystać kategorię 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
A także szybkie rozszerzenie
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 } }
źródło
U mnie to rozwiązanie działa idealnie:
https://spin.atomicobject.com/2017/08/11/swift-extending-uitableviewcontroller/
Rozszerza UITableViewController. Ale jeśli używasz tylko UITableView, nadal będzie działać, po prostu rozszerz UITableView zamiast UITableViewController. Wywołaj metody
sizeHeaderToFit()
lubsizeFooterToFit()
za każdym razem, gdy nastąpi zdarzenie, które zmieniatableViewHeader
wysokość.źródło
Skopiowano z tego posta
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 } } }
źródło