Có thể sử dụng AutoLayout với tableHeaderView của UITableView không?


97

Kể từ khi tôi phát hiện ra AutoLayouttôi sử dụng nó ở mọi nơi, bây giờ tôi đang cố gắng sử dụng nó với a tableHeaderView.

Tôi đã thực hiện một subclasssố UIViewbổ sung tất cả mọi thứ (nhãn vv ...) Tôi muốn làm việc khó khăn của họ, sau đó tôi thêm này CustomViewđến UITableView' tableHeaderView.

Mọi thứ hoạt động tốt, ngoại trừ UITableViewluôn hiển thị trên các CustomView, bởi ở trên tôi muốn nói CustomViewdưới những UITableViewvì thế nó không thể nhìn thấy!

Có vẻ như bất kể tôi làm gì, heightdấu UITableView' luôntableHeaderView là 0 (chiều rộng, x và y cũng vậy).

Câu hỏi của tôi: liệu có thể thực hiện được điều này mà không cần đặt khung theo cách thủ công không?

EDIT: Các CustomView' subviewmà tôi đang sử dụng có những khó khăn này:

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

Tôi đang sử dụng một thư viện tiện dụng 'KeepLayout' vì việc viết các ràng buộc theo cách thủ công sẽ mất nhiều thời gian và quá nhiều dòng cho một ràng buộc duy nhất nhưng các phương pháp thì tự giải thích.

UITableViewcó những hạn chế sau:

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

Tôi không biết liệu tôi có phải đặt một số ràng buộc trực tiếp trên hay không CustomView, tôi nghĩ rằng chiều cao của CustomView được xác định bởi các ràng buộc trên UILabel"tiêu đề" trong đó.

CHỈNH SỬA 2: Sau một cuộc điều tra khác, có vẻ như chiều cao và chiều rộng của CustomView đã được tính toán chính xác, nhưng phần trên cùng của CustomView vẫn ở cùng mức so với phần đầu của UITableView và chúng di chuyển cùng nhau khi tôi cuộn.


Có, nó là có thể. Bạn có thể hiển thị mã bạn đang sử dụng không? Thật khó để đưa ra lời khuyên mà không biết bạn đã thiết lập những ràng buộc nào trên chế độ xem tiêu đề.
jrturton

Một cách dễ dàng để bạn thực hiện điều này là thêm chế độ xem đó trong IB vào tableView..chỉ cần tạo chế độ xem trong cùng một cảnh có chứa tableview và kéo nó vào bảng.
Mariam K.

Tôi đang cố gắng tránh IB nhiều nhất có thể, cho đến nay tôi không phải sử dụng nó, nếu tôi không thể làm cho nó hoạt động, tôi sẽ thử với IB
ItsASecret

1
Apple khuyên các nhà phát triển sử dụng IB bất cứ khi nào có thể khi nói đến tự động thanh toán. Nó thực sự giúp tránh rất nhiều vấn đề không nhất quán.
Mariam K.

Giải pháp tự động thanh toán hoàn chỉnh thực sự là ở đây
malex

Câu trả lời:


134

Tôi đã hỏi và trả lời một câu hỏi tương tự ở đây . Tóm lại, tôi thêm tiêu đề một lần và sử dụng nó để tìm chiều cao cần thiết. Sau đó, chiều cao đó có thể được áp dụng cho tiêu đề và tiêu đề được đặt lần thứ hai để phản ánh sự thay đổi.

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

Nếu bạn có nhãn nhiều dòng, điều này cũng dựa vào chế độ xem tùy chỉnh cài đặtMaxLayoutWidth ưu tiên của mỗi nhãn:

- (void)layoutSubviews
{
    [super layoutSubviews];

    self.titleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.titleLabel.frame);
    self.subtitleLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.subtitleLabel.frame);
}

hoặc có lẽ tổng quát hơn:

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

Cập nhật tháng 1 năm 2015

