Jak zmienić rozmiar tableHeaderView w UITableView?

103

Mam problem ze zmianą rozmiaru tableHeaderView. To proste nie działa.

1) Utwórz UITableView i UIView (100 x 320 pikseli);

2) Ustaw UIView jako tableHeaderView w UITableView;

3) Buduj i ruszaj. Wszystko w porządku.

Teraz chcę zmienić rozmiar tableHeaderView, więc dodaję ten kod w viewDidLoad:

self.tableView.autoresizesSubviews = YES;

self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

CGRect newFrame = self.tableView.tableHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
self.tableView.tableHeaderView.frame = newFrame;

Wysokość tableHeaderView powinna mieć wartość 200, ale pojawia się wartość 100.

Jeśli napiszę:

self.tableView.autoresizesSubviews = YES;


CGRect newFrame = myHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
myHeaderView.frame = newFrame;


self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

Następnie zaczyna się od 200 wysokości, tak jak chcę. Ale chcę mieć możliwość modyfikowania go w czasie wykonywania.

Próbowałem też tego bez powodzenia:

self.tableView.autoresizesSubviews = YES;

self.tableView.tableHeaderView = myHeaderView;
self.tableView.tableFooterView = myFooterView;

CGRect newFrame = self.tableView.tableHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
self.tableView.tableHeaderView.frame = newFrame;

[self.tableView.tableHeaderView setNeedsLayout];
[self.tableView.tableHeaderView setNeedsDisplay];
[self.tableView setNeedsLayout];
[self.tableView setNeedsDisplay];

Chodzi o to: Jak zmienić rozmiar tableHeaderView w czasie wykonywania ???

Czy ktoś może to zrobić?

Dzięki

ja ja

ja ja
źródło

Odpowiedzi:

180

FYI: Uruchomiłem to, modyfikując tableHeaderView i ponownie ustawiając go. W takim przypadku dostosowuję rozmiar tableHeaderView po zakończeniu ładowania widoku podrzędnego UIWebView.

[webView sizeToFit];
CGRect newFrame = headerView.frame;
newFrame.size.height = newFrame.size.height + webView.frame.size.height;
headerView.frame = newFrame;
[self.tableView setTableHeaderView:headerView];
kubi
źródło
13
+1 To zadziałało dla mnie. Wywołanie „setTableHeaderView” po zmianie rozmiaru widoku podrzędnego jest kluczem. Problem polega na tym, że mój widok podrzędny zmienia rozmiar w ciągu sekundy jako animacja. Teraz próbuję dowiedzieć się, jak za jego pomocą animować tableHeaderView.
Andrew
1
Świetnie, wielkie dzięki. Dla mnie jest to przykład jednej z mniej pożądanych cech właściwości w Objective-C. Nie ma sposobu, abyśmy wiedzieli (i nie ma powodu, dla którego powinniśmy wiedzieć), że ustawienie nagłówka ma skutek uboczny w postaci ponownego obliczenia wysokości. Powinien to zrobić automatycznie, gdy zaktualizujemy wysokość nagłówka, albo za [tableView recalculateHeaderHeight]każdym razem powinniśmy wywołać coś takiego .
jakeboxer
48
@Andrew wiem, że to prawie rok zbyt późno, ale lepiej późno nigdy potem: Udało mi się ożywić zmieniający wysokość widoku nagłówka tabeli owijając setTableHeaderView:połączenia z [tableview beginUpdates]i[tableview endUpdates]
jasongregori
2
@jasongregori przydatny komentarz, który dodałeś - widzę, że ta sama technika (beginUpdates + endUpdates) nie animuje zmiany wysokości dla tableFooterView tak, jak robi to tableHeaderView. Czy znalazłeś dobry sposób animowania również tableFooterView?
kris
1
Imponujące, to zdarzenie działa, gdy robi się to wewnątrz bloku animacji UIView, chociaż nie sądzę, aby było to zbyt wydajne w tym przypadku.
Może
12

Ta odpowiedź jest stara i najwyraźniej nie działa na iOS 7 i nowszych.

Napotkałem ten sam problem, a także chciałem, aby zmiany były animowane, więc utworzyłem podklasę UIView dla mojego widoku nagłówka i dodałem te metody:

- (void)adjustTableHeaderHeight:(NSUInteger)newHeight{
    NSUInteger oldHeight = self.frame.size.height;
    NSInteger originChange = oldHeight - newHeight;

    [UIView beginAnimations:nil context:nil];

    [UIView setAnimationDuration:1.0f];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    self.frame = CGRectMake(self.frame.origin.x, 
                        self.frame.origin.y, 
                        self.frame.size.width, 
                        newHeight);

    for (UIView *view in [(UITableView *)self.superview subviews]) {
        if ([view isKindOfClass:[self class]]) {
            continue;
        }
        view.frame = CGRectMake(view.frame.origin.x, 
                            view.frame.origin.y - originChange, 
                            view.frame.size.width, 
                            view.frame.size.height);
    }

    [UIView commitAnimations];
}

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
    [(UITableView *)self.superview setTableHeaderView:self];
}

