Apakah mungkin menggunakan AutoLayout dengan tableHeaderView milik UITableView?

97

Karena saya menemukan AutoLayoutsaya menggunakannya di mana-mana, sekarang saya mencoba menggunakannya dengan file tableHeaderView.

Saya membuat subclassdari UIViewmenambahkan semua (label dll ...) yang saya inginkan dengan batasan mereka, lalu saya menambahkan ini CustomViewke UITableView' tableHeaderView.

Semuanya bekerja dengan baik kecuali UITableViewselalu menampilkan atas yang CustomView, oleh di atas saya maksud CustomViewadalah di bawah ini UITableViewsehingga tidak bisa dilihat!

Tampaknya tidak peduli apa yang saya lakukan, heightdari UITableView' tableHeaderViewadalah selalu 0 (jadi adalah lebar, x dan y).

Pertanyaan saya: apakah mungkin untuk mencapai hal ini tanpa menyetel frame secara manual ?

EDIT: The CustomView' subviewyang saya gunakan memiliki kendala ini:

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

Saya menggunakan pustaka praktis 'KeepLayout' karena menulis batasan secara manual membutuhkan waktu yang lama dan terlalu banyak baris untuk satu batasan tunggal tetapi metodenya menjelaskan sendiri.

Dan UITableViewmemiliki batasan ini:

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

Saya tidak tahu apakah saya harus mengatur beberapa batasan secara langsung CustomView, menurut saya ketinggian CustomView ditentukan oleh batasan pada UILabel"judul" di dalamnya.

EDIT 2: Setelah penyelidikan lain, tampaknya tinggi dan lebar CustomView dihitung dengan benar, tetapi bagian atas CustomView masih berada di level yang sama dengan bagian atas UITableView dan mereka bergerak bersamaan saat saya menggulir.

Ini sebuah rahasia
sumber
Ya, itu mungkin. Bisakah Anda menunjukkan kode yang Anda gunakan? Sulit untuk memberi saran tanpa mengetahui batasan apa yang telah Anda siapkan pada tampilan header.
jrturton
Cara mudah bagi Anda untuk melakukannya adalah dengan menambahkan tampilan tersebut di IB ke tableView .. cukup buat tampilan dalam adegan yang sama yang berisi tampilan tabel dan seret ke tabel.
Mariam K.
Saya berusaha menghindari IB sebisa saya, sejauh ini saya tidak harus menggunakannya, jika saya tidak bisa membuatnya bekerja, saya akan mencoba dengan IB
Rahasia
1
Apple menyarankan pengembang untuk menggunakan IB jika memungkinkan dalam hal tata letak otomatis. Ini sangat membantu dalam menghindari banyak masalah inkonsistensi.
Mariam K.
Solusi autolayout lengkap yang benar ada di sini
malex

Jawaban:

134

Saya bertanya dan menjawab pertanyaan serupa di sini . Singkatnya, saya menambahkan tajuk sekali dan menggunakannya untuk menemukan ketinggian yang diperlukan. Ketinggian itu kemudian dapat diterapkan ke tajuk, dan tajuk disetel untuk kedua kalinya untuk mencerminkan perubahan.

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

Jika Anda memiliki label multi-baris, ini juga bergantung pada tampilan kustom yang menyetel preferMaxLayoutWidth dari setiap label:

- (void)layoutSubviews
{
    [super layoutSubviews];

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

atau mungkin lebih umum:

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

Perbarui Januari 2015

Sayangnya ini sepertinya masih perlu. Berikut ini versi cepat dari proses tata letak:

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

Saya merasa berguna untuk memindahkan ini ke dalam ekstensi di 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
    }
}

Pemakaian:

let header = SCAMessageView()
header.titleLabel.text = "Warning"
header.subtitleLabel.text = "Warning message here."
tableView.setAndLayoutTableHeaderView(header)
Ben Packard
sumber
8
Alternatif untuk menggunakan preferredMaxLayoutWidthadalah menambahkan batasan lebar (sama dengan lebar tampilan tabel) pada tampilan header sebelum menggunakan systemLayoutSizeFittingSize:.
Benjohn
2
CATATAN: jika Anda mengalami bahwa tajuk berada di atas sel pertama, maka Anda lupa mengatur ulang properti tajuk keself.tableView.tableHeaderView
Laszlo
8
Seringkali saya heran betapa sulitnya melakukan sesuatu yang benar-benar sepele seperti ini.
TylerJames
5
CATATAN: Jika Anda perlu mendapatkan lebar yang tepat sebagai tableView, Anda harus mendapatkan tinggi dengan prioritas horizontal yang diperlukanlet 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 = headerakan berfungsi di iOS 10.2
Kesong Xie
24

Saya tidak dapat menambahkan tampilan header menggunakan batasan (dalam kode). Jika saya memberikan batasan lebar dan / atau tinggi pada pandangan saya, saya mengalami error dengan pesan yang mengatakan:

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

Saat saya menambahkan tampilan di storyboard ke tampilan tabel saya, itu tidak menunjukkan kendala, dan berfungsi dengan baik sebagai tampilan header, jadi menurut saya penempatan tampilan header tidak dilakukan dengan menggunakan batasan. Tampaknya tidak berperilaku seperti pandangan normal dalam hal itu.

Lebar secara otomatis menjadi lebar tampilan tabel, satu-satunya hal yang perlu Anda setel adalah tingginya - nilai asal diabaikan, jadi tidak masalah apa yang Anda masukkan untuk itu. Misalnya, ini berfungsi dengan baik (seperti halnya 0,0,0,80 untuk rect):

UIView *headerview = [[UIView alloc] initWithFrame:CGRectMake(1000,1000, 0, 80)];
headerview.backgroundColor = [UIColor yellowColor];
self.tableView.tableHeaderView = headerview;
rdelmar.dll
sumber
Saya memiliki pengecualian itu juga, tetapi menambahkan kategori ke UITableView memperbaikinya. Saya menemukannya di jawaban itu: stackoverflow.com/questions/12610783/…
ItsASecret
Saya masih akan mencoba saran Anda, tapi besok pagi, sudah jam 1:34 pagi. Saya akan tidur, terima kasih banyak telah meluangkan waktu untuk menjawab! (Tapi saya benar-benar ingin tidak menentukan ketinggian, saya ingin itu dihitung dengan batasan yang saya siapkan pada label di CustomView)
ItsASecret
Saya sudah mencobanya dan ya pengaturan bingkai berfungsi, tetapi saya sedang mencari cara untuk menghindari pengaturan bingkai, saya akan terus mencari dan jika saya tidak menemukan yang lain, saya akan menerima jawaban Anda
Rahasianya
1
Saya mendapatkan pengecualian ini (saat ini menguji 7.1) jika tampilan tajuk yang ditambahkan memilikinya translatesAutoresizingMaskIntoConstraints = NO. Mengaktifkan terjemahan mencegah kesalahan - Saya curiga UITableViewmulai 7.1 tidak mencoba mengatur tampilan tajuknya secara otomatis dan menginginkan sesuatu dengan bingkai yang telah ditentukan sebelumnya.
Benjohn
17

Saya melihat banyak metode di sini melakukan begitu banyak hal yang tidak perlu, tetapi Anda tidak perlu terlalu banyak menggunakan tata letak otomatis di tampilan tajuk. Anda hanya perlu membuat file xib, meletakkan batasan Anda dan membuatnya seperti ini:

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
    }
Ramon Vasconcelos
sumber
Ini juga berfungsi untuk kami di iOS 11 dengan header tinggi dinamis dengan label multi-baris.
Ben Scheirman
1
Anda juga dapat menghapus flexibleHeight-Autoresizing-Option di IB tentunya.
d4Rk
Saya telah mencoba untuk mengatur ketinggian tableFooterView saya (melalui xib / nib) dan tidak berhasil mengatur frame, tinggi, layoutIfNeeded (), dll. Tetapi solusi ini akhirnya memungkinkan saya untuk mengaturnya.
vikzilla
Jangan lupa untuk mengatur batasan ketinggian ke seluruh tampilan dalam file xib.
Denis Kutlubaev
6

