Làm cách nào để thay đổi kích thước tableHeaderView của UITableView?


103

Tôi đang gặp sự cố khi thay đổi kích thước tableHeaderView. Nó đơn giản không hoạt động.

1) Tạo một UITableView và UIView (100 x 320 px);

2) Đặt UIView dưới dạng tableHeaderView của UITableView;

3) Xây dựng và Bắt đầu. Mọi thứ ổn cả.

Bây giờ, tôi muốn thay đổi kích thước tableHeaderView, vì vậy tôi thêm mã này vào 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;

Chiều cao của tableHeaderView sẽ xuất hiện với 200, nhưng xuất hiện với 100.

Nếu tôi viết:

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;

Sau đó, nó bắt đầu với 200 chiều cao, như tôi muốn. Nhưng tôi muốn có thể sửa đổi nó trong thời gian chạy.

Tôi cũng đã thử điều này, nhưng không thành công:

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];

Vấn đề ở đây là: Làm cách nào để thay đổi kích thước của tableHeaderView trong thời gian chạy ???

Có ai có thể làm điều này?

Cảm ơn

iMe

Câu trả lời:


180

FYI: Tôi đã làm cho điều này hoạt động bằng cách sửa đổi tableHeaderView và thiết lập lại nó. Trong trường hợp này, tôi đang điều chỉnh kích thước của tableHeaderView khi chế độ xem phụ UIWebView tải xong.

[webView sizeToFit];
CGRect newFrame = headerView.frame;
newFrame.size.height = newFrame.size.height + webView.frame.size.height;
headerView.frame = newFrame;
[self.tableView setTableHeaderView:headerView];

13
+1 Điều này đã làm việc cho tôi. Gọi 'setTableHeaderView' sau khi lượt xem phụ của bạn đã thay đổi kích thước là chìa khóa. Vấn đề là, chế độ xem phụ của tôi thay đổi kích thước trong một giây dưới dạng hoạt ảnh. Bây giờ tôi đang cố gắng tìm ra cách tạo hoạt ảnh cho tableHeaderView với nó.
Andrew

1
Hoàn hảo, cảm ơn rất nhiều. Đối với tôi, đây là một ví dụ về một trong những phẩm chất ít được mong muốn của các thuộc tính trong Objective-C. Không có cách nào để chúng tôi biết (và không có lý do gì chúng tôi nên biết) rằng việc đặt tiêu đề có tác dụng phụ là tính toán lại chiều cao. Nó sẽ tự động làm điều đó khi chúng tôi cập nhật chiều cao của tiêu đề hoặc chúng tôi phải gọi một cái gì đó giống như [tableView recalculateHeaderHeight]mọi lần.
jakeboxer

48
@Andrew Tôi biết điều này đã quá muộn gần một năm, nhưng muộn hơn thì không bao giờ: Tôi có thể tạo hiệu ứng cho chiều cao thay đổi của chế độ xem tiêu đề bảng bằng cách gói lệnh setTableHeaderView:gọi bằng [tableview beginUpdates][tableview endUpdates]
jasongregori 15/11/11

2
Nhận xét hữu ích @jasongregori mà bạn đã thêm - tôi thấy rằng kỹ thuật tương tự (beginUpdates + endUpdates) không tạo hiệu ứng thay đổi chiều cao cho tableFooterView như cách nó thực hiện với tableHeaderView. Bạn đã tìm ra cách hay để tạo hiệu ứng cho tableFooterView chưa?
kris

1
Thật ấn tượng, sự kiện nó hoạt động khi thực hiện nó bên trong khối hoạt ảnh UIView, mặc dù tôi không nghĩ nó rất hiệu quả trong trường hợp đó.
Có thể

12

Câu trả lời này đã cũ và dường như không hoạt động trên iOS 7 trở lên.

Tôi đã gặp phải vấn đề tương tự và tôi cũng muốn các thay đổi để tạo hoạt ảnh, vì vậy tôi đã tạo một lớp con của UIView cho chế độ xem tiêu đề của mình và thêm các phương thức sau:

- (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];
}

