Cách triển khai Tiêu đề và chân trang của phần xem bảng tùy chỉnh với Storyboard


176

Không cần sử dụng bảng phân cảnh, chúng ta có thể chỉ cần kéo một UIViewkhung vẽ lên, đặt nó ra và sau đó đặt nó trong các phương thức tableView:viewForHeaderInSectionhoặc tableView:viewForFooterInSectionủy nhiệm.

Làm cách nào để chúng tôi thực hiện điều này với StoryBoard nơi chúng tôi không thể kéo UIView lên khung vẽ

Câu trả lời:


90

Tôi biết câu hỏi này là dành cho iOS 5, nhưng vì lợi ích của độc giả trong tương lai, lưu ý rằng iOS 6 hiệu quả bây giờ chúng ta có thể sử dụng dequeueReusableHeaderFooterViewWithIdentifierthay vì dequeueReusableCellWithIdentifier.

Vì vậy viewDidLoad, gọi, registerNib:forHeaderFooterViewReuseIdentifier:hoặc registerClass:forHeaderFooterViewReuseIdentifier:. Sau đó vào viewForHeaderInSection, gọi tableView:dequeueReusableHeaderFooterViewWithIdentifier:. Bạn không sử dụng nguyên mẫu ô với API này (đó là chế độ xem dựa trên NIB hoặc chế độ xem được tạo theo chương trình), nhưng đây là API mới dành cho các tiêu đề và chân trang bị loại bỏ.


13
thở dài Thật xấu hổ khi hai câu trả lời rõ ràng này về việc sử dụng NIB thay vì một tế bào proto hiện đang có dưới 5 phiếu và câu trả lời "hack nó với các tế bào proto" đã tăng lên trên 200.
Stewohn

5
Sự khác biệt là với các xe cho thuê từ Tieme bạn có thể làm cho thiết kế của bạn trong kịch bản giống nhau và không sử dụng một xib riêng
CedricSoubrie

Bạn có thể bằng cách nào đó tránh sử dụng tệp NIB riêng biệt và sử dụng bảng phân cảnh thay thế?
Foriger

@Foriger - Không phải lúc này. Đó là một thiếu sót kỳ lạ trong lượt xem bảng phân cảnh, nhưng đó là những gì nó được. Sử dụng hack kydgy đó với các ô nguyên mẫu nếu bạn muốn, nhưng cá nhân tôi, tôi chỉ đưa ra sự khó chịu của NIB cho các chế độ xem tiêu đề / chân trang.
Rob

2
Chỉ nói rằng nó "cũ" là không đủ mạnh, IMHO. Câu trả lời được chấp nhận là một cách ít ỏi để giải quyết một thực tế rằng không phải bạn không thể có các ô nguyên mẫu cho các chế độ xem đầu trang và chân trang. Nhưng tôi nghĩ điều đó đơn giản là sai, bởi vì nó cho rằng việc loại bỏ các tiêu đề và chân trang hoạt động tương tự như việc loại bỏ các ô (mà sự hiện diện của API mới hơn cho các tiêu đề / chân trang cho thấy có thể không phải là trường hợp). Câu trả lời được chấp nhận là một cách giải quyết sáng tạo có thể hiểu được trong iOS 5, nhưng, trong iOS 6 trở lên, tốt nhất nên sử dụng API được thiết kế rõ ràng để sử dụng lại tiêu đề / chân trang.
Rob

384

Chỉ cần sử dụng một ô nguyên mẫu làm tiêu đề và / hoặc chân trang của bạn.

  • thêm một ô phụ và đặt các yếu tố mong muốn của bạn vào đó.
  • đặt mã định danh thành một cái gì đó cụ thể (trong trường hợp của tôi là Mục lục)
  • thực hiện tableView:viewForHeaderInSection:phương pháp hoặc tableView:viewForFooterInSection:phương pháp
  • sử dụng [tableView dequeueReusableCellWithIdentifier:]để có được tiêu đề
  • thực hiện tableView:heightForHeaderInSection:phương pháp.

(xem screenhot)

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

Chỉnh sửa: Cách thay đổi tiêu đề (câu hỏi đã nhận xét):

  1. Thêm nhãn vào ô tiêu đề
  2. đặt thẻ của nhãn thành một số cụ thể (ví dụ 123)
  3. Trong tableView:viewForHeaderInSection:phương pháp của bạn có được nhãn bằng cách gọi:
    UILabel *label = (UILabel *)[headerView viewWithTag:123]; 
  1. Bây giờ bạn có thể sử dụng nhãn để đặt tiêu đề mới:
    [label setText:@"New Title"];

