Odkąd odkryłem, AutoLayout
że używam go wszędzie, teraz próbuję go używać z rozszerzeniem tableHeaderView
.
Zrobiłem subclass
z UIView
dodatkiem wszystkiego (etykiety itp ...) Chciałem z ich ograniczeniami, a następnie dodałem to CustomView
do UITableView
" tableHeaderView
.
Wszystko działa dobrze z wyjątkiem UITableView
zawsze wyświetla powyższychCustomView
przez wyżej mam na myśli CustomView
to podUITableView
więc nie widać!
Wydaje się, że bez względu na to, co robię, height
z UITableView
' tableHeaderView
jest zawsze 0 (tak samo jak szerokość, x i y).
Moje pytanie: czy w ogóle da się to zrobić bez ręcznego ustawiania ramy ?
EDIT:CustomView
' subview
że używam ma następujące ograniczenia:
_title = [[UILabel alloc]init];
_title.text = @"Title";
[self addSubview:_title];
[_title keep:[KeepTopInset rules:@[[KeepEqual must:5]]]]; // title has to stay at least 5 away from the supperview Top
[_title keep:[KeepRightInset rules:@[[KeepMin must:5]]]];
[_title keep:[KeepLeftInset rules:@[[KeepMin must:5]]]];
[_title keep:[KeepBottomInset rules:@[[KeepMin must:5]]]];
Używam przydatnej biblioteki „KeepLayout”, ponieważ ręczne pisanie ograniczeń zajmuje wieczność i zbyt wiele wierszy dla jednego ograniczenia, ale metody nie wymagają wyjaśnień.
I UITableView
ma te ograniczenia:
_tableView = [[UITableView alloc]init];
_tableView.translatesAutoresizingMaskIntoConstraints = NO;
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor clearColor];
[self.view addSubview:_tableView];
[_tableView keep:[KeepTopInset rules:@[[KeepEqual must:0]]]];// These 4 constraints make the UITableView stays 0 away from the superview top left right and bottom.
[_tableView keep:[KeepLeftInset rules:@[[KeepEqual must:0]]]];
[_tableView keep:[KeepRightInset rules:@[[KeepEqual must:0]]]];
[_tableView keep:[KeepBottomInset rules:@[[KeepEqual must:0]]]];
_detailsView = [[CustomView alloc]init];
_tableView.tableHeaderView = _detailsView;
Nie wiem, czy muszę ustawiać pewne ograniczenia bezpośrednio na CustomView
, myślę, że wysokość CustomView jest określana przez ograniczenia zawarte w UILabel
„tytule”.
EDYCJA 2: Po kolejnym dochodzeniu wydaje się, że wysokość i szerokość CustomView są poprawnie obliczone, ale górna część CustomView jest nadal na tym samym poziomie co górna część UITableView i poruszają się razem, gdy przewijam.
źródło
Odpowiedzi:
Tutaj zadałem i odpowiedziałem na podobne pytanie . Podsumowując, raz dodaję nagłówek i używam go do znalezienia wymaganej wysokości. Wysokość tę można następnie zastosować do nagłówka, a nagłówek jest ustawiany po raz drugi, aby odzwierciedlić zmianę.
- (void)viewDidLoad { [super viewDidLoad]; self.header = [[SCAMessageView alloc] init]; self.header.titleLabel.text = @"Warning"; self.header.subtitleLabel.text = @"This is a message with enough text to span multiple lines. This text is set at runtime and might be short or long."; //set the tableHeaderView so that the required height can be determined self.tableView.tableHeaderView = self.header; [self.header setNeedsLayout]; [self.header layoutIfNeeded]; CGFloat height = [self.header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; //update the header's frame and set it again CGRect headerFrame = self.header.frame; headerFrame.size.height = height; self.header.frame = headerFrame; self.tableView.tableHeaderView = self.header; }
Jeśli masz etykiety wielowierszowe, zależy to również od ustawienia widoku niestandardowego preferowanejMaxLayoutWidth każdej etykiety:
- (void)layoutSubviews { [super layoutSubviews]; self.titleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.titleLabel.frame); self.subtitleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.subtitleLabel.frame); }
a może bardziej ogólnie:
override func layoutSubviews() { super.layoutSubviews() for view in subviews { guard let label = view as? UILabel where label.numberOfLines == 0 else { continue } label.preferredMaxLayoutWidth = CGRectGetWidth(label.frame) } }
Aktualizacja styczeń 2015
Niestety nadal wydaje się to konieczne. Oto szybka wersja procesu tworzenia układu:
tableView.tableHeaderView = header header.setNeedsLayout() header.layoutIfNeeded() header.frame.size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) tableView.tableHeaderView = header
Uważam, że przydatne jest przeniesienie tego do rozszerzenia w UITableView:
extension UITableView { //set the tableHeaderView so that the required height can be determined, update the header's frame and set it again func setAndLayoutTableHeaderView(header: UIView) { self.tableHeaderView = header header.setNeedsLayout() header.layoutIfNeeded() header.frame.size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) self.tableHeaderView = header } }
Stosowanie:
let header = SCAMessageView() header.titleLabel.text = "Warning" header.subtitleLabel.text = "Warning message here." tableView.setAndLayoutTableHeaderView(header)
źródło
preferredMaxLayoutWidth
jest dodanie ograniczenia szerokości (równego szerokości widoku tabeli) w widoku nagłówka przed użyciemsystemLayoutSizeFittingSize:
.self.tableView.tableHeaderView
let height = header.systemLayoutSizeFittingSize(CGSizeMake(CGRectGetWidth(self.bounds), 0), withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityFittingSizeLevel).height
header.setNeedsLayout() header.layoutIfNeeded() header.frame.size = header.systemLayoutSizeFitting(UILayoutFittingCompressedSize) self.tableHeaderView = header
działałoby na iOS 10.2Nie udało mi się dodać widoku nagłówka za pomocą ograniczeń (w kodzie). Jeśli podam dla mojego widoku ograniczenie szerokości i / lub wysokości, wyskakuje mi komunikat o treści:
"terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super."
Kiedy dodaję widok w scenorysie do widoku tabeli, nie pokazuje on żadnych ograniczeń i działa dobrze jako widok nagłówka, więc myślę, że umieszczenie widoku nagłówka nie jest wykonywane przy użyciu ograniczeń. W tym względzie nie wygląda jak normalny widok.
Szerokość jest automatycznie szerokością widoku tabeli, jedyne, co musisz ustawić, to wysokość - wartości początkowe są ignorowane, więc nie ma znaczenia, co wstawisz dla nich. Na przykład to zadziałało dobrze (podobnie jak 0,0,0,80 dla prostownika):
UIView *headerview = [[UIView alloc] initWithFrame:CGRectMake(1000,1000, 0, 80)]; headerview.backgroundColor = [UIColor yellowColor]; self.tableView.tableHeaderView = headerview;
źródło
translatesAutoresizingMaskIntoConstraints = NO
. Włączenie tłumaczenia zapobiega błędowi - podejrzewam,UITableView
że od wersji 7.1 nie próbuje automatycznie układać widoku nagłówka i chce czegoś z ustawioną ramką.Widziałem tutaj wiele metod, które robią tyle niepotrzebnych rzeczy, ale nie potrzebujesz aż tak dużo, aby użyć automatycznego układu w widoku nagłówka. Musisz tylko stworzyć swój plik xib, umieścić swoje ograniczenia i zainicjować go w ten sposób:
func loadHeaderView () { guard let headerView = Bundle.main.loadNibNamed("CourseSearchHeader", owner: self, options: nil)?[0] as? UIView else { return } headerView.autoresizingMask = .flexibleWidth headerView.translatesAutoresizingMaskIntoConstraints = true tableView.tableHeaderView = headerView }
źródło
Innym rozwiązaniem jest wysłanie tworzenia widoku nagłówka do następnego wywołania głównego wątku:
- (void)viewDidLoad { [super viewDidLoad]; // .... dispatch_async(dispatch_get_main_queue(), ^{ _profileView = [[MyView alloc] initWithNib:@"MyView.xib"]; self.tableView.tableHeaderView = self.profileView; }); }
Uwaga: naprawia błąd, gdy załadowany widok ma stałą wysokość. Nie próbowałem, gdy wysokość nagłówka zależy tylko od jego zawartości.
EDYTOWAĆ :
Możesz znaleźć czystsze rozwiązanie tego problemu, implementując tę funkcję i wywołując ją
viewDidLayoutSubviews
- (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; [self sizeHeaderToFit]; }
źródło
tableHeaderView
są trochę buggy z automatycznym układem. Istnieje kilka obejść, takich jak ten. Ale ponieważ to pisałem, znalazłem lepsze i czystsze rozwiązanie tutaj stackoverflow.com/a/21099430/127493 przez wywołanie- (void)sizeHeaderToFit
inviewDidLayoutSubviews
Kod:
extension UITableView { func sizeHeaderToFit(preferredWidth: CGFloat) { guard let headerView = self.tableHeaderView else { return } headerView.translatesAutoresizingMaskIntoConstraints = false let layout = NSLayoutConstraint( item: headerView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: preferredWidth) headerView.addConstraint(layout) let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height headerView.frame = CGRectMake(0, 0, preferredWidth, height) headerView.removeConstraint(layout) headerView.translatesAutoresizingMaskIntoConstraints = true self.tableHeaderView = headerView } }
źródło
Rozszerzono to rozwiązanie http://collindonnell.com/2015/09/29/dynamically-sized-table-view-header-or-footer-using-auto-layout/ dla widoku stopki tabeli:
@interface AutolayoutTableView : UITableView @end @implementation AutolayoutTableView - (void)layoutSubviews { [super layoutSubviews]; // Dynamic sizing for the header view if (self.tableHeaderView) { CGFloat height = [self.tableHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; CGRect headerFrame = self.tableHeaderView.frame; // If we don't have this check, viewDidLayoutSubviews() will get // repeatedly, causing the app to hang. if (height != headerFrame.size.height) { headerFrame.size.height = height; self.tableHeaderView.frame = headerFrame; self.tableHeaderView = self.tableHeaderView; } [self.tableHeaderView layoutIfNeeded]; } // Dynamic sizing for the footer view if (self.tableFooterView) { CGFloat height = [self.tableFooterView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; CGRect footerFrame = self.tableFooterView.frame; // If we don't have this check, viewDidLayoutSubviews() will get // repeatedly, causing the app to hang. if (height != footerFrame.size.height) { footerFrame.size.height = height; self.tableFooterView.frame = footerFrame; self.tableFooterView = self.tableFooterView; } self.tableFooterView.transform = CGAffineTransformMakeTranslation(0, self.contentSize.height - footerFrame.size.height); [self.tableFooterView layoutIfNeeded]; } } @end
źródło
self.tableFooterView.transform
część? Dlaczego jest to konieczne?Możesz uzyskać autolayout, aby podać rozmiar, używając metody systemLayoutSizeFittingSize .
Możesz następnie użyć tego do stworzenia ramki dla swojej aplikacji. Ta technika działa zawsze, gdy potrzebujesz znać rozmiar widoku, który wewnętrznie używa autoukładu.
Wygląda jak kod w języku swift
//Create the view let tableHeaderView = CustomTableHeaderView() //Set the content tableHeaderView.textLabel.text = @"Hello world" //Ask auto layout for the smallest size that fits my constraints let size = tableHeaderView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize) //Create a frame tableHeaderView.frame = CGRect(origin: CGPoint.zeroPoint, size: size) //Set the view as the header self.tableView.tableHeaderView = self.tableHeaderView
Lub w Objective-C
//Create the view CustomTableHeaderView *header = [[CustomTableHeaderView alloc] initWithFrame:CGRectZero]; //Set the content header.textLabel.text = @"Hello world"; //Ask auto layout for the smallest size that fits my constraints CGSize size = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; //Create a frame header.frame = CGRectMake(0,0,size.width,size.height); //Set the view as the header self.tableView.tableHeaderView = header
Należy również zauważyć, że w tym konkretnym przypadku przesłanianie wymagaConstraintBasedLayout w podklasie, powoduje wykonanie przebiegu układu, jednak wyniki tego przebiegu układu są ignorowane, a ramka systemowa jest ustawiona na szerokość tableView i 0 wysokość.
źródło
Poniższe zadziałały dla mnie.
UIView
jako widoku nagłówka.UIView
Główną korzyścią, jaką widzę, jest ograniczenie obliczeń ramek. Apple powinno naprawdę zaktualizować
UITableView
API, aby było to łatwiejsze.Przykład użycia SnapKit:
let layoutView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: 60)) layoutView.backgroundColor = tableView.backgroundColor tableView.tableHeaderView = layoutView let label = UILabel() layoutView.addSubview(label) label.text = "I'm the view you really care about" label.snp_makeConstraints { make in make.edges.equalTo(EdgeInsets(top: 10, left: 15, bottom: -5, right: -15)) }
źródło
Dziwne rzeczy się zdarzają. systemLayoutSizeFittingSize działa świetnie na iOS9, ale w moim przypadku nie na iOS 8. Więc ten problem rozwiązuje się dość łatwo. Po prostu uzyskaj link do dolnego widoku w nagłówku i w viewDidLayoutSubviews po super wywołanie granic widoku nagłówka aktualizacji, wstawiając wysokość jako CGRectGetMaxY (yourview.frame) + dopełnienie
UPD: Najłatwiejsze rozwiązanie wszechczasów : w widoku nagłówka umieść widok podrzędny i przypnij go do lewej , prawej , góry . W tym widoku podrzędnym umieść podglądy z automatycznymi ograniczeniami wysokości. Następnie przekaż całą pracę do autoukładu (bez konieczności obliczania)
- (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; CGFloat height = CGRectGetMaxY(self.tableView.tableHeaderView.subviews.firstObject.frame); self.tableView.tableHeaderView.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), height); self.tableView.tableHeaderView = self.tableView.tableHeaderView; }
W rezultacie podwidok rozszerza się / kurczy tak, jak powinien, na końcu wywołuje viewDidLayoutSubviews. W tym momencie znamy rzeczywisty rozmiar widoku, więc ustaw wysokość headerView i zaktualizuj ją przez ponowne przypisanie. Działa jak marzenie!
Działa również w widoku stopki.
źródło
Zaktualizowano dla Swift 4.2
extension UITableView { var autolayoutTableViewHeader: UIView? { set { self.tableHeaderView = newValue guard let header = newValue else { return } header.setNeedsLayout() header.layoutIfNeeded() header.frame.size = header.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) self.tableHeaderView = header } get { return self.tableHeaderView } } }
źródło
możesz dodać górne + poziome ograniczenie lokalizacji między nagłówkiem a widokiem tabeli, aby umieścić je poprawnie (jeśli sam nagłówek zawiera wszystkie niezbędne wewnętrzne ograniczenia układu, aby mieć poprawną ramkę)
w metodzie tableViewController viewDidLoad
headerView.translatesAutoresizingMaskIntoConstraints = false tableView.tableHeaderView = headerView headerView.widthAnchor.constraint(equalTo: tableView.widthAnchor).isActive = true headerView.topAnchor.constraint(equalTo: tableView.topAnchor).isActive = true headerView.centerXAnchor.constraint(equalTo: tableView.centerXAnchor).isActive = true
źródło
Mój widok nagłówka tabeli jest podklasą UIView - utworzyłem pojedynczy contentView UIView w inicjatorze, z jego granicami takimi samymi jak ramka widoku nagłówka tabeli i dodałem wszystkie moje obiekty jako podwidok tego.
Następnie dodaj ograniczenia dla obiektów w
layoutSubviews
metodzie widoku nagłówka tabeli, a nie w inicjatorze. To rozwiązało awarię.- (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:CGRectMake(0, 0, 0, 44.0)]; if (self) { UIView *contentView = [[UIView alloc] initWithFrame:self.bounds]; contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth; // add other objects as subviews of content view } return self; } - (void)layoutSubviews { [super layoutSubviews]; // remake constraints here }
źródło
Mój układ AutoLayout działa bardzo dobrze:
CGSize headerSize = [headerView systemLayoutSizeFittingSize:CGSizeMake(CGRectGetWidth([UIScreen mainScreen].bounds), 0) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel]; headerView.frame = CGRectMake(0, 0, headerSize.width, headerSize.height); self.tableView.tableHeaderView = headerView;
źródło
W większości przypadków najlepszym rozwiązaniem jest po prostu nie walka z frameworkiem i korzystanie z autorezowania masek:
// embrace autoresizing masks and let the framework add the constraints for you headerView.translatesAutoresizingMaskIntoConstraints = true headerView.autoresizingMask = [.flexibleWidth, .flexibleHeight] // figure out what's the best size based on the table view width let width = self.tableView.frame.width let targetSize = headerView.systemLayoutSizeFitting(CGSize(width: width, height: CGFloat.greatestFiniteMagnitude), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) headerView.frame.size = targetSize self.tableView.tableHeaderView = headerView
Używając masek autorezacji, mówisz frameworkowi, jak twój widok powinien zmienić swój rozmiar, gdy nadzór zmieni swój rozmiar. Ale ta zmiana jest oparta na ustawionej początkowej klatce.
źródło
Wiem, że to stary post, ale po przejrzeniu wszystkich postów SO dotyczących tego i spędzeniu całego popołudnia bawiąc się tym, w końcu wpadłem na czyste, a jednocześnie bardzo proste rozwiązanie
Przede wszystkim moja hierarchia widoków wygląda następująco:
tableHeaderView
Teraz w widoku (nr 3) ustawiłem wszystkie ograniczenia, tak jak zwykle, włączając dolną przestrzeń do kontenera. Spowoduje to, że kontener (tj. 3.View, tj. HeaderView) zmieni rozmiar na siebie w oparciu o jego podziały i ich ograniczenia.
Następnie ustawiłem ograniczenia między
3. View
i2. View
do tych:Zauważ, że celowo pomijam dolną przestrzeń.
Gdy wszystko to zostanie zrobione w scenorysie, wszystko, co pozostaje do zrobienia, to wkleić te trzy linie kodów:
if (self.headerView.frame.size.height != self.tableView.tableHeaderView.frame.size.height) { UIView *header = self.tableView.tableHeaderView; CGRect frame = self.tableView.tableHeaderView.frame; frame.size.height = self.headerView.frame.size.height + frame.origin.y; header.frame = frame; self.tableView.tableHeaderView = header; }
źródło
Porady: Jeśli używasz metody setAndLayoutTableHeaderView, powinieneś zaktualizować ramkę subviews, więc w tej sytuacji preferowanyMaxLayoutWidth UILabel powinien wywołać przed wywołaniem systemLayoutSizeFittingSize, nie wywołuj w layoutSubview.
pokaż kod
źródło
Podziel się moim podejściem.
UITableView+XXXAdditions.m
- (void)xxx_setTableHeaderView:(UIView *)tableHeaderView layoutBlock:(void(^)(__kindof UIView *tableHeaderView, CGFloat *containerViewHeight))layoutBlock { CGFloat containerViewHeight = 0; UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectZero]; [backgroundView addSubview:tableHeaderView]; layoutBlock(tableHeaderView, &containerViewHeight); backgroundView.frame = CGRectMake(0, 0, 0, containerViewHeight); self.tableHeaderView = backgroundView; }
Stosowanie.
[self.tableView xxx_setTableHeaderView:myView layoutBlock:^(__kindof UIView * _Nonnull tableHeaderView, CGFloat *containerViewHeight) { *containerViewHeight = 170; [tableHeaderView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(@20); make.centerX.equalTo(@0); make.size.mas_equalTo(CGSizeMake(130, 130)); }]; }];
źródło
W moim przypadku metoda z systemLayoutSizeFittingSize z jakiegoś powodu nie działała. U mnie zadziałała modyfikacja rozwiązania opublikowana przez HotJarda (jego oryginalne rozwiązanie również nie działało w moim przypadku na iOS 8). Musiałem w widoku nagłówka umieścić widok podrzędny i przypiąć go do lewej, prawej, góry (nie przypinaj do dołu). Umieść wszystko za pomocą autolayout w tym podglądzie i w kodzie zrób to:
- (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; [self resizeHeaderToFitSubview]; } - (void)resizeHeaderToFitSubview { UIView *header = self.tableView.tableHeaderView; [header setNeedsLayout]; [header layoutIfNeeded]; CGFloat height = CGRectGetHeight(header.subviews.firstObject.bounds); header.bounds = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), height); self.tableView.tableHeaderView = nil; self.tableView.tableHeaderView = header; }
źródło
Stary post. Ale dobry post. Oto moje 2 centy.
Po pierwsze, upewnij się, że widok nagłówka ma swoje ograniczenia tak ułożone, aby mógł obsługiwać własny wewnętrzny rozmiar zawartości. Następnie wykonaj następujące czynności.
//ViewDidLoad headerView.translatesAutoresizingMaskIntoConstraints = false headerView.configure(title: "Some Text A") //Somewhere else headerView.update(title: "Some Text B) private var widthConstrained = false override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if widthConstrained == false { widthConstrained = true tableView.addConstraint(NSLayoutConstraint(item: headerView, attribute: .width, relatedBy: .equal, toItem: tableView, attribute: .width, multiplier: 1, constant: 0)) headerView.layoutIfNeeded() tableView.layoutIfNeeded() } } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) coordinator.animate(alongsideTransition: { (context) in self.headerView.layoutIfNeeded() self.tableView.layoutIfNeeded() }, completion: nil) }
źródło
Udało mi się to osiągnąć w następujący sposób (działa to w przypadku stopki w ten sam sposób).
Najpierw będziesz potrzebować małego
UITableView
rozszerzenia:Szybki 3
extension UITableView { fileprivate func adjustHeaderHeight() { if let header = self.tableHeaderView { adjustFrame(header) } } private func adjustFrame(_ view: UIView) { view.frame.size.height = calculatedViewHeight(view) } fileprivate func calculatedHeightForHeader() -> CGFloat { if let header = self.tableHeaderView { return calculatedViewHeight(header) } return 0.0 } private func calculatedViewHeight(_ view: UIView) -> CGFloat { view.setNeedsLayout() let height = view.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height return height } }
W Twoim widoku implementacja klasy kontrolera:
// this is a UIView subclass with autolayout private var headerView = MyHeaderView() override func loadView() { super.loadView() // ... self.tableView.tableHeaderView = headerView self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension // ... } override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() // this is to prevent recursive layout calls let requiredHeaderHeight = self.tableView.calculatedHeightForHeader() if self.headerView.frame.height != requiredHeaderHeight { self.tableView.adjustHeaderHeight() } }
Uwagi dotyczące nagłówka
UIView
implementacji widoku podrzędnego :Musisz mieć 100% pewności, że Twój widok nagłówka ma poprawną konfigurację automatycznego układu. Poleciłbym zacząć od prostego widoku nagłówka z jednym ograniczeniem wysokości i wypróbować powyższą konfigurację.
Zastąp
requiresConstraintBasedLayout
i zwróćtrue
:.
class MyHeaderView: UIView { // ... override static var requiresConstraintBasedLayout : Bool { return true } // ... }
źródło
Dla użytkowników platformy Xamarin:
public override void ViewDidLayoutSubviews() { base.ViewDidLayoutSubviews(); TableviewHeader.SetNeedsLayout(); TableviewHeader.LayoutIfNeeded(); var height = TableviewHeader.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize).Height; var frame = TableviewHeader.Frame; frame.Height = height; TableviewHeader.Frame = frame; }
Zakładając, że nazwałeś widok nagłówka widoku tabeli jako TableviewHeader
źródło
Oto, jak możesz to zrobić w swoim
UIViewController
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if headerView.frame.size.height == 0 { headerView.label.preferredMaxLayoutWidth = view.bounds.size.width - 20 let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height headerView.frame.size = CGSize(width: tableView.bounds.size.width, height: height) } }
źródło
Każde oparte na ograniczeniach
UIView
może być dobretableHeaderView
.Należy ustawić
tableFooterView
przed, a następnie nałożyć dodatkowe ograniczenie końcowe natableFooterView
itableHeaderView
.- (void)viewDidLoad { ........................ // let self.headerView is some constraint-based UIView self.tableView.tableFooterView = [UIView new]; [self.headerView layoutIfNeeded]; 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; [self.tableFooterView.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor].active = YES;
}
Wszystkie szczegóły i fragmenty kodu można znaleźć tutaj
źródło
Znalazłem obejście. Zawiń widok nagłówka autolayout wrriten xib w pustym opakowaniu uiview i przypisz widok nagłówka do właściwości tableViewHeader tableView.
UIView *headerWrapper = [[UIView alloc] init]; AXLHomeDriverHeaderView *headerView = [AXLHomeDriverHeaderView loadViewFromNib]; [headerWrapper addSubview:headerView]; [headerView mas_makeConstraints:^(MASConstraintMaker *make) { make.edges.equalTo(headerWrapper); }]; self.tableView.tableHeaderView = headerView;
źródło
Oto, co działa dla UITableViewController w iOS 12,
Przeciągnij UIView do TableView powyżej wszystkich prototypowych komórek dla nagłówka i poniżej wszystkich prototypowych komórek dla stopki. W razie potrzeby skonfiguruj nagłówek i stopkę. Ustaw wszystkie wymagane ograniczenia.
Teraz użyj następujących metod rozszerzających
public static class UITableVIewExtensions { public static void MakeHeaderAutoDimension(this UITableView tableView) { if (tableView.TableHeaderView is UIView headerView) { var size = headerView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize); if (headerView.Frame.Size.Height != size.Height) { var frame = headerView.Frame; frame.Height = size.Height; headerView.Frame = frame; tableView.TableHeaderView = headerView; tableView.LayoutIfNeeded(); } } } public static void MakeFooterAutoDimension(this UITableView tableView) { if (tableView.TableFooterView is UIView footerView) { var size = footerView.SystemLayoutSizeFittingSize(UIView.UILayoutFittingCompressedSize); if (footerView.Frame.Size.Height != size.Height) { var frame = footerView.Frame; frame.Height = size.Height; footerView.Frame = frame; tableView.TableFooterView = footerView; tableView.LayoutIfNeeded(); } } } }
i wywołaj go w ViewDidLayoutSubviews podklasy UITableViewController
public override void ViewDidLayoutSubviews() { base.ViewDidLayoutSubviews(); TableView.MakeHeaderAutoDimension(); TableView.MakeFooterAutoDimension(); }
źródło
Napotkałem problem z uzyskaniem szerokości 375pt, jedynym sposobem, który zadziałał dla mnie, jest przekazanie tableView w celu uzyskania prawidłowej szerokości. Wolałem też AutoLayout zamiast ustawiania rozmiaru ramki.
Oto wersja, która działa dla mnie:
Xamarin.iOS
public static void AutoLayoutTableHeaderView(this UITableView tableView, UIView header) { tableView.TableHeaderView = header; tableView.SetNeedsLayout(); tableView.LayoutIfNeeded(); header.WidthAnchor.ConstraintEqualTo(tableView.Bounds.Width).Active = true; tableView.TableHeaderView = header; }
Wersja Swift (zmodyfikowana na podstawie odpowiedzi @Ben Packard)
extension UITableView { //set the tableHeaderView so that the required height can be determined, update the header's frame and set it again func setAndLayoutTableHeaderView(header: UIView) { self.tableHeaderView = header self.setNeedsLayout() self.layoutIfNeeded() header.widthAnchor.widthAnchor.constraint(equalTo: self.bounds.width).isActive = true self.tableHeaderView = header } }
źródło
Stworzyłem podklasę
UITableView
i użyłemUIStackView
zarówno nagłówka, jak i stopki, umożliwia również ustawienie więcej niż jednego widoku.https://github.com/omaralbeik/StackableTableView
źródło
Moim rozwiązaniem jest stworzenie takiej nowej klasy.
class BaseTableHeaderView: UIView { func sizeToFitBasedOnConstraints(width: CGFloat = Screen.width) { let size = systemLayoutSizeFitting(CGSize(width: width, height: 10000), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) frame = CGRect(origin: .zero, size: size) } override func willMove(toSuperview newSuperview: UIView?) { sizeToFitBasedOnConstraints() super.willMove(toSuperview: newSuperview) } }
Aby go użyć, po prostu dodaj wszystkie podglądy do instancji programu
BaseTableHeaderView
i dołącz je do widoku tabeli.let tableHeaderView = BaseTableHeaderView() tableHeaderView.addSubview(...) tableView.tableHeaderView = tableHeaderView
Automatycznie zmieni rozmiar na podstawie swoich ograniczeń.
źródło
Zaakceptowana odpowiedź jest przydatna tylko w przypadku tabel z jedną sekcją. W przypadku wielu sekcji
UITableView
po prostu upewnij się, że nagłówek dziedziczy poUITableViewHeaderFooterView
a wszystko będzie dobrze.Alternatywnie, po prostu umieść bieżący nagłówek w pliku
contentView
aUITableViewHeaderFooterView
. Dokładnie tak, jakUITableViewCell
działa.źródło
tableHeaderView
nie dotyczy nagłówka sekcji.