스토리 보드를 사용하여 사용자 정의 테이블 뷰 섹션 머리글 및 바닥 글을 구현하는 방법


176

스토리 보드를 사용하지 않고 간단히 UIView캔버스에 A 를 끌어다 놓고 레이아웃을 지정 tableView:viewForHeaderInSection하거나 tableView:viewForFooterInSectionoror 메소드 를 설정할 수 있습니다.

UIView를 캔버스로 드래그 할 수없는 StoryBoard로 어떻게 이것을 수행합니까?

답변:


91

이 질문은 iOS 5에 대한 것이지만 향후 독자를 위해 효과적인 iOS 6 dequeueReusableHeaderFooterViewWithIdentifier대신에 사용할 수 있습니다 dequeueReusableCellWithIdentifier.

에서 또는 viewDidLoad중 하나를 호출하십시오 . 그런 다음에 전화하십시오 . 이 API에는 셀 프로토 타입을 사용하지 않지만 (NIB 기반보기 또는 프로그래밍 방식으로 작성된보기), 이는 대기열에서 제외 된 머리글 및 바닥 글을위한 새로운 API입니다.registerNib:forHeaderFooterViewReuseIdentifier:registerClass:forHeaderFooterViewReuseIdentifier:viewForHeaderInSectiontableView:dequeueReusableHeaderFooterViewWithIdentifier:


13
한숨 프로토 세포 대신 NIB를 사용하는 것에 대한이 두 가지 정답이 현재 5 표 미만이며 "프로토 셀로 해킹"답변이 200 이상
이라는 수치는 부끄러운

5
차이는 Tieme에서 해킹과 같은 스토리 보드에 디자인을 만들 수 있다는 것입니다 별도의 XIB 사용하지
CedricSoubrie

어떻게 든 별도의 NIB 파일을 사용하지 않고 스토리 보드를 대신 사용할 수 있습니까?
Foriger

@Foriger-지금은 아닙니다. 스토리 보드 테이블 뷰에서 홀수 생략이지만 그것이 무엇입니까. 원하는 경우 프로토 타입 셀과 함께 그 해킹을 사용하십시오. 그러나 개인적으로, 나는 머리글 / 바닥 글보기를 위해 NIB의 성가심을 참았습니다.
Rob

2
IMHO는 그것이 "오래되었다"고 말하는 것만으로는 충분하지 않다. 허용되는 답변은 머리글 및 바닥 글보기를위한 프로토 타입 셀을 가질 수 없다는 사실을 피하기위한 복잡한 방법입니다. 그러나 헤더와 바닥 글을 대기열에서 제외하는 것은 셀을 대기열에서 제외하는 것과 동일하게 작동한다고 가정하기 때문에 단순히 잘못되었다고 생각합니다 (헤더 / 바닥 글에 대한 최신 API가 존재하지 않을 수도 있음). 허용되는 답변은 iOS 5에서 이해할 수 있었던 창의적인 해결 방법이지만 iOS 6 이상에서는 머리글 / 바닥 글 재사용을 위해 명시 적으로 설계된 API를 사용하는 것이 가장 좋습니다.
Rob

384

프로토 타입 셀을 섹션 머리글 및 / 또는 바닥 글로 사용하십시오.

  • 추가 셀을 추가하고 원하는 요소를 넣습니다.
  • 식별자를 특정 것으로 설정하십시오 (제 경우에는 SectionHeader)
  • 구현하는 tableView:viewForHeaderInSection:방법 또는 tableView:viewForFooterInSection:방법을
  • [tableView dequeueReusableCellWithIdentifier:]헤더를 얻는 데 사용
  • tableView:heightForHeaderInSection:방법을 구현하십시오 .

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