7
Đến bữa tiệc muộn, nhưng để vô hiệu hóa các nhấp chuột trên chế độ xem tiêu đề của phần, chỉ cần trả về cell.contentView trong viewForHeaderInSection phương thức (không cần thêm bất kỳ UIView tùy chỉnh nào).
paulvs

3
@PaulVon Tôi đã cố gắng thực hiện điều đó trong dự án mới nhất của mình, nhưng nếu bạn làm điều đó chỉ cần cố gắng nhấn vào một trong các tiêu đề của bạn và nó sẽ sụp đổ
Hons

13
Trong iOS 6.0, dequeueReusableHeaderFooterViewWithIdentifierđược giới thiệu và hiện được ưa thích hơn câu trả lời này. Nhưng sử dụng chính xác bây giờ mất nhiều bước hơn. Tôi có hướng dẫn @ samwize.com/2015/11/06/ .
lấy mẫu

3
Không tạo phầnHeadViews của bạn bằng UITableViewCells, nó sẽ tạo ra hành vi không mong muốn. Sử dụng một UIView thay thế.
Đánh giá cao vào

4
Vui lòng không bao giờ sử dụng UITableViewCelllàm chế độ xem tiêu đề. Bạn sẽ rất khó để gỡ lỗi các trục trặc hình ảnh - tiêu đề đôi khi sẽ biến mất vì cách các tế bào bị mất hiệu lực và bạn sẽ tìm kiếm hàng giờ tại sao điều đó cho đến khi bạn nhận ra UITableViewCellkhông thuộc về UITableViewtiêu đề.
raven_raven

53

Trong iOS 6.0 trở lên, mọi thứ đã thay đổi với dequeueReusableHeaderFooterViewWithIdentifierAPI mới .

Tôi đã viết một hướng dẫn (đã thử nghiệm trên iOS 9), có thể tóm tắt như sau:

  1. Phân lớp UITableViewHeaderFooterView
  2. Tạo Nib với chế độ xem lớp con và thêm 1 chế độ xem chứa tất cả các chế độ xem khác trong đầu trang / chân trang
  3. Đăng ký Nib trong viewDidLoad
  4. Triển khai viewForHeaderInSectionvà sử dụng dequeueReusableHeaderFooterViewWithIdentifierđể lấy lại tiêu đề / chân trang

2
Cảm ơn bạn vì câu trả lời này KHÔNG phải là hack, và cách làm đúng đắn. Ngoài ra, cảm ơn vì đã giới thiệu tôi với UITableViewHeaderFooterVIew. Btw, hướng dẫn chỉ là tuyệt vời! :)
Roboris

Chào mừng bạn Việc triển khai tiêu đề / chân trang cho chế độ xem bảng hoặc bộ sưu tập không được tài liệu tốt cho lắm. Mới hôm qua tôi đã bị kẹt trong tiêu đề getter để hiển thị khi thiết kế qua Storyboad .
lấy mẫu

1
Đây phải là câu trả lời đánh giá hàng đầu cho chắc chắn!
Ben Williams

Bạn có thể giải thích làm thế nào để làm như vậy thông qua bảng phân cảnh, thay vì xib? Khi làm như vậy, tôi thành công dequeue, nhưng UILabels của tôi là null.
bunkerdive

22

Tôi đã làm cho nó hoạt động trong iOS7 bằng cách sử dụng một tế bào nguyên mẫu trong bảng phân cảnh. Tôi có một nút trong chế độ xem tiêu đề phần tùy chỉnh của mình để kích hoạt một segue được thiết lập trong bảng phân cảnh.

Bắt đầu với giải pháp của Tieme

Như pedro.m chỉ ra, vấn đề với điều này là việc nhấn vào tiêu đề của phần khiến cho ô đầu tiên trong phần được chọn.

Như Paul Von chỉ ra, điều này được khắc phục bằng cách trả về nội dung của ô thay vì toàn bộ ô.

Tuy nhiên, như Hons chỉ ra, một cú nhấn dài vào phần tiêu đề đã nói sẽ làm sập ứng dụng.

Giải pháp là xóa mọi cử chỉ nhận dạng khỏi 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; }