Thật không may, điều này vẫn có vẻ cần thiết. Đây là một phiên bản nhanh chóng của quy trình bố trí:

tableView.tableHeaderView = header
header.setNeedsLayout()
header.layoutIfNeeded()
header.frame.size = header.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
tableView.tableHeaderView = header

Tôi thấy hữu ích khi chuyển nó thành một tiện ích mở rộng trên 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
    }
}

Sử dụng:

let header = SCAMessageView()
header.titleLabel.text = "Warning"
header.subtitleLabel.text = "Warning message here."
tableView.setAndLayoutTableHeaderView(header)

8
Một thay thế cho việc sử dụng preferredMaxLayoutWidthlà thêm một ràng buộc chiều rộng (bằng chiều rộng của chế độ xem bảng) trên chế độ xem tiêu đề trước khi sử dụng systemLayoutSizeFittingSize:.
Benjohn

2
LƯU Ý: nếu bạn gặp phải trường hợp tiêu đề nằm trên các ô đầu tiên, thì bạn đã quên đặt lại thuộc tính tiêu đề thànhself.tableView.tableHeaderView
Laszlo

7
Thường thì nó khiến tôi kinh ngạc về việc làm một việc hoàn toàn tầm thường như thế này có thể phức tạp đến mức nào.
TylerJames

5
LƯU Ý: Nếu bạn cần để có được chiều rộng chính xác như tableView, bạn sẽ nhận được chiều cao với ưu tiên ngang cần thiếtlet height = header.systemLayoutSizeFittingSize(CGSizeMake(CGRectGetWidth(self.bounds), 0), withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityFittingSizeLevel).height
JakubKnejzlik

3
Just Use header.setNeedsLayout() header.layoutIfNeeded() header.frame.size = header.systemLayoutSizeFitting(UILayoutFittingCompressedSize) self.tableHeaderView = headersẽ hoạt động trên iOS 10.2
Kesong Xie

24

Tôi không thể thêm dạng xem tiêu đề bằng cách sử dụng các ràng buộc (trong mã). Nếu tôi cung cấp chế độ xem của mình về chiều rộng và / hoặc chiều cao, tôi sẽ gặp sự cố với thông báo:

 "terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super."

Khi tôi thêm một dạng xem trong bảng phân cảnh vào dạng xem bảng của mình, nó không hiển thị ràng buộc và nó hoạt động tốt như một dạng xem tiêu đề, vì vậy tôi nghĩ rằng vị trí của dạng xem tiêu đề không được thực hiện bằng cách sử dụng các ràng buộc. Nó dường như không hoạt động như một quan điểm bình thường về mặt đó.

Chiều rộng tự động là chiều rộng của chế độ xem bảng, điều duy nhất bạn cần đặt là chiều cao - các giá trị gốc bị bỏ qua, vì vậy bạn đặt những gì cho những giá trị đó không quan trọng. Ví dụ, điều này hoạt động tốt (0,0,0,80 đối với trực tràng cũng vậy):

UIView *headerview = [[UIView alloc] initWithFrame:CGRectMake(1000,1000, 0, 80)];
headerview.backgroundColor = [UIColor yellowColor];
self.tableView.tableHeaderView = headerview;

Tôi cũng có ngoại lệ đó nhưng việc thêm một danh mục vào UITableView đã sửa nó, tôi đã tìm thấy nó trong câu trả lời đó: stackoverflow.com/questions/12610783/…
ItsASecret

Tôi vẫn sẽ thử những gì bạn đề xuất, nhưng sáng mai, là 1:34 sáng tôi sẽ đi ngủ, cảm ơn bạn rất nhiều vì đã dành thời gian trả lời! (Nhưng tôi thực sự không muốn chỉ định chiều cao, tôi muốn nó được tính toán bằng các ràng buộc tôi thiết lập trên nhãn trong CustomView)
ItsASecret