To zasadniczo animuje wszystkie widoki podrzędne UITableView, które nie są tego samego typu klasy, co klasa wywołująca. Na końcu animacji wywołuje setTableHeaderView w superviewie (UITableView) - bez tego zawartość UITableView przeskoczy z powrotem przy następnym przewijaniu przez użytkownika. Jedynym ograniczeniem, które do tej pory znalazłem, jest to, że jeśli użytkownik spróbuje przewinąć UITableView podczas animacji, przewijanie będzie animowane tak, jakby widok nagłówka nie został zmieniony (nic wielkiego, jeśli animacja jest szybki).

garrettmoon
źródło
działa idealnie, usunąłem animację bo jej nie potrzebowałem.
Jiho Kang
Działało idealnie, dopóki nie wydarzyło się iOS7 ... dodałem moje rozwiązanie poniżej
avishic
1
WTF? Tak bardzo bawisz się w UITableView, że naprawdę nie powinieneś się dziwić, że nie działa w nowszych wersjach iOS ...;)
Daniel Rinser
10

Jeśli chcesz warunkowo animować zmiany, możesz wykonać następujące czynności:

- (void) showHeader:(BOOL)show animated:(BOOL)animated{

    CGRect closedFrame = CGRectMake(0, 0, self.view.frame.size.width, 0);
    CGRect newFrame = show?self.initialFrame:closedFrame;

    if(animated){
        // The UIView animation block handles the animation of our header view
        [UIView beginAnimations:nil context:nil];
        [UIView setAnimationDuration:0.3];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        // beginUpdates and endUpdates trigger the animation of our cells
        [self.tableView beginUpdates];
    }

    self.headerView.frame = newFrame;
    [self.tableView setTableHeaderView:self.headerView];

    if(animated){
        [self.tableView endUpdates];
        [UIView commitAnimations];
    }
}

Zwróć uwagę, że animacja jest podwójna:

  1. Animacja komórek poniżej tableHeaderView. Odbywa się to za pomocą beginUpdatesiendUpdates
  2. Animacja aktualnego widoku nagłówka. Odbywa się to za pomocą UIViewbloku animacji.

Aby zsynchronizować te dwie animacje animationCurve, należy ustawić na UIViewAnimationCurveEaseInOuti czas trwania 0.3, który wydaje się być tym, czego używa UITableView do swojej animacji.

Aktualizacja

Stworzyłem projekt Xcode na gihub, który to robi. Sprawdź projekt ResizeTableHeaderViewAnimatedw besi / ios-quickies

zrzut ekranu

Besi
źródło
2
Ta technika działa, ale nie działa w przypadku widoków tabeli z nagłówkami sekcji. Kiedy używasz opcji Rozpocznij aktualizacje / Zakończ aktualizacje, koliduje z blokiem animacji, pozostawiając zduplikowane nagłówki sekcji.
BlueFish
9

Myślę, że powinno działać, jeśli po prostu ustawisz wysokość myHeaderView w następujący sposób:

CGRect newFrame = myHeaderView.frame;
newFrame.size.height = newFrame.size.height + 100;
myHeaderView.frame = newFrame;

self.tableView.tableHeaderView = myHeaderView;
Greg Martin
źródło
To faktycznie działa, ale tylko wtedy, gdy jest używane w
viewDidLayoutSubviews
6

Użyto rozwiązania @garrettmoon powyżej do iOS 7.
Oto zaktualizowane rozwiązanie oparte na @ garrettmoon:

- (void)adjustTableHeaderHeight:(NSUInteger)newHeight animated:(BOOL)animated {

    [UIView beginAnimations:nil context:nil];

    [UIView setAnimationDuration:[CATransaction animationDuration]];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];

    self.frame = CGRectMake(self.frame.origin.x,
                        self.frame.origin.y,
                        self.frame.size.width,
                        newHeight);

    [(UITableView *)self.superview setTableHeaderView:self];

    [UIView commitAnimations];
}

- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
    [(UITableView *)self.superview setTableHeaderView:self];
}
avishic
źródło
5

U mnie to zadziałało na iOS 7 i 8. Ten kod działa na kontrolerze widoku tabeli.

[UIView animateWithDuration:0.3 animations:^{
    CGRect oldFrame = self.headerView.frame;
    self.headerView.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, oldFrame.size.width, newHeight);
    [self.tableView setTableHeaderView:self.headerView];
}];
Darcy Rayner
źródło
4