편집 : 헤더 제목을 변경하는 방법 (주석 질문) :

  1. 머리글 셀에 레이블 추가
  2. 라벨의 태그를 특정 번호로 설정하십시오 (예 : 123)
  3. 귀하의 tableView:viewForHeaderInSection:방법 에서 다음 을 호출하여 레이블을 가져옵니다.
    UILabel *label = (UILabel *)[headerView viewWithTag:123]; 
  1. 이제 라벨을 사용하여 새 제목을 설정할 수 있습니다.
    [label setText:@"New Title"];

7
파티에 늦었지만 섹션 헤더보기에서 클릭을 비활성화하려면 viewForHeaderInSection 메소드에 cell.contentView를 반환 하십시오 (사용자 정의 UIView 를 추가 할 필요가 없음).
paulvs

3
@PaulVon 나는 내 최신 프로젝트에서 그렇게하려고 시도했지만 그렇게하면 헤더 중 하나를 길게 누르면 충돌이 발생합니다
Hons

13
iOS 6.0에서 dequeueReusableHeaderFooterViewWithIdentifier도입되었으며 이제이 답변보다 선호됩니다. 그러나 올바르게 사용하면 더 많은 단계가 필요합니다. @ samwize.com/2015/11/06/… 안내서가 있습니다 .
samwize

3
UITableViewCells를 사용하여 sectionHeaderView를 작성하지 마십시오. 예기치 않은 동작이 발생합니다. 대신 UIView를 사용하십시오.
AppreciateIt

4
UITableViewCell헤더보기로 사용하지 마십시오 . 시각적 결함을 디버깅하는 것은 매우 어려울 것입니다. 셀이 대기열에서 분리되는 방식으로 인해 헤더가 때때로 사라지고 헤더에 UITableViewCell속하지 않을 때까지 왜 그런지 몇 시간 동안 찾고 있습니다 UITableView.
raven_raven

53

iOS 6.0 이상에서는 새로운 dequeueReusableHeaderFooterViewWithIdentifierAPI로 상황이 변경되었습니다 .

다음과 같이 요약 할 수 있는 가이드 (iOS 9에서 테스트 됨)를 작성했습니다 .

  1. 아강 UITableViewHeaderFooterView
  2. 서브 클래스 뷰로 펜촉을 생성하고 머리글 / 바닥 글에 다른 모든 뷰를 포함하는 컨테이너 뷰 1 개를 추가합니다.
  3. 펜촉을 등록하십시오 viewDidLoad
  4. 머리글 / 바닥 글을 다시 가져 오기 위해 구현 viewForHeaderInSection및 사용dequeueReusableHeaderFooterViewWithIdentifier

2
해킹이 아니며이 작업을 수행하는 올바른 방법이 아닙니다. 또한 UITableViewHeaderFooterVIew를 소개해 주셔서 감사합니다. Btw, 가이드는 훌륭합니다! :)
Roboris

어서 오십시오. 테이블 또는 컬렉션 뷰에 대한 머리글 / 바닥 글 구현은 Apple에서 잘 설명하지 않았습니다. 어제 storyboad 통해 디자인 할 때 getter 헤더에 표시되었습니다 .
samwize

1
이것은 최고 등급의 답변이어야합니다!
벤 윌리엄스

xib 대신 스토리 보드를 통해 그렇게하는 방법을 설명해 주시겠습니까? 그렇게 할 때 성공적으로 대기열에서 제외하지만 UILabels는 null입니다.
bunkerdive 2016 년

22

스토리 보드의 프로토 타입 셀을 사용하여 iOS7에서 작동하게했습니다. 스토리 보드에 설정된 segue를 트리거하는 사용자 정의 섹션 헤더보기에 버튼이 있습니다.

Tieme의 솔루션으로 시작

pedro.m이 지적했듯이, 이것의 문제점은 섹션 헤더를 두드리면 섹션의 첫 번째 셀이 선택된다는 것입니다.

Paul Von이 지적했듯이 이것은 전체 셀 대신 셀의 contentView를 반환하여 수정됩니다.