Tôi đã thử và vâng, việc đặt khung hoạt động, nhưng tôi đang tìm cách để tránh thiết lập khung, tôi sẽ tiếp tục xem xét và nếu không tìm thấy gì khác, tôi sẽ chấp nhận câu trả lời của bạn
ItsASecret

1
Tôi nhận được ngoại lệ này (hiện đang thử nghiệm 7.1) nếu chế độ xem tiêu đề được thêm vào có translatesAutoresizingMaskIntoConstraints = NO. Việc bật tính năng dịch sẽ tránh được lỗi - tôi nghi ngờ UITableViewkể từ 7.1 không cố gắng tự động xử lý chế độ xem tiêu đề của nó và muốn một cái gì đó với khung được đặt trước.
Benjohn

16

Tôi đã thấy rất nhiều phương pháp ở đây thực hiện quá nhiều thứ không cần thiết, nhưng bạn không cần nhiều như vậy để sử dụng bố cục tự động trong chế độ xem tiêu đề. Bạn chỉ cần tạo tệp xib cho mình, đặt các ràng buộc của bạn và khởi tạo nó như thế này:

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
    }

Điều này cũng hiệu quả với chúng tôi trên iOS 11 với tiêu đề chiều cao động với các nhãn nhiều dòng.
Ben Scheirman

1
Tất nhiên, bạn cũng có thể xóa tùy chọn Linh hoạt-Tự động sửa kích thước-Linh hoạt trong IB.
d4Rk

Tôi đã cố gắng thiết lập chiều cao của tableFooterView của mình (thông qua xib / nib) và không thành công trong việc thiết lập khung, chiều cao, layoutIfNeeded (), v.v. Nhưng giải pháp này cuối cùng đã cho phép tôi thiết lập nó.
vikzilla

Đừng quên đặt giới hạn chiều cao cho toàn bộ chế độ xem trong tệp xib.
Denis Kutlubaev

6

Một giải pháp khác là gửi tạo chế độ xem tiêu đề đến cuộc gọi luồng chính tiếp theo:

- (void)viewDidLoad {
    [super viewDidLoad];

    // ....

    dispatch_async(dispatch_get_main_queue(), ^{
        _profileView = [[MyView alloc] initWithNib:@"MyView.xib"];
        self.tableView.tableHeaderView = self.profileView;
    });
}

Lưu ý: Nó sửa lỗi khi chế độ xem được tải có chiều cao cố định. Tôi chưa thử khi chiều cao tiêu đề chỉ phụ thuộc vào nội dung của nó.

BIÊN TẬP :

Bạn có thể tìm thấy giải pháp rõ ràng hơn cho vấn đề này bằng cách triển khai chức năng này và gọi nó trongviewDidLayoutSubviews

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [self sizeHeaderToFit];
}

1
@TussLaszlo tableHeaderViewlà một loại lỗi với tính năng tự động thanh toán. Có một số cách giải quyết, như cách giải quyết này. Nhưng kể từ khi tôi viết bài này, tôi đã tìm ra một giải pháp tốt hơn và rõ ràng hơn ở đây stackoverflow.com/a/21099430/127493 bằng cách gọi nó - (void)sizeHeaderToFittrongviewDidLayoutSubviews
Martin

4

Mã:

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

Nó hoạt động. Cung cấp các ràng buộc tự động thanh toán thích hợp cho tất cả các lượt xem phụ tableheaderview. Nếu bạn bỏ lỡ một ràng buộc duy nhất, nó sẽ không hoạt động.
abhimuralidharan

4

Đã mở rộng giải pháp này http://collindonnell.com/2015/09/29/dynamently-sized-table-view-header-or-footer-using-auto-layout/ cho chế độ xem chân trang bảng:

@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

Hôm qua đã dành một ngày để cố gắng làm cho tableHeader tự động thay đổi kích thước / bố cục một cách chính xác. Giải pháp này phù hợp với tôi. Cảm ơn nhiều.
docchang