Dzieje się tak, ponieważ ustawiacz tableHeaderView.

Musisz ustawić wysokość UIView przed ustawieniem tableHeaderView. (Byłoby znacznie łatwiej, gdyby Apple otworzył źródła tego frameworka ...)

Thomas Decaux
źródło
4

W systemie iOS 9 i starszym tableHeaderViewnie zmieniał układu po zmianie jego rozmiaru. Ten problem został rozwiązany w iOS 10.

Aby rozwiązać ten problem, zrób to za pomocą następującego kodu:

self.tableView.tableHeaderView = self.tableView.tableHeaderView;
klaudz
źródło
2

W iOS 9.x robienie tego viewDidLoaddziała dobrze:

var frame = headerView.frame
frame.size.height = 11  // New size
headerView.frame = frame

headerViewjest zadeklarowany jako @IBOutlet var headerView: UIView!i połączony w scenorysie, gdzie jest umieszczony w górnej części tableView, aby działał jako tableHeaderView.

Eneko Alonso
źródło
2

Dotyczy to tylko sytuacji, gdy używasz automatycznego układu i ustawisz translatesAutoresizingMaskIntoConstraints = falseniestandardowy widok nagłówka.

Najlepszym i najprostszym sposobem jest nadpisanie intrinsicContentSize. Wewnętrznie UITableViewużywa intrinsicContentSizedo określenia rozmiaru nagłówka / stopki. Po nadpisaniu intrinsicContentSizew widoku niestandardowym należy wykonać poniższe czynności

  1. skonfigurować niestandardowy układ widoku nagłówka / stopki (podglądy)
  2. odwołać się invalidateIntrinsicContentSize()
  3. wywołać tableView.setNeedsLayout()itableView.layoutIfNeeded()

Następnie UITableViewnagłówek / stopka zostaną zaktualizowane tak, jak chcesz. Nie ma potrzeby ustawiania zerowego widoku ani resetowania.

Jedną z rzeczy naprawdę interesujące dla UITableView.tableHeaderViewlub .tableFooterViewjest to, że UIStackViewtracą swoją zdolność do zarządzania swoim arrangedSubviews. Jeśli chcesz użyć UIStackViewjako tableHeaderView lub tableFooterView, trzeba osadzić stackView w sposób UIViewi zastąpić UIView„s intrinsicContentSize.

Ryan
źródło
2

Dla szybkiego 5 przetestowanego kodu

override func viewDidLayoutSubviews() {
      super.viewDidLayoutSubviews()

        guard let headerView = self.tblProfile.tableHeaderView else {
            return
        }

        let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)

        if headerView.frame.size.height != size.height {
            headerView.frame.size.height = size.height
            self.tblProfile.tableHeaderView = headerView
            self.tblProfile.layoutIfNeeded()
        }
    }

Uwaga: musisz podać wszystkie ograniczenia widoku podrzędnego z góry, dołu, początku, końca. Więc otrzyma cały wymagany rozmiar.

Źródła zaczerpnięte z: https://useyourloaf.com/blog/variable-height-table-view-header/

Hardik Thakkar
źródło
1

Ustawienie wysokości właściwości widoku nagłówka tableView.tableHeaderVieww programie viewDidLoadwydaje się nie działać, wysokość widoku nagłówka nadal nie zmienia się zgodnie z oczekiwaniami.

Po wielu próbach walki z tym problemem. Odkryłem, że możesz zmienić wysokość, wywołując widok nagłówka, tworząc logikę wewnątrz - (void)didMoveToParentViewController:(UIViewController *)parentmetody.

Przykładowy kod wyglądałby więc następująco:

- (void)didMoveToParentViewController:(UIViewController *)parent {
    [super didMoveToParentViewController:parent];

    if ( _tableView.tableHeaderView == nil ) {
        UIView *header = [[[UINib nibWithNibName:@"your header view" bundle:nil] instantiateWithOwner:self options:nil] firstObject];

        header.frame = CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), HeaderViewHeight);

        [_tableView setTableHeaderView:header];
    }
}
Enix
źródło
0

Zauważyłem, że inicjator initWithFrame UIView nie honoruje poprawnie przekazanego przeze mnie polecenia. W związku z tym wykonałem następujące czynności, które działały idealnie:

 - (id)initWithFrame:(CGRect)aRect {

    CGRect frame = [[UIScreen mainScreen] applicationFrame];

    if ((self = [super initWithFrame:CGRectZero])) {

        // Ugly initialization behavior - initWithFrame will not properly honor the frame we pass
        self.frame = CGRectMake(0, 0, frame.size.width, 200);

        // ...
    }
}

