Cara Menerapkan Header dan Footer Bagian Tampilan Tabel Khusus dengan Storyboard

176

Tanpa menggunakan storyboard, kita cukup menyeretnya UIViewke kanvas, meletakkannya dan meletakkannya di metode tableView:viewForHeaderInSectionatau tableView:viewForFooterInSectiondelegasikan.

Bagaimana kita mencapai ini dengan StoryBoard di mana kita tidak bisa menyeret UIView ke kanvas

Seamus
sumber

Jawaban:

91

Aku tahu pertanyaan ini adalah untuk iOS 5, tapi untuk kepentingan pembaca masa depan, catatan bahwa iOS efektif 6 kita dapat sekarang menggunakan dequeueReusableHeaderFooterViewWithIdentifierbukan dequeueReusableCellWithIdentifier.

Jadi viewDidLoad, telepon salah registerNib:forHeaderFooterViewReuseIdentifier:atau registerClass:forHeaderFooterViewReuseIdentifier:. Lalu masuk viewForHeaderInSection, panggil tableView:dequeueReusableHeaderFooterViewWithIdentifier:. Anda tidak menggunakan prototipe sel dengan API ini (ini adalah tampilan berbasis NIB atau tampilan yang dibuat secara program), tetapi ini adalah API baru untuk header dan footer yang dequeued.

rampok
sumber
13
menghela napas memalukan bahwa dua jawaban bersih ini tentang menggunakan NIB dan bukan sel proto saat ini di bawah 5 suara, dan jawaban "retas dengan sel proto" naik di atas 200.
Benjohn
5
Perbedaannya adalah bahwa dengan peretasan dari Tieme Anda dapat membuat desain Anda di storyboard yang sama dan tidak menggunakan Xib yang terpisah
CedricSoubrie
Dapatkah Anda entah bagaimana menghindari menggunakan file NIB yang terpisah dan menggunakan storyboard saja?
Foriger
@ Foriger - Tidak pada saat ini. Ini kelalaian aneh dalam tampilan tabel storyboard, tapi memang begitu. Gunakan kludgy hack dengan sel-sel prototipe jika Anda mau, tetapi secara pribadi, saya hanya tahan dengan gangguan NIB untuk tampilan header / footer.
Rob
2
Hanya mengatakan bahwa itu "lama" tidak cukup kuat, IMHO. Jawaban yang diterima adalah cara sederhana untuk menyiasati kenyataan bahwa Anda tidak dapat memiliki sel prototipe untuk tampilan header dan footer. Tapi saya pikir itu salah, karena menganggap bahwa dequeuing header dan footer bekerja sama dengan dequeuing sel (yang ditunjukkan oleh API baru untuk header / footer mungkin tidak demikian). Jawaban yang diterima adalah solusi kreatif yang dapat dipahami kembali di iOS 5, tetapi, di iOS 6 dan yang lebih baru, yang terbaik adalah menggunakan API yang dirancang secara eksplisit untuk digunakan kembali header / footer.
Rob
384

Cukup gunakan sel prototipe sebagai header dan / atau footer bagian Anda.

  • tambahkan sel ekstra dan masukkan elemen yang Anda inginkan di dalamnya.
  • setel pengidentifikasi ke sesuatu yang spesifik (dalam kasus saya SectionHeader)
  • menerapkan tableView:viewForHeaderInSection:metode atau tableView:viewForFooterInSection:metode tersebut
  • gunakan [tableView dequeueReusableCellWithIdentifier:]untuk mendapatkan header
  • mengimplementasikan tableView:heightForHeaderInSection:metode.

(lihat tangkapan layar)

-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    static NSString *CellIdentifier = @"SectionHeader"; 
    UITableViewCell *headerView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (headerView == nil){
        [NSException raise:@"headerView == nil.." format:@"No cells with matching CellIdentifier loaded from your storyboard"];
    }
    return headerView;
}  

Sunting: Cara mengubah judul tajuk (pertanyaan yang dikomentari):

  1. Tambahkan label ke sel header
  2. atur tag label ke nomor tertentu (mis. 123)
  3. Dalam tableView:viewForHeaderInSection:metode Anda, dapatkan label dengan menelepon:
    UILabel *label = (UILabel *)[headerView viewWithTag:123]; 
  1. Sekarang Anda dapat menggunakan label untuk menetapkan judul baru:
    [label setText:@"New Title"];