Về cơ bản, điều này tạo hoạt ảnh cho tất cả các lần xem con của UITableView không cùng loại với lớp gọi. Ở cuối hoạt ảnh, nó gọi setTableHeaderView trên superview (UITableView) - nếu không có điều này, nội dung UITableView sẽ nhảy trở lại vào lần tiếp theo người dùng cuộn. Hạn chế duy nhất mà tôi đã tìm thấy về vấn đề này cho đến nay là nếu người dùng cố gắng cuộn UITableView trong khi hoạt ảnh đang diễn ra, việc cuộn sẽ tạo hoạt ảnh như thể chế độ xem tiêu đề chưa được thay đổi kích thước (không phải là vấn đề lớn nếu hoạt ảnh nhanh chóng).


hoạt động hoàn hảo, loại bỏ hoạt ảnh vì tôi không cần nó.
Jiho Kang

Hoạt động hoàn hảo cho đến khi iOS7 xảy ra ... đã thêm giải pháp của tôi bên dưới
avishic 24/09/2013

1
WTF? Bạn đang rối tung quá nhiều với bên trong của UITableView mà bạn thực sự không nên ngạc nhiên rằng nó không làm việc trong các phiên bản iOS mới ...;)
Daniel Rinser

10

Nếu bạn muốn tạo hoạt ảnh có điều kiện cho các thay đổi, bạn có thể làm như sau:

- (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];
    }
}

Xin lưu ý rằng hoạt ảnh được gấp hai lần:

  1. Hoạt ảnh của các ô bên dưới tableHeaderView. Điều này được thực hiện bằng cách sử dụngbeginUpdatesendUpdates
  2. Hoạt ảnh của chế độ xem tiêu đề thực tế. Điều này được thực hiện bằng cách sử dụng một UIViewkhối hoạt hình.

Để đồng bộ hóa hai hoạt ảnh đó, animationCurvebạn phải đặt thành UIViewAnimationCurveEaseInOutvà thời lượng thành0.3 , đây dường như là những gì UITableView sử dụng cho hoạt ảnh của nó.

Cập nhật

Tôi đã tạo một dự án Xcode trên gihub, dự án này thực hiện điều này. Kiểm tra dự án ResizeTableHeaderViewAnimatedtrong besi / ios-quickies

ảnh chụp màn hình


2
Kỹ thuật này hoạt động, nhưng nó không hoạt động đối với các dạng xem bảng có tiêu đề phần. Khi bạn sử dụng cập nhật bắt đầu / cập nhật kết thúc, nó sẽ can thiệp vào khối hoạt ảnh, để lại các tiêu đề phần trùng lặp.
BlueFish

9

Tôi nghĩ rằng nó sẽ hoạt động nếu bạn chỉ đặt chiều cao của myHeaderView như vậy:

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

self.tableView.tableHeaderView = myHeaderView;

Đây thực sự hoạt động, nhưng chỉ khi được sử dụng trong viewDidLayoutSubviews
KoCMoHaBTa

6

Đã sử dụng giải pháp @garrettmoon ở trên cho đến iOS 7.
Đây là giải pháp được cập nhật dựa trên @ garrettmoon's:

- (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];
}

5

Điều này đã hoạt động đối với tôi trên iOS 7 và 8. Mã này đang chạy trên bộ điều khiển chế độ xem bảng.

[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];
}];

4

Đó là bởi vì trình thiết lập của tableHeaderView.

Bạn phải đặt chiều cao UIView trước khi đặt tableHeaderView. (Sẽ dễ dàng hơn nhiều nếu Apple mở nguồn khuôn khổ này ...)


4

Trên iOS 9 trở xuống, tableHeaderViewsẽ không bố trí lại sau khi thay đổi kích thước. Sự cố này đã được giải quyết trong iOS 10.

Để giải quyết vấn đề này, chỉ cần thực hiện với mã sau:

self.tableView.tableHeaderView = self.tableView.tableHeaderView;

2

Trên iOS 9.x, thực hiện điều này trên viewDidLoadhoạt động tốt:

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

headerViewđược khai báo là @IBOutlet var headerView: UIView!và được kết nối trên bảng phân cảnh, nơi nó được đặt ở đầu tableView, để hoạt động như tableHeaderView.


2

Điều này chỉ dành cho khi bạn sử dụng bố cục tự động và đặt thành translatesAutoresizingMaskIntoConstraints = falsechế độ xem tiêu đề tùy chỉnh.

Cách tốt nhất và đơn giản nhất là ghi đè intrinsicContentSize. UITableViewSử dụng nội bộ intrinsicContentSizeđể quyết định kích thước đầu trang / chân trang của nó. Khi bạn đã ghi đè intrinsicContentSizetrong chế độ xem tùy chỉnh của mình, Điều bạn cần làm là như bên dưới

  1. định cấu hình bố cục của chế độ xem đầu trang / chân trang tùy chỉnh (các lượt xem phụ)
  2. cầu khẩn invalidateIntrinsicContentSize()
  3. kêu gọi tableView.setNeedsLayout()tableView.layoutIfNeeded()