Zaletą tego jest to, że jest lepiej zawarty w kodzie widoku.

Harald Schubert
źródło
Pobieranie ramki z UIScreento zły pomysł, jak ponownie wykorzystasz swój widok?
Zorayr,
0

Zaimplementowałem animowaną zmianę wysokości nagłówka stołu, aby rozwinąć się do ogólnego ekranu po dotknięciu. Jednak kod może pomóc w innych przypadkach:

// Swift
@IBAction func tapped(sender: UITapGestureRecognizer) {

    self.tableView.beginUpdates()       // Required to update cells. 

    // Collapse table header to original height
    if isHeaderExpandedToFullScreen {   

        UIView.animateWithDuration(0.5, animations: { () -> Void in
            self.scrollView.frame.size.height = 110   // original height in my case is 110
        })

    }
    // Expand table header to overall screen            
    else {      
        let screenSize = self.view.frame           // "screen" size

        UIView.animateWithDuration(0.5, animations: { () -> Void in
            self.scrollView.frame.size.height = screenSize.height
        })
    }

    self.tableView.endUpdates()  // Required to update cells. 

    isHeaderExpandedToFullScreen= !isHeaderExpandedToFullScreen  // Toggle
}
Alexander Volkov
źródło
0

Nagłówek zmiany rozmiaru UITableView - UISearchBar z paskiem zakresu

Chciałem UITableViewz UISearchBarjako nagłówek do stołu więc mam hierarchię, która wygląda następująco

UITableView
  |
  |--> UIView
  |     |--> UISearchBar
  |
  |--> UITableViewCells

Metody UISearchBarDelegate

Jak stwierdzono w innym miejscu, jeśli nie ustawisz tabeliTableViewHeader po jej zmianie, nic się nie stanie.

- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
    searchBar.showsScopeBar = YES;
    [UIView animateWithDuration:0.2f animations:^{
        [searchBar sizeToFit];
        CGFloat height = CGRectGetHeight(searchBar.frame);

        CGRect frame = self.tableView.tableHeaderView.frame;
        frame.size.height = height;
        self.tableHeaderView.frame = frame;
        self.tableView.tableHeaderView = self.tableHeaderView;
    }];

    [searchBar setShowsCancelButton:YES animated:YES];
    return YES;
}

- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
{
    searchBar.showsScopeBar = NO;
    [UIView animateWithDuration:0.f animations:^{
        [searchBar sizeToFit];

        CGFloat height = CGRectGetHeight(searchBar.frame);

        CGRect frame = self.tableView.tableHeaderView.frame;
        frame.size.height = height;
        self.tableHeaderView.frame = frame;
        self.tableView.tableHeaderView = self.tableHeaderView;
    }];

    [searchBar setShowsCancelButton:NO animated:YES];
    return YES;
}
Cameron Lowell Palmer
źródło
0

Jeśli niestandardowy headerView jest zaprojektowany przy użyciu autolayout i headerView musi zostać zaktualizowany po pobraniu z sieci Web lub podobnym leniwym zadaniu. następnie w iOS-Swift zrobiłem to i zaktualizowałem nagłówek nagłówka za pomocą poniższego kodu:

//to reload your cell data
self.tableView.reloadData()
dispatch_async(dispatch_get_main_queue(),{
// this is needed to update a specific tableview's headerview layout on main queue otherwise it's won't update perfectly cause reloaddata() is called
  self.tableView.beginUpdates()
  self.tableView.endUpdates()
} 
Rafat touqir Rafsun
źródło
0

Oczywiście Apple powinno już zaimplementować UITableViewAutomaticDimension dla tableHeaderView i tableFooterView ...

Wydaje się, że przy użyciu ograniczeń dotyczących układu działają u mnie następujące elementy:

CGSize   s  = [ self  systemLayoutSizeFittingSize : UILayoutFittingCompressedSize ];
CGRect   f  = [ self  frame ];

f.size   = s;

[ self  setFrame : f ];
digitaldaemon
źródło
0

Jeśli tableHeaderView jest elementem webView z regulacją zawartości, możesz spróbować:

[self.webView.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    self.webView.height = self.webView.scrollView.contentSize.height;
    self.tableView.tableHeaderView = self.webView;
}

Przetestowałem to na iOS9 i iOS11, działało dobrze.

无 夜 之 星辰
źródło
-3

Czy próbowałeś [self.tableView reloadData]po zmianie wysokości?

codelogic
źródło
1
Chodzi o tableHeaderView, który jest widokiem statycznym. - [UITableView reloadData]jest przeznaczony tylko do dynamicznych widoków (komórek), a także do sekcji Nagłówki, o których oczywiście myśleliście;)
Julian F. Weinert