Ikat aku
sumber
7
Terlambat ke pesta, tetapi untuk menonaktifkan klik pada tampilan tajuk bagian, cukup kembalikan cell.contentView dalam viewForHeaderInSection metode (tidak perlu menambahkan UIViews khusus).
paulvs
3
@PaulVon Saya mencoba melakukan itu dalam proyek terbaru saya, tetapi jika Anda melakukannya, coba tekan kembali salah satu header Anda dan itu akan crash
Hons
13
Di iOS 6.0, dequeueReusableHeaderFooterViewWithIdentifierdiperkenalkan, dan sekarang lebih disukai daripada jawaban ini. Tetapi menggunakan itu dengan benar sekarang membutuhkan lebih banyak langkah. Saya memiliki panduan @ samwize.com/2015/11/06/… .
samwize
3
Jangan membuat sectionHeaderViews Anda menggunakan UITableViewCells, itu akan membuat perilaku yang tidak terduga. Gunakan UIView sebagai gantinya.
AppreciateIt
4
Harap jangan pernah gunakan UITableViewCellsebagai tampilan tajuk. Anda akan sangat kesulitan untuk men-debug gangguan visual - tajuk terkadang akan hilang karena bagaimana sel dikosongkan dan Anda akan mencari berjam-jam mengapa sampai Anda menyadari UITableViewCelltidak termasuk dalam UITableViewtajuk.
raven_raven
53

Di iOS 6.0 dan di atasnya, banyak hal telah berubah dengan dequeueReusableHeaderFooterViewWithIdentifierAPI baru .

Saya telah menulis panduan (diuji pada iOS 9), yang dapat diringkas seperti:

  1. Subkelas UITableViewHeaderFooterView
  2. Buat Nib dengan tampilan subclass, dan tambahkan 1 tampilan kontainer yang berisi semua tampilan lain di header / footer
  3. Daftarkan Nib di viewDidLoad
  4. Terapkan viewForHeaderInSectiondan gunakan dequeueReusableHeaderFooterViewWithIdentifieruntuk mendapatkan kembali header / footer
samwize
sumber
2
Terima kasih atas jawaban ini yang BUKAN peretasan, dan cara yang tepat untuk melakukan sesuatu. Juga, terima kasih telah memperkenalkan saya ke UITableViewHeaderFooterVIew. Btw, panduannya sangat bagus! :)
Roboris
Selamat datang. Menerapkan tajuk / catatan kaki untuk tampilan tabel atau koleksi tidak didokumentasikan dengan baik oleh Apple. Baru kemarin saya terjebak di header getter untuk ditampilkan ketika merancang melalui storyboad .
samwize
1
Ini harus menjadi jawaban berperingkat teratas untuk pasti!
Ben Williams
Bisakah Anda menjelaskan cara melakukannya melalui storyboard, bukan xib? Ketika melakukan itu, saya berhasil dequeue, tetapi UILabels saya adalah nol.
bunkerdive
22

Saya membuatnya bekerja di iOS7 menggunakan sel prototipe di storyboard. Saya memiliki tombol di tampilan tajuk bagian kustom saya yang memicu segue yang diatur di storyboard.

Mulailah dengan solusi Tieme

Seperti yang ditunjukkan oleh pedro.m, masalah dengan ini adalah mengetuk header bagian menyebabkan sel pertama di bagian tersebut dipilih.

Seperti yang ditunjukkan Paul Von, ini diperbaiki dengan mengembalikan konten sel, bukan seluruh sel.

Namun, seperti yang ditunjukkan oleh Hons, penekanan yang cukup lama pada header bagian tersebut akan merusak aplikasi.

Solusinya adalah dengan menghapus gestureRecognizers dari contentView.

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
     static NSString *CellIdentifier = @"SectionHeader";
     UITableViewCell *sectionHeaderView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

     while (sectionHeaderView.contentView.gestureRecognizers.count) {
         [sectionHeaderView.contentView removeGestureRecognizer:[sectionHeaderView.contentView.gestureRecognizers objectAtIndex:0]];
     }

     return sectionHeaderView.contentView; }