Chào! Bạn có thể vui lòng giải thích self.tableFooterView.transformmột phần? Tại sao nó cần thiết?
mrvn

Biến đổi @mrvn được sử dụng để di chuyển footer xuống cuối tableView.
k06a

3

Bạn có thể yêu cầu autolayout cung cấp kích thước cho bạn bằng cách sử dụng phương thức systemLayoutSizeFittingSize .

Sau đó, bạn có thể sử dụng điều này để tạo khung cho ứng dụng của mình. Kỹ thuật này hoạt động bất cứ khi nào bạn cần biết kích thước của một dạng xem sử dụng tính năng tự động thanh toán nội bộ.

Mã trong nhanh chóng trông giống như

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

Hoặc trong 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

Cũng cần lưu ý rằng trong trường hợp cụ thể này, việc ghi đè requestConstraintBasedLayout trong lớp con của bạn, dẫn đến việc thực hiện chuyển bố cục, tuy nhiên kết quả của truyền bố cục này bị bỏ qua và khung hệ thống được đặt thành chiều rộng của tableView và 0 chiều cao.


3

Những điều sau đây đã làm việc cho tôi.

  1. Sử dụng một cái cũ đơn giản UIViewlàm chế độ xem tiêu đề.
  2. Thêm lượt xem phụ vào đó UIView
  3. Sử dụng autolayout trên các lượt xem phụ

Lợi ích chính mà tôi thấy là hạn chế tính toán khung. Apple thực sự nên cập nhật UITableViewAPI của để làm cho việc này dễ dàng hơn.

Ví dụ sử dụng 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))
}

3

Những điều kỳ lạ xảy ra. systemLayoutSizeFittingSize hoạt động tốt cho iOS9, nhưng không phù hợp với iOS 8 trong trường hợp của tôi. Vì vậy, vấn đề này giải quyết khá dễ dàng. Chỉ cần lấy liên kết đến chế độ xem dưới cùng trong tiêu đề và trong viewDidLayoutSubviews sau khi giới hạn chế độ xem tiêu đề cập nhật siêu cuộc gọi bằng cách chèn chiều cao dưới dạng CGRectGetMaxY (yourview.frame) + padding

UPD: Giải pháp đơn giản nhất từ ​​trước đến nay : Vì vậy, trong chế độ xem tiêu đề, hãy đặt chế độ xem phụ và ghim nó sang trái , phải , trên cùng . Trong lượt xem phụ đó, hãy đặt các lượt xem phụ của bạn với các ràng buộc về chiều cao tự động. Sau đó giao tất cả công việc cho tự động thanh toán (không cần tính toán)

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

Kết quả là lượt xem phụ đang mở rộng / thu hẹp như bình thường, ở cuối nó gọi là viewDidLayoutSubviews. Tại thời điểm chúng tôi biết kích thước thực của chế độ xem, vì vậy hãy đặt chiều cao của headerView và cập nhật nó bằng cách gán lại. Hoạt động như một sự quyến rũ!

Cũng hoạt động cho chế độ xem chân trang.


1
Điều này lặp lại đối với tôi trong iOS 10.
Simon

3

Đã cập nhật cho 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
        }
    }
}

2

bạn có thể thêm ràng buộc vị trí trên cùng + ngang giữa tiêu đề và chế độ xem bảng, để đặt nó một cách chính xác (nếu bản thân tiêu đề chứa tất cả các ràng buộc bố cục bên trong cần thiết để có một khung chính xác)

trong phương thức viewDidLoad tableViewController

    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

1

Chế độ xem tiêu đề bảng của tôi là một lớp con UIView - Tôi đã tạo một UIView contentView trong trình khởi tạo, với các giới hạn của nó giống như khung của chế độ xem tiêu đề bảng và thêm tất cả các đối tượng của tôi làm chế độ xem phụ của nó.