Sau đó, UITableViewđầu trang / chân trang của sẽ được cập nhật như bạn muốn. Không cần đặt nil hoặc đặt lại chế độ xem.

Một điều thực sự thú vị đối với UITableView.tableHeaderViewhoặc .tableFooterViewUIStackViewmất khả năng quản lý của nó arrangedSubviews. Nếu bạn muốn sử dụng UIStackViewdưới dạng tableHeaderView hoặc tableFooterView, bạn phải nhúng stackView vào a UIViewvà override UIView's intrinsicContentSize.


2

Đối với mã thử nghiệm nhanh 5

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()
        }
    }

Lưu ý: Bạn cần cung cấp cho tất cả các ràng buộc của chế độ xem phụ ở dạng đầu, cuối, đầu, cuối. Vì vậy, nó sẽ nhận được toàn bộ kích thước yêu cầu.

Tham khảo lấy từ: https://useyourloaf.com/blog/variable-height-table-view-header/


1

Đặt chiều cao cho tableView.tableHeaderViewthuộc tính chế độ xem tiêu đề trong viewDidLoadcó vẻ không hoạt động, chiều cao chế độ xem tiêu đề vẫn không thay đổi như mong đợi.

Sau khi chiến đấu chống lại vấn đề này trong nhiều lần thử. Tôi thấy rằng, bạn có thể thay đổi chiều cao bằng cách gọi chế độ xem tiêu đề tạo logic bên trong - (void)didMoveToParentViewController:(UIViewController *)parent phương thức.

Vì vậy, mã ví dụ sẽ giống như sau:

- (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];
    }
}

0

Tôi nhận thấy trình khởi tạo initWithFrame của UIView không phù hợp với trực tiếp mà tôi đi vào. Do đó, tôi đã thực hiện những điều sau đây hoạt động hoàn hảo:

 - (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);

        // ...
    }
}

Ưu điểm của điều này là nó được đóng gói tốt hơn vào mã chế độ xem của bạn.


Tìm nạp khung hình từ UIScreenlà một ý tưởng tồi, bạn sẽ sử dụng lại khung nhìn của mình như thế nào?
Zorayr

0

Tôi đã triển khai thay đổi chiều cao động của tiêu đề của bảng để mở rộng ra màn hình tổng thể khi chạm vào. Tuy nhiên, mã có thể giúp ích trong các trường hợp khác:

// 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
}

0

Tiêu đề thay đổi kích thước UITableView - UISearchBar với Scope Bar

Tôi muốn một UITableViewvới một UISearchBarlàm tiêu đề cho bảng để tôi có một hệ thống phân cấp trông như thế này

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

Phương pháp UISearchBarDelegate

Như đã được nêu ở phần khác, nếu bạn không setTableViewHeader sau khi thay đổi nó, sẽ không có gì xảy ra.

- (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;
}

0

Nếu headerView tùy chỉnh được thiết kế bằng cách sử dụng autolayout và headerView cần được cập nhật sau khi tìm nạp web hoặc tác vụ lười tương tự. sau đó trong iOS-Swift, tôi đã làm điều này và đã cập nhật headerView của tôi bằng cách sử dụng mã dưới đây:

//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()
} 

0

Rõ ràng là bây giờ Apple nên triển khai UITableViewAutomaticDimension cho tableHeaderView & tableFooterView ...

Điều sau có vẻ hiệu quả với tôi bằng cách sử dụng (các) đối chiếu bố cục:

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

f.size   = s;

[ self  setFrame : f ];

0

Nếu tableHeaderView của bạn là một webView có thể điều chỉnh nội dung, bạn có thể thử:

[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;
}

Tôi đã thử nghiệm nó trên iOS9 và iOS11, hoạt động tốt.


-3

Bạn đã thử [self.tableView reloadData]sau khi thay đổi chiều cao chưa?


1
Đó là về tableHeaderView, là một chế độ xem tĩnh. - [UITableView reloadData]chỉ dành cho các quan điểm động (tế bào) và cũng là sectionHeaders mà bạn có những suy nghĩ đã có nghĩa là;)
Julian F. Weinert
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.