그러나 Hons가 지적했듯이 해당 섹션 헤더를 길게 누르면 앱이 중단됩니다.

해결 방법은 contentView에서 gestureRecognizer를 제거하는 것입니다.

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

섹션 헤더보기에서 제스처를 사용하지 않는 경우이 작은 해킹이 완료된 것 같습니다.


2
네가 옳아. 방금 테스트와 그 작동을 테스트 했으므로 연구 중에 찾은 모든 가능한 솔루션을 포함하는 내 게시물 ( hons82.blogspot.it/2014/05/uitableviewheader-done-right.html )을 업데이트했습니다 . 이 수정에 대한 들으을 많이합니다
우등

좋은 답변 ... 고마워
Jogendra.Com

13

스토리 보드를 사용하는 경우 테이블보기에서 프로토 타입 셀을 사용하여 헤더보기를 레이아웃 할 수 있습니다. 고유 ID와 viewForHeaderInSection을 설정하면 해당 ID를 가진 셀을 대기열에서 빼고 UIView로 캐스트 할 수 있습니다.


12

Swift 구현이 필요한 경우 허용 된 답변의 지침을 따르고 UITableViewController에서 다음 메소드를 구현하십시오.

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
어떤 이유로 든 heightForHeaderInSection을 재정의 할 때까지 그것은 나를 위해 작동하지 않았습니다.
Entalpi

이것은 꽤 오래된 코드입니다. 다른 답변을 게시해야합니다!
JZ.

heightForHeaderInSection을 재정의해야한다는 것을 몰랐기 때문에 멈췄습니다. 감사합니다 @Entalpi
guijob

1
@JZ. Swift 3 예제에서는 다음 self.tableView.dequeueReusableHeaderFooterView 및 Swift 2를 사용하여 deque를 사용합니다. self.tableView.dequeueReusableCellWithIdentifier 차이점이 있습니까?
Vrutin Rathod

1
이것은 내 엉덩이를 구했다! headerCell에 높이를 추가하면 표시됩니다.
CRey

9

내가 생각해 낸 해결책은 기본적으로 스토리 보드를 도입하기 전에 사용한 것과 동일한 해결책입니다.

비어있는 새 인터페이스 클래스 파일을 작성하십시오. UIView를 캔버스로 드래그하여 원하는대로 레이아웃합니다.

펜촉을 수동으로로드하고 viewForHeaderInSection 또는 viewForFooterInSection 대리자 메소드의 적절한 머리글 / 바닥 글 섹션에 할당하십시오.

애플이 스토리 보드로이 시나리오를 단순화하고 더 좋고 더 간단한 솔루션을 계속 찾고 있었으면 좋겠다. 예를 들어 사용자 정의 테이블 머리글과 바닥 글은 추가하기가 쉽습니다.


스토리 보드 셀을 헤더 방법으로 사용하는 경우 잘 애플 :) stackoverflow.com/questions/9219234/#11396643
Tieme

2
단, 정적 셀이 아닌 프로토 타입 셀에서만 작동합니다.
Jean-Denis Muys

1
정말 쉬운 해결책 중 하나는 Pixate 를 사용하는 것입니다 . 헤더를 참조하지 않고도 많은 사용자 정의를 수행 할 수 있습니다. 구현 해야하는 tableView:titleForHeaderInSection것은 하나의 라이너입니다.
John Starr Dewar

5
그게 진짜 해결책입니다 ... 불행히도 그 위에는 없었기 때문에 그것을 찾는 데 시간이 걸렸습니다. 나는 hons82.blogspot.it/2014/05/uitableviewheader-done-right.html
Hons

드래그 앤 드롭 만하면됩니다.
Ricardo

5

셀의 contentView를 반환하면 두 가지 문제가 있습니다.

  1. 제스처 관련 충돌
  2. contentView를 재사용하지 않습니다 ( viewForHeaderInSection전화 할 때마다 새 셀을 작성 함)

해결책:

테이블 머리글 \ 바닥 글의 래퍼 클래스입니다. UITableViewHeaderFooterView내부에서 셀을 보유하는 컨테이너입니다.

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

UITableView에 클래스를 등록하십시오 (예 : viewDidLoad에).

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

UITableViewDelegate에서 :

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

나는 머리글 / 바닥 글보기를 느리게 만들기 위해 다음을 수행했습니다.

  • 스토리 보드에 섹션 머리글 / 바닥 글에 대한 자유 형식 뷰 컨트롤러 추가
  • 뷰 컨트롤러에서 헤더에 대한 모든 내용 처리
  • 테이블 뷰 컨트롤러에서 다시 채워지는 섹션 머리글 / 바닥 글에 대해 가변 뷰 컨트롤러 배열을 제공하십시오. [NSNull null]
  • 뷰 컨트롤러가 존재하지 않으면 viewForHeaderInSection / viewForFooterInSection에서 스토리 보드를 사용하여 그것을 작성하십시오.

2

헤더가 모든 적절한 단계를 수행해도 재사용되지 않는 시나리오에서 어려움을 겪었습니다.

따라서 빈 섹션 표시 (0 행) 상황을 달성하려는 모든 사람에게 유용한 팁은 다음과 같이 경고합니다.

dequeueReusableHeaderFooterViewWithIdentifier는 하나 이상의 행을 반환 할 때까지 헤더를 재사용하지 않습니다.

그것이 도움이되기를 바랍니다.


1

Damon의 제안 에 대한 후속 조치 는 공개 표시 기가있는 일반 행처럼 헤더를 선택 가능하게 만드는 방법입니다.

UIButton (하위 클래스 이름 "ButtonWithArgument")에서 하위 클래스로 분류 된 Button을 헤더의 프로토 타입 셀에 추가하고 제목 텍스트를 삭제했습니다 (굵은 "Title"텍스트는 프로토 타입 셀의 또 다른 UILabel입니다)

인터페이스 빌더의 버튼

그런 다음 Button을 전체 헤더보기로 설정하고 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

1

Tieme의 솔루션 을 기본으로 사용해야 하지만 viewWithTag:다른 접근 방법을 잊어 버리고 대신 해당 섹션을 다시로드하여 헤더를 다시로드하십시오.

따라서 모든 멋진 AutoLayout것들 과 함께 사용자 정의 셀 헤더보기를 작성한 후에는 대기열에서 빼고 설정 후 다음과 같이 contentView를 반환하십시오.

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

헤더가 뷰 배열을 기반으로하는 솔루션은 어떻습니까?

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

대리인의 다음 :

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

1
  1. 에 셀 추가 StoryBoard및 설정reuseidentified

    SB

  2. 암호

    class TP_TaskViewTableViewSectionHeader: UITableViewCell{
    }
    

    링크

  3. 사용하다:

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

2
이것은 헤더를 추가하는 올바른 방법이 아닙니다
Manish Malviya

2
그럼 방법은 무엇입니까?
Pramod Shukla

1

laszlo answer와 유사하지만 테이블 셀과 섹션 헤더 셀 모두에 동일한 프로토 타입 셀을 재사용 할 수 있습니다. 아래의 첫 두 함수를 UIViewController 서브 클래스에 추가하십시오.

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

다음은 @Vitaliy Gozhenko의 대답은 스위프트에.
요약하면 UITableViewCell이 포함 된 UITableViewHeaderFooterView를 작성합니다. 이 UITableViewCell은 "dequeuable"이며 스토리 보드에서 디자인 할 수 있습니다.

  1. 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. viewDidLoad 함수에서이 클래스로 테이블 뷰를 연결하십시오.

    self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID")
    
  3. 섹션 헤더를 요청할 때 CustomHeaderFooterView를 큐에 넣고 셀을 삽입하십시오.

    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;
    }
    
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.