Jika Anda tidak menggunakan gerakan dalam tampilan tajuk bagian Anda, retasan kecil ini sepertinya menyelesaikannya.

Damon
sumber
2
Kamu benar. Saya baru saja menguji itu dan berfungsi, jadi saya memperbarui posting saya ( hons82.blogspot.it/2014/05/uitableviewheader-done-right.html ) yang berisi semua solusi yang mungkin saya temukan selama penelitian saya. Terima kasih banyak untuk perbaikan ini
Hons
Jawaban yang Baik ... Terima kasih Man
Jogendra.Com
13

Jika Anda menggunakan storyboard, Anda dapat menggunakan sel prototipe di tampilan tabel untuk tata letak tampilan header Anda. Menyetel id unik dan viewForHeaderInSection Anda bisa membuat sel dengan ID itu dan melemparkannya ke UIView.

barksten
sumber
12

Jika Anda memerlukan Implementasi Swift dari ini, ikuti petunjuk pada jawaban yang diterima dan kemudian di dalam Anda UITableViewController menerapkan metode berikut:

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    return tableView.dequeueReusableCell(withIdentifier: "CustomHeader")
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 75
}
JZ.
sumber
2
Untuk beberapa alasan itu tidak bekerja untuk saya sampai saya menimpa tinggi.
Entalpi
Ini kode yang cukup lama. Anda harus memposting jawaban lain!
JZ.
Saya macet karena saya tidak tahu saya harus menimpa heightForHeaderInSection. Terima kasih @Entalpi
guijob
1
@ JZ. Dalam contoh Swift 3 Anda, Anda melakukan deque dengan self.tableView.dequeueReusableHeaderFooterView dan Swift 2 berikut: self.tableView.dequeueReusableCellWithIdentifier apakah ada perbedaan?
Vrutin Rathod
1
Ini menyelamatkan pantatku! menambahkan ketinggian ke headerCell membuatnya terlihat.
CRey
9

Solusi yang saya temukan pada dasarnya adalah solusi yang sama yang digunakan sebelum pengenalan storyboard.

Buat file kelas antarmuka baru yang kosong. Seret UIView ke kanvas, tata letak seperti yang diinginkan.

Muat nib secara manual, tetapkan ke bagian header / footer yang sesuai dalam metode delegasi viewForHeaderInSection atau viewForFooterInSection.

Saya berharap Apple menyederhanakan skenario ini dengan storyboard dan terus mencari solusi yang lebih baik atau lebih sederhana. Misalnya header dan footer tabel kustom langsung ditambahkan.

Seamus
sumber
Nah, apel melakukannya jika Anda menggunakan sel storyboard sebagai metode tajuk :) stackoverflow.com/questions/9219234/#11396643
Tieme
2
Kecuali itu hanya berfungsi untuk sel prototipe, bukan sel statis.
Jean-Denis Muys
1
Salah satu solusi yang sangat mudah adalah dengan menggunakan Pixate ; Anda dapat melakukan cukup banyak penyesuaian tanpa mendapatkan referensi ke tajuk. Yang harus Anda terapkan adalah tableView:titleForHeaderInSection, yang merupakan one-liner.
John Starr Dewar
5
Itu solusi nyata ... sayangnya itu tidak di atas, jadi butuh beberapa saat untuk mengetahuinya. Saya merangkum masalah yang saya miliki hons82.blogspot.it/2014/05/uitableviewheader-done-right.html
Hons
Lebih mudah dari itu, cukup seret dan lepas.
Ricardo
5

Saat Anda mengembalikan konten sel Anda akan memiliki 2 masalah:

  1. Kecelakaan terkait dengan gerakan
  2. Anda tidak menggunakan kembali contentView (setiap kali dalam viewForHeaderInSectionpanggilan, Anda membuat sel baru)

Larutan:

Kelas pembungkus untuk header tabel \ footer. Itu hanya wadah, yang diwarisi dari UITableViewHeaderFooterView, yang menyimpan sel di dalamnya

https://github.com/Magnat12/MGTableViewHeaderWrapperView.git