Nếu bạn không sử dụng cử chỉ trong chế độ xem tiêu đề phần của mình, thì vụ hack nhỏ này dường như đã hoàn thành.


2
Bạn đúng. Tôi mới thử nghiệm nó và nó hoạt động, vì vậy tôi đã cập nhật bài viết của mình ( hons82.blogspot.it/2014/05/uitableviewheader-done-right.html ) chứa tất cả các giải pháp có thể tôi tìm thấy trong quá trình nghiên cứu. Thx rất nhiều cho sửa chữa này
Hons

Câu trả lời hay ... Cảm ơn người đàn ông
Jogendra.Com

13

Nếu bạn sử dụng bảng phân cảnh, bạn có thể sử dụng một ô nguyên mẫu trong chế độ xem bảng để bố trí chế độ xem tiêu đề của mình. Đặt một id duy nhất và viewForHeaderInSection, bạn có thể loại bỏ ô có ID đó và chuyển nó thành UIView.


12

Nếu bạn cần Thực hiện Swift việc này, hãy làm theo các hướng dẫn về câu trả lời được chấp nhận và sau đó, trong UITableViewCont kiểm soát bạn thực hiện các phương pháp sau:

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
}

2
Vì một số lý do, nó không hoạt động với tôi cho đến khi tôi ghi đè lên heightForHeaderInSection.
Entalpi

Đây là mã khá cũ. bạn nên đăng câu trả lời khác!
JZ.

Tôi đã bị kẹt vì tôi không biết tôi phải ghi đè heightForHeaderInSection. Cảm ơn @Entalpi
guijob

1
@JZ. Trong ví dụ về Swift 3 của bạn, bạn sử dụng self.tableView.dequeueReabilitiesHeaderFooterView và Swift 2: self.tableView.dequeueReabilitiesCellWithIdentifier sau đây có khác nhau không?
Vrutin Rathod

1
Điều này đã cứu mông tôi! thêm một chiều cao cho headerCell làm cho nó hiển thị.
CRey

9

Giải pháp tôi đưa ra về cơ bản là cùng một giải pháp được sử dụng trước khi giới thiệu bảng phân cảnh.

Tạo một tệp lớp giao diện mới, trống. Kéo một UIView vào khung vẽ, bố trí theo ý muốn.

Tải ngòi thủ công, gán cho phần đầu trang / chân trang thích hợp trong các phương thức ủy nhiệm của viewForHeaderInSection hoặc viewForFooterInSection.

Tôi đã hy vọng rằng Apple đã đơn giản hóa kịch bản này bằng các bảng phân cảnh và tiếp tục tìm kiếm một giải pháp tốt hơn hoặc đơn giản hơn. Ví dụ, các tiêu đề và chân trang của bảng tùy chỉnh được chuyển thẳng tới.


Vâng, apple đã làm nếu bạn sử dụng ô bảng phân cảnh làm phương thức tiêu đề :) stackoverflow.com/questions/9219234/#11396643
Tieme

2
Ngoại trừ việc chỉ hoạt động cho các ô nguyên mẫu, không phải các ô tĩnh.
Jean-Denis Muys

1
Một giải pháp thực sự dễ dàng là sử dụng Pixate ; bạn có thể thực hiện khá nhiều tùy chỉnh mà không cần tham chiếu đến tiêu đề. Tất cả bạn phải thực hiện là tableView:titleForHeaderInSection, đó là một lót.
John Starr Dewar

5
Đó là giải pháp thực sự ... tiếc là nó không ở trên đầu, vì vậy tôi phải mất một thời gian để tìm ra nó. Tôi đã tóm tắt các vấn đề tôi gặp phải hons82.blogspot.it/2014/05/uitableviewheader-done-right.html
Hons

Nó dễ hơn thế, chỉ cần kéo và thả.
Ricardo

5

Khi bạn trả về nội dung của ô, bạn sẽ gặp 2 vấn đề:

  1. tai nạn liên quan đến cử chỉ
  2. bạn không sử dụng lại nội dung Xem (mỗi lần viewForHeaderInSectiongọi, bạn tạo ô mới)

Giải pháp:

Lớp Wrapper cho tiêu đề bảng \ chân trang. Nó chỉ là vật chứa, được thừa hưởng từ UITableViewHeaderFooterView, chứa tế bào bên trong

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

Đăng ký lớp trong UITableView của bạn (ví dụ: trong viewDidLoad)

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

Trong UITableViewDelegate của bạn:

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