Sau đó, thêm các ràng buộc cho các đối tượng của bạn trong layoutSubviewsphương thức của dạng xem tiêu đề bảng thay vì trong trình khởi tạo. Điều đó đã giải quyết được sự cố.

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

1

AutoLayout của tôi đang hoạt động rất tốt:

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;

Tôi đã không làm điều này chính xác nhưng bạn đã cho tôi một ý tưởng hay - loại bỏ headerView, thiết lập lại khung của nó và thêm nó trở lại.
dinesharjani

1

Đối với hầu hết các trường hợp, giải pháp tốt nhất chỉ đơn giản là không chống lại khuôn khổ và nắm lấy các mặt nạ tự động khôi phục:

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

Bằng cách sử dụng mặt nạ tự động khôi phục, bạn đang cho khung công tác biết cách chế độ xem của bạn sẽ thay đổi kích thước của nó khi chế độ xem siêu cấp thay đổi kích thước của nó. Nhưng thay đổi này dựa trên khung ban đầu bạn đã đặt.


0

Tôi biết đây là một bài viết cũ nhưng Sau khi xem qua tất cả các bài đăng SO liên quan đến điều này và trải qua cả buổi chiều để chơi với điều này, cuối cùng tôi đã nghĩ ra một giải pháp đơn giản và gọn gàng

Trước hết, hệ thống phân cấp chế độ xem của tôi trông như thế này:

  1. Bảng xem
    1. Lượt xem tableHeaderView
      1. Xem với một cửa hàng được gọi là headerView

Bây giờ bên trong Chế độ xem (số 3), tôi thiết lập tất cả các ràng buộc như bình thường, bao gồm cả không gian dưới cùng để chứa. Điều này sẽ làm cho vùng chứa (tức là 3.View tức là headerView) tự kích thước dựa trên các lượt xem phụ của nó và các ràng buộc của chúng.

Sau đó, tôi đặt các ràng buộc giữa 3. View2. Viewsau:

  1. Không gian trên cùng cho vùng chứa: 0
  2. Không gian dẫn đến vùng chứa: 0
  3. Khoảng trống sau đến vùng chứa: 0

Lưu ý rằng tôi cố ý bỏ qua khoảng trống dưới cùng.

Khi tất cả điều này được thực hiện trong bảng phân cảnh, mọi thứ còn lại phải làm là dán ba dòng mã đó:

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

0

Mẹo: Nếu bạn sử dụng phương thức setAndLayoutTableHeaderView, bạn nên cập nhật khung của lượt xem phụ, vì vậy trong tình huống này, UILabel's favouriteMaxLayoutWidth phải gọi trước khi systemLayoutSizeFittingSize được gọi, không gọi trong layoutSubview.

chương trình mã


0

Chia sẻ cách tiếp cận của tôi.

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

Sử dụng.

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

0

Trong trường hợp của tôi, phương pháp với systemLayoutSizeFittingSize vì một số lý do đã không hoạt động. Điều phù hợp với tôi là một sửa đổi của giải pháp được đăng bởi HotJard (giải pháp gốc của anh ấy cũng không hoạt động trong trường hợp của tôi trên iOS 8). Những gì tôi cần làm là trong chế độ xem tiêu đề, đặt một chế độ xem phụ và ghim nó sang trái, phải, trên cùng (không ghim xuống dưới). Đặt mọi thứ bằng cách sử dụng autolayout trong lượt xem phụ đó và trong mã thực hiện việc này:

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

0

Một bài cũ. Nhưng một bài viết tốt. Đây là 2 xu của tôi.

Thứ nhất, hãy đảm bảo rằng chế độ xem tiêu đề của bạn được sắp xếp các ràng buộc để có thể hỗ trợ kích thước nội dung nội tại của chính nó. Sau đó thực hiện như sau.

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

0

Tôi đã có thể đạt được nó bằng cách tiếp cận sau (cách này hoạt động với footer theo cách tương tự).