Daftarkan kelas di UITableView Anda (misalnya, dalam viewDidLoad)

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.tableView registerClass:[MGTableViewHeaderWrapperView class] forHeaderFooterViewReuseIdentifier:@"ProfileEditSectionHeader"];
}

Dalam UITableViewDelegate Anda:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    MGTableViewHeaderWrapperView *view = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"ProfileEditSectionHeader"];

    // init your custom cell
    ProfileEditSectionTitleTableCell *cell = (ProfileEditSectionTitleTableCell * ) view.cell;
    if (!cell) {
        cell = [tableView dequeueReusableCellWithIdentifier:@"ProfileEditSectionTitleTableCell"];
        view.cell = cell;
    }

    // Do something with your cell

    return view;
}
Vitaliy Gozhenko
sumber
2

Saya biasa melakukan hal berikut untuk membuat tampilan header / footer dengan malas:

  • Tambahkan pengontrol tampilan bentuk bebas untuk header bagian / footer ke storyboard
  • Tangani semua hal untuk header di controller tampilan
  • Di dalam pengontrol tampilan tabel, sediakan sebuah array pengontrol tampilan yang bisa diubah untuk header bagian / footer yang dihuni kembali [NSNull null]
  • Di viewForHeaderInSection / viewForFooterInSection jika view controller belum ada, buat dengan storyboard instantiateViewControllerWithIdentifier, ingat dalam array dan kembalikan view controller view
Vipera Berus
sumber
2

Saya mengalami masalah dalam skenario di mana Header tidak pernah digunakan kembali bahkan melakukan semua langkah yang tepat.

Jadi sebagai catatan tip untuk semua orang yang ingin mencapai situasi acara bagian kosong (0 baris) diingatkan bahwa:

dequeueReusableHeaderFooterViewWithIdentifier tidak akan menggunakan kembali header sampai Anda mengembalikan setidaknya satu baris

Semoga ini bisa membantu

Juan Pedro Lozano
sumber
1

Untuk menindaklanjuti saran Damon , berikut adalah bagaimana saya membuat header dapat dipilih seperti baris normal dengan indikator pengungkapan.

Saya menambahkan Tombol subkelas dari UIButton (nama subkelas "ButtonWithArgument") ke sel prototipe header dan menghapus teks judul (teks "Judul" tebal adalah UILabel lain dalam sel prototipe)

Tombol Dalam Interface Builder

kemudian atur Tombol ke seluruh tampilan tajuk, dan menambahkan indikator pengungkapan dengan trik Avario

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    static NSString *CellIdentifier = @"PersonGroupHeader";
    UITableViewCell *headerView = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(headerView == nil)
    {
        [NSException raise:@"headerView == nil, PersonGroupTableViewController" format:[NSString stringWithFormat:@"Storyboard does not have prototype cell with identifier %@",CellIdentifier]];
    }

    //  https://stackoverflow.com/a/24044628/3075839
    while(headerView.contentView.gestureRecognizers.count)
    {
        [headerView.contentView removeGestureRecognizer:[headerView.contentView.gestureRecognizers objectAtIndex:0]];
    }


    ButtonWithArgument *button = (ButtonWithArgument *)[headerView viewWithTag:4];
    button.frame = headerView.bounds; // set tap area to entire header view
    button.argument = [[NSNumber alloc] initWithInteger:section]; // from ButtonWithArguments subclass
    [button addTarget:self action:@selector(headerViewTap:) forControlEvents:UIControlEventTouchUpInside];

    // https://stackoverflow.com/a/20821178/3075839
    UITableViewCell *disclosure = [[UITableViewCell alloc] init];
    disclosure.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    disclosure.userInteractionEnabled = NO;
    disclosure.frame = CGRectMake(button.bounds.origin.x + button.bounds.size.width - 20 - 5, // disclosure 20 px wide, right margin 5 px
          (button.bounds.size.height - 20) / 2,
          20,
          20);
    [button addSubview:disclosure];

    // configure header title text

    return headerView.contentView;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 35.0f;
}

-(void) headerViewTap:(UIGestureRecognizer *)gestureRecognizer;
{
    NSLog(@"header tap");
    NSInteger section = ((NSNumber *)sender.argument).integerValue;
    // do something here
}

ButtonWithArgument.h