Solusi lain adalah mengirimkan pembuatan tampilan header ke panggilan utas utama berikutnya:

- (void)viewDidLoad {
    [super viewDidLoad];

    // ....

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

Catatan: Ini memperbaiki bug saat tampilan yang dimuat memiliki ketinggian tetap. Saya belum pernah mencoba ketika tinggi header hanya bergantung pada isinya.

EDIT:

Anda dapat menemukan solusi yang lebih bersih untuk masalah ini dengan mengimplementasikan fungsi ini , dan memanggilnyaviewDidLayoutSubviews

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    [self sizeHeaderToFit];
}
Martin
sumber
1
@TussLaszlo tableHeaderViewagak bermasalah dengan autolayout. Ada beberapa solusi, seperti ini. Tapi karena saya menulis ini, saya telah menemukan yang lebih baik dan lebih bersih solusi di sini stackoverflow.com/a/21099430/127493 dengan memanggil - (void)sizeHeaderToFitdiviewDidLayoutSubviews
Martin
4

Kode:

  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
          }
  }
Phil
sumber
Berhasil. Berikan batasan autolayout yang tepat ke semua subview tableheaderview. Jika Anda melewatkan satu kendala, itu tidak akan berhasil.
abhimuralidharan
4

Perluas solusi ini http://collindonnell.com/2015/09/29/dynamically-sized-table-view-header-or-footer-using-auto-layout/ untuk tampilan footer tabel:

@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
k06a
sumber
Menghabiskan satu hari kemarin mencoba membuat tableHeader mengubah ukuran / tata letak otomatis dengan benar. Solusi ini berhasil untuk saya. Terima kasih banyak.
docchang
Hai! Bisakah Anda menjelaskan self.tableFooterView.transformbagiannya? Mengapa itu perlu?
mrvn
@mrvn transform digunakan untuk memindahkan footer ke bagian bawah tableView.
k06a
3

Anda bisa mendapatkan autolayout untuk memberi Anda ukuran dengan menggunakan metode systemLayoutSizeFittingSize .

Anda kemudian dapat menggunakan ini untuk membuat bingkai untuk aplikasi Anda. Teknik ini berfungsi kapan pun Anda perlu mengetahui ukuran tampilan yang menggunakan tata letak otomatis secara internal.

Kode dengan cepat terlihat seperti

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

Atau di 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

Perlu juga diperhatikan bahwa dalam contoh khusus ini, mengganti requireConstraintBasedLayout di subkelas Anda, memang menghasilkan pemasangan tata letak, namun hasil dari pemasangan tata letak ini diabaikan dan frame sistem disetel ke lebar tableView dan tinggi 0.

Jonathan
sumber
3

Berikut ini berhasil untuk saya.

  1. Gunakan yang lama polos UIViewsebagai tampilan header.
  2. Tambahkan subview untuk itu UIView
  3. Gunakan autolayout pada subview

Manfaat utama yang saya lihat adalah membatasi kalkulasi bingkai. Apple harus benar-benar memperbarui UITableViewAPI untuk membuatnya lebih mudah.

Contoh menggunakan 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))
}
David Nix
sumber
3

Hal-hal aneh terjadi. systemLayoutSizeFittingSize berfungsi sangat baik untuk iOS9, tetapi tidak untuk iOS 8 dalam kasus saya. Jadi masalah ini diselesaikan dengan cukup mudah. Cukup dapatkan tautan ke tampilan bawah di header dan di viewDidLayoutSubviews setelah batas tampilan header pembaruan panggilan super dengan memasukkan tinggi sebagai CGRectGetMaxY (yourview.frame) + padding

UPD: Solusi termudah yang pernah ada : Jadi, dalam tampilan header tempatkan subview dan sematkan ke kiri , kanan , atas . Di subview itu, tempatkan subview Anda dengan batasan ketinggian otomatis. Setelah itu serahkan semua pekerjaan ke autolayout (tidak perlu perhitungan)

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

Akibatnya subview meluas / menyusut seperti seharusnya, pada akhirnya subview memanggil viewDidLayoutSubviews. Pada saat kami mengetahui ukuran sebenarnya dari tampilan, jadi setel tinggi headerView dan perbarui dengan menetapkan ulang. Bekerja seperti pesona!

Juga berfungsi untuk tampilan footer.

HotJard
sumber
1
Ini loop untuk saya di iOS 10.
Simon
3

Diperbarui untuk 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
        }
    }
}
Caleb Friden
sumber
2

Anda dapat menambahkan batasan lokasi atas + horizontal antara header dan tableview, untuk menempatkannya, dengan benar (jika header itu sendiri berisi semua batasan tata letak internal yang diperlukan untuk memiliki bingkai yang benar)

dalam metode 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
GreatWiz
sumber
1

Tampilan header tabel saya adalah subclass UIView - Saya membuat UIView contentView tunggal di dalam penginisialisasi, dengan batasannya sama dengan bingkai tampilan header tabel dan menambahkan semua objek saya sebagai subview dari itu.

Kemudian tambahkan batasan untuk objek Anda dalam layoutSubviewsmetode tampilan header tabel daripada dalam penginisialisasi. Itu memecahkan kecelakaan itu.

- (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
}
Ryan
sumber
1

AutoLayout saya bekerja dengan sangat baik:

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;
RomanV
sumber
Saya tidak melakukan ini dengan tepat tetapi Anda memberi saya ide bagus - menghapus headerView, mengatur ulang bingkainya, dan menambahkannya kembali.
dinesharjani
1

Untuk kebanyakan kasus, solusi terbaik adalah tidak melawan framework dan menggunakan masker autoresizing:

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

Dengan menggunakan autoresizing masks, Anda memberi tahu framework bagaimana tampilan Anda harus mengubah ukurannya saat superview mengubah ukurannya. Tetapi perubahan ini didasarkan pada bingkai awal yang Anda tetapkan.

pfandrade
sumber
0

Saya tahu ini adalah posting lama tetapi Setelah melalui semua posting SO tentang ini dan melewati sepanjang sore bermain dengan ini, saya akhirnya menemukan solusi yang bersih namun sangat sederhana

Pertama-tama, hierarki tampilan saya terlihat seperti ini:

  1. Tampilan Tabel
    1. Melihat tableHeaderView
      1. Lihat dengan outlet yang disebut headerView

Sekarang di dalam View (No.3), saya mengatur semua batasan seperti biasanya termasuk ruang bawah ke container. Ini akan membuat wadah (yaitu 3. Tampilan yaitu headerView) untuk mengukur dirinya sendiri berdasarkan subview dan batasannya.

Setelah itu, saya menetapkan batasan antara 3. Viewdan 2. Viewuntuk ini:

  1. Ruang Atas ke wadah: 0
  2. Leading Space to container: 0
  3. Trailing Space ke container: 0

Perhatikan bahwa saya sengaja menghilangkan ruang bawah.

Setelah semua ini selesai di papan cerita, yang perlu dilakukan adalah menempelkan tiga baris kode tersebut:

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;
}
Marc-Alexandre Bérubé
sumber
0

Tips: Jika Anda menggunakan metode setAndLayoutTableHeaderView, Anda harus mengupdate frame subview, jadi dalam situasi ini, preferMaxLayoutWidth UILabel harus memanggil sebelum systemLayoutSizeFittingSize dipanggil, jangan panggil layoutSubview.

kode acara

pengguna1511613
sumber
0

Bagikan pendekatan saya.

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

Pemakaian.

[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));
    }];
  }];
Vincent Sit
sumber
0

Dalam metode kasus saya dengan systemLayoutSizeFittingSize karena alasan tertentu tidak berfungsi. Apa yang berhasil untuk saya adalah modifikasi solusi yang diposting oleh HotJard (dia solusi asli juga tidak berfungsi dalam kasus saya di iOS 8). Yang perlu saya lakukan adalah di tampilan header, tempatkan subview dan pin ke kiri, kanan, atas (jangan sematkan ke bawah). Letakkan semuanya menggunakan autolayout di subview itu dan di kode lakukan ini:

- (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;
}
Leszek Szary
sumber
0

Posting lama. Tapi postingan yang bagus. Ini 2 sen saya.

Pertama, pastikan bahwa tampilan header Anda memiliki batasan yang diatur sehingga dapat mendukung ukuran konten intrinsiknya sendiri. Kemudian lakukan hal berikut.

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

sumber
0

Saya bisa mencapainya dengan pendekatan berikut (ini berfungsi untuk footer dengan cara yang sama).

Pertama, Anda memerlukan UITableViewekstensi kecil :

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

Dalam implementasi kelas pengontrol tampilan Anda:

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

Catatan tentang UIViewimplementasi subview header :

  1. Anda harus 100% yakin bahwa tampilan header Anda memiliki pengaturan tata letak otomatis yang benar. Saya akan merekomendasikan untuk memulai dengan tampilan tajuk sederhana hanya dengan satu kendala tinggi dan mencoba penyiapan di atas.

  2. Timpa requiresConstraintBasedLayoutdan kembalikan true:

.

class MyHeaderView: UIView {
   // ...
   override static var requiresConstraintBasedLayout : Bool {
       return true
   }
   // ...
}
Yevhen Dubinin
sumber
0

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

Dengan asumsi Anda menamai tampilan header dari tampilan tabel Anda sebagai TableviewHeader

Gustavo Baiocchi Costa
sumber
0

Inilah cara Anda melakukannya 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)
    }
  }
onmyway133
sumber
0

Apa pun yang berbasis kendala UIViewbisa menjadi bagus tableHeaderView.

Seseorang perlu menyetel tableFooterViewsebelum dan kemudian memberlakukan batasan tambahan tambahan pada tableFooterViewdan tableHeaderView.

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

}

Semua detail dan cuplikan kode dapat ditemukan di sini

malex
sumber
0

Saya telah menemukan solusinya. bungkus tampilan autolayout wrriten xib header Anda dalam pembungkus uiview kosong, dan tetapkan tampilan header ke properti 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;
tounaobun
sumber
0

Inilah yang berfungsi untuk UITableViewController di ios 12,

Drap UIView ke TableView di atas semua sel prototipe untuk header dan di bawah semua sel prototipe untuk footer. Siapkan header dan footer Anda sesuai kebutuhan. Tetapkan semua batasan yang diperlukan.

Sekarang gunakan metode ekstensi berikut

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

dan menyebutnya dalam ViewDidLayoutSubviews dari subclass UITableViewController

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

    TableView.MakeHeaderAutoDimension();
    TableView.MakeFooterAutoDimension();
}
Ayah Mabuk
sumber
0

Saya mengalami masalah untuk mendapatkan lebar 375pt, satu-satunya cara yang berhasil bagi saya adalah menyampaikan tableView untuk mendapatkan lebar yang benar. Saya juga lebih suka AutoLayout daripada mengatur Ukuran bingkai.

Ini versi yang cocok untuk saya:

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

Versi Swift (dimodifikasi dari jawaban @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
    }
}
Baron Ch'ng
sumber
0

Solusi saya adalah membuat kelas baru seperti ini.

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

}

Untuk menggunakannya, cukup tambahkan semua subview Anda ke sebuah instance BaseTableHeaderViewdan lampirkan ke tampilan tabel Anda.

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

Ini akan secara otomatis mengubah ukuran berdasarkan batasannya.

Hesse Huang
sumber
-1

Jawaban yang diterima hanya berguna untuk tabel dengan satu bagian. Untuk multi-bagian, UITableViewpastikan header Anda diwarisi dari UITableViewHeaderFooterViewdan Anda akan baik-baik saja.

Sebagai alternatif, hanya menanamkan header saat ini dalam contentViewsebuah UITableViewHeaderFooterView. Persis seperti UITableViewCellkarya.

redent84
sumber
9
Pertanyaannya tableHeaderViewbukan tentang header bagian.
Rpranata