Đầu tiên, bạn sẽ cần UITableViewphần mở rộng nhỏ :

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

Trong triển khai lớp bộ điều khiển chế độ xem của bạn:

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

Lưu ý về UIViewviệc triển khai lượt xem phụ của tiêu đề :

  1. Bạn phải chắc chắn 100% rằng chế độ xem tiêu đề của bạn có thiết lập tự động thanh toán chính xác. Tôi khuyên bạn nên bắt đầu với chế độ xem tiêu đề đơn giản chỉ với một ràng buộc chiều cao và thử thiết lập ở trên.

  2. Ghi đè requiresConstraintBasedLayoutvà trả lại true:

.

class MyHeaderView: UIView {
   // ...
   override static var requiresConstraintBasedLayout : Bool {
       return true
   }
   // ...
}

0

Đối với người dùng 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;
}

Giả sử bạn đặt tên cho chế độ xem tiêu đề của chế độ xem bảng của mình là TableviewHeader


0

Đây là cách bạn có thể làm trong 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)
    }
  }

0

Bất kỳ dựa trên ràng buộc nào UIViewcũng có thể là một điều tốt tableHeaderView.

Người ta cần đặt một tableFooterViewtrước và sau đó áp đặt thêm ràng buộc theo sau trên tableFooterViewtableHeaderView.

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

}

Người ta có thể tìm thấy tất cả các chi tiết và đoạn mã ở đây


0

Tôi đã tìm ra một cách giải quyết. bọc chế độ xem tiêu đề autolayout wrriten xib của bạn trong một trình bao bọc uiview trống và gán chế độ xem tiêu đề cho thuộc tính tableViewHeader của 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;

0

Đây là những gì hoạt động cho UITableViewController trong iOS 12,

Đặt một UIView vào TableView phía trên tất cả các ô nguyên mẫu cho đầu trang và bên dưới tất cả các ô nguyên mẫu cho chân trang. Thiết lập đầu trang và chân trang của bạn nếu cần. Đặt tất cả các ràng buộc cần thiết.

Bây giờ sử dụng các phương pháp mở rộng sau

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

và gọi nó trong ViewDidLayoutSubviews của lớp con của UITableViewController

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();

    TableView.MakeHeaderAutoDimension();
    TableView.MakeFooterAutoDimension();
}

0

Tôi đã gặp phải sự cố lấy chiều rộng 375pt, cách duy nhất phù hợp với tôi là chuyển tiếp tableView để có được chiều rộng chính xác. Tôi cũng thích Tự động thanh toán hơn thiết lập Kích thước khung hình.

Đây là phiên bản phù hợp với tôi:

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

Phiên bản Swift (sửa đổi từ câu trả lời @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
    }
}


0

Giải pháp của tôi là tạo một lớp mới như thế này.

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

}

Để sử dụng nó, chỉ cần thêm tất cả các BaseTableHeaderViewchế độ xem phụ của bạn vào một phiên bản của và đính kèm nó vào chế độ xem bảng của bạn.

let tableHeaderView = BaseTableHeaderView()
tableHeaderView.addSubview(...)
tableView.tableHeaderView = tableHeaderView

Nó sẽ tự động thay đổi kích thước dựa trên các ràng buộc của nó.


-1

Câu trả lời được chấp nhận chỉ hữu ích cho các bảng có một phần duy nhất. Đối với nhiều phần, UITableViewchỉ cần đảm bảo rằng tiêu đề của bạn kế thừa từUITableViewHeaderFooterView và bạn sẽ ổn.

Thay vào đó, chỉ cần nhúng tiêu đề hiện tại của bạn vào trong contentViewmột UITableViewHeaderFooterView. Chính xác như UITableViewCelltác phẩm.


9
câu hỏi tableHeaderViewkhông phải về tiêu đề phần.
Rpranata
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.