#import <UIKit/UIKit.h>

@interface ButtonWithArgument : UIButton
@property (nonatomic, strong) NSObject *argument;
@end

ButtonWithArgument.m

#import "ButtonWithArgument.h"
@implementation ButtonWithArgument
@end
MarkF
sumber
1

Anda harus menggunakan solusi Tieme sebagai basis tetapi lupakan tentang viewWithTag:pendekatan mencurigakan dan lainnya, alih-alih mencoba memuat ulang tajuk Anda (dengan memuat ulang bagian itu).

Jadi setelah Anda mengatur tampilan tajuk sel kustom Anda dengan semua AutoLayoutbarang mewah , cukup dequeue dan kembalikan contentView setelah pengaturan Anda, seperti:

-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
 static NSString *CellIdentifier = @"SectionHeader"; 

    SettingsTableViewCell *sectionHeaderCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    sectionHeaderCell.myPrettyLabel.text = @"Greetings";
    sectionHeaderCell.contentView.backgroundColor = [UIColor whiteColor]; // don't leave this transparent

    return sectionHeaderCell.contentView;
}  
Laszlo
sumber
1

Bagaimana dengan solusi di mana header didasarkan pada view array:

class myViewController: UIViewController {
    var header: [UILabel] = myStringArray.map { (thisTitle: String) -> UILabel in
        let headerView = UILabel()
            headerView.text = thisTitle
    return(headerView)
}

Selanjutnya dalam delegasi:

extension myViewController: UITableViewDelegate {
    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return(header[section])
    }
}
CyrIng
sumber
1
  1. Tambahkan sel StoryBoard, dan aturreuseidentified

    sb

  2. Kode

    class TP_TaskViewTableViewSectionHeader: UITableViewCell{
    }
    

    dan

    tautan

  3. Menggunakan:

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let header = tableView.dequeueReusableCell(withIdentifier: "header", for: IndexPath.init(row: 0, section: section))
        return header
    }
    
leo.tan
sumber
2
Ini bukan cara yang tepat untuk menambahkan Header
Manish Malviya
2
lalu bagaimana caranya?
Pramod Shukla
1

Mirip dengan jawaban laszlo tetapi Anda dapat menggunakan kembali sel prototipe yang sama untuk sel tabel dan sel header bagian. Tambahkan dua fungsi pertama di bawah ini ke subClass UIViewController Anda

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell") as! DataCell
    cell.data1Label.text = "DATA KEY"
    cell.data2Label.text = "DATA VALUE"
    return cell
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 75
}

// Example of regular data cell dataDelegate to round out the example
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell", for: indexPath) as! PlayerCell

    cell.data1Label.text = "\(dataList[indexPath.row].key)"
    cell.data2Label.text = "\(dataList[indexPath.row].value)"
    return cell
}
Richard Legault
sumber
0

Inilah jawaban @Vitaliy Gozhenko , di Swift.
Untuk meringkas Anda akan membuat UITableViewHeaderFooterView yang berisi UITableViewCell. UITableViewCell ini akan menjadi "dequeuable" dan Anda dapat mendesainnya di storyboard Anda.

  1. Buat kelas UITableViewHeaderFooterView

    class CustomHeaderFooterView: UITableViewHeaderFooterView {
    var cell : UITableViewCell? {
        willSet {
            cell?.removeFromSuperview()
        }
        didSet {
            if let cell = cell {
                cell.frame = self.bounds
                cell.autoresizingMask = [UIViewAutoresizing.FlexibleHeight, UIViewAutoresizing.FlexibleWidth]
                self.contentView.backgroundColor = UIColor .clearColor()
                self.contentView .addSubview(cell)
            }
        }
    }
    
  2. Hubungkan tampilan tabel Anda dengan kelas ini di fungsi viewDidLoad Anda:

    self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID")
    
  3. Saat menanyakan, untuk header bagian, keluarkan CustomHeaderFooterView, dan masukkan sel ke dalamnya

    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let view = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier("SECTION_ID") as! CustomHeaderFooterView
        if view.cell == nil {
            let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell")
            view.cell = cell;
        }
    
        // Fill the cell with data here
    
        return view;
    }
    
CedricSoubrie
sumber