2

Tôi thường làm như sau để tạo chế độ xem tiêu đề / chân trang một cách lười biếng:

  • Thêm trình điều khiển dạng xem tự do cho phần đầu trang / chân trang vào bảng phân cảnh
  • Xử lý tất cả mọi thứ cho tiêu đề trong trình điều khiển xem
  • Trong bộ điều khiển xem bảng cung cấp một loạt các bộ điều khiển xem có thể thay đổi cho các tiêu đề / chân trang của phần được lặp lại với [NSNull null]
  • Trong viewForHeaderInSection / viewForFooterInSection nếu trình điều khiển khung nhìn chưa tồn tại, hãy tạo nó bằng bảng phân cảnh tức thờiViewControllWithIdentifier, ghi nhớ nó trong mảng và trả về khung nhìn của trình điều khiển xem

2

Tôi đã gặp rắc rối trong một kịch bản mà Header không bao giờ được sử dụng lại ngay cả khi thực hiện tất cả các bước thích hợp.

Vì vậy, như một lưu ý cho tất cả những ai muốn đạt được tình huống hiển thị các phần trống (0 hàng), hãy cảnh báo rằng:

dequeueReabilitiesHeaderFooterViewWithIdentifier sẽ không sử dụng lại tiêu đề cho đến khi bạn trả lại ít nhất một hàng

Hy vọng nó giúp


1

Để theo dõi đề xuất của Damon , đây là cách tôi thực hiện tiêu đề có thể chọn giống như một hàng bình thường với chỉ báo tiết lộ.

Tôi đã thêm một nút được phân lớp từ UIButton (tên lớp con "ButtonWithArgument") vào ô nguyên mẫu của người đứng đầu và xóa văn bản tiêu đề (văn bản "Tiêu đề" in đậm là một UILabel khác trong ô nguyên mẫu)

Nút trong Trình tạo giao diện

sau đó đặt Nút thành toàn bộ chế độ xem tiêu đề và thêm chỉ báo tiết lộ với thủ thuật của 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
}

NútWithArgument.h

#import <UIKit/UIKit.h>

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

NútWithArgument.m

#import "ButtonWithArgument.h"
@implementation ButtonWithArgument
@end

1

Bạn nên sử dụng giải pháp của Tieme làm cơ sở nhưng quên đi viewWithTag:các phương pháp tiếp cận cá và khác, thay vào đó hãy thử tải lại tiêu đề của bạn (bằng cách tải lại phần đó).

Vì vậy, sau khi bạn ngồi lên xem tiêu đề ô tùy chỉnh của bạn với tất cả các ưa thích AutoLayout nội dung , chỉ cần loại bỏ nó và trả lại nội dung Xem sau khi thiết lập của bạn, như:

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

1

Điều gì về một giải pháp trong đó tiêu đề dựa trên một mảng xem:

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

Tiếp theo trong đại biểu:

extension myViewController: UITableViewDelegate {
    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return(header[section])
    }
}

1
  1. Thêm ô vào StoryBoardvà đặtreuseidentified

    sb

  2. class TP_TaskViewTableViewSectionHeader: UITableViewCell{
    }
    

    liên kết

  3. Sử dụng:

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let header = tableView.dequeueReusableCell(withIdentifier: "header", for: IndexPath.init(row: 0, section: section))
        return header
    }
    

2
Đây không phải là cách thích hợp để thêm Tiêu đề
Manish Malviya

2
Vậy thì sao?
Pramod Shukla

1

Tương tự như câu trả lời laszlo nhưng bạn có thể sử dụng lại cùng một ô nguyên mẫu cho cả các ô của bảng và ô tiêu đề của phần. Thêm hai chức năng đầu tiên bên dưới vào lớp con UIViewControll của bạn

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
}

0

Đây là câu trả lời của @Vitaliy Gozhenko , trong Swift.
Để tóm tắt, bạn sẽ tạo một UITableViewHeaderFooterView có chứa UITableViewCell. UITableViewCell này sẽ "có thể điều chỉnh được" và bạn có thể thiết kế nó trong bảng phân cảnh của mình.

  1. Tạo một lớp 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. Cắm bảng xem của bạn với lớp này trong chức năng viewDidLoad của bạn:

    self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID")
    
  3. Khi yêu cầu, đối với tiêu đề phần, hãy loại bỏ CustomHeaderFooterView và chèn một ô vào đó

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