스토리 보드에서 여러 컨트롤러와 함께 사용할 사용자 정의 셀을 만들려면 어떻게해야합니까?


216

작업중 인 앱에서 스토리 보드를 사용하려고합니다. 응용 프로그램에는 목록사용자가 있으며 각각에는 다른 컬렉션 (목록의 구성원, 사용자가 소유 한 목록)이 포함되어 있습니다. 따라서 수업 ListCellUserCell수업이 있습니다. 목표는 앱 전체에서 (즉, 내 테이블 뷰 컨트롤러에서) 재사용 할 수 있도록하는 것입니다.

그것이 내가 문제에 처한 곳입니다.

스토리 보드에서 모든 뷰 컨트롤러에서 재사용 할 수있는 사용자 정의 테이블 뷰 셀을 어떻게 만듭니 까?

지금까지 시도한 특정 사항은 다음과 같습니다.

  • Controller # 1에서 프로토 타입 셀을 추가하고 클래스를 내 UITableViewCell서브 클래스로 설정하고 재사용 ID를 설정하고 레이블을 추가 한 후 클래스의 콘센트에 연결했습니다. Controller # 2에서 빈 프로토 타입 셀을 추가하고 동일한 클래스로 설정 한 후 이전과 같이 id를 재사용하십시오. 셀이 컨트롤러 # 2에 표시 될 때 레이블이 표시되지 않습니다. 컨트롤러 # 1에서 잘 작동합니다.

  • 각 셀 유형을 다른 NIB로 설계하고 적절한 셀 클래스에 연결했습니다. 스토리 보드에서 빈 프로토 타입 셀을 추가하고 클래스를 설정하고 내 셀 클래스를 참조하도록 ID를 재사용하십시오. 컨트롤러의 viewDidLoad메소드에서 재사용 ID에 해당 NIB 파일을 등록하십시오. 표시되면 두 컨트롤러의 셀이 프로토 타입처럼 비어있었습니다.

  • 두 컨트롤러의 프로토 타입을 비우고 클래스를 설정하고 ID를 내 셀 클래스로 재사용하십시오. 코드로 셀의 UI를 완전히 구성했습니다. 셀은 모든 컨트롤러에서 완벽하게 작동합니다.

두 번째 경우에는 프로토 타입이 항상 NIB를 재정의하고 있다고 생각하고 프로토 타입 셀을 죽인 경우 재사용 ID에 대한 NIB를 등록하면 작동합니다. 그러나 셀에서 다른 프레임으로 segue를 설정할 수 없었습니다. 스토리 보드를 사용하는 요점입니다.

하루가 끝나면 스토리 보드에서 테이블 뷰 기반 흐름을 연결하고 코드가 아닌 시각적으로 셀 레이아웃을 정의하는 두 가지가 필요합니다. 지금까지 두 가지를 얻는 방법을 알 수 없습니다.

답변:


205

내가 이해 한대로 다음을 원합니다.

  1. 여러 스토리 보드 장면에서 사용할 수있는 IB의 셀을 디자인하십시오.
  2. 셀이있는 장면에 따라 해당 셀에서 고유 한 스토리 보드 segue를 구성하십시오.

불행히도 현재는이를 수행 할 방법이 없습니다. 이전의 시도가 작동하지 않은 이유를 이해하려면 스토리 보드 및 프로토 타입 테이블 뷰 셀의 작동 방식에 대해 더 많이 이해해야합니다. ( 이러한 다른 시도가 효과가없는 이유 에 대해 신경 쓰지 않는다면 지금 떠나십시오. 버그를 제기 할 것을 제안하는 것 외에는 다른 마법적인 해결책이 없습니다.)

스토리 보드는 본질적으로 .xib 파일 모음에 지나지 않습니다. 스토리 보드에 프로토 타입 셀이있는 테이블 뷰 컨트롤러를로드하면 다음과 같은 결과가 발생합니다.

  • 각 프로토 타입 셀은 실제로 자체 내장 미니 니브입니다. 따라서 테이블 뷰 컨트롤러가로드 될 때 각 프로토 타입 셀의 펜촉과 호출을 통해 실행됩니다 -[UITableView registerNib:forCellReuseIdentifier:].
  • 테이블보기는 컨트롤러에 셀을 요청합니다.
  • 당신은 아마 전화 -[UITableView dequeueReusableCellWithIdentifier:]
  • 지정된 재사용 식별자가있는 셀을 요청하면 셀에 펜촉이 등록되어 있는지 확인합니다. 그렇다면 해당 셀의 인스턴스를 인스턴스화합니다. 이것은 다음 단계로 구성됩니다.

    1. 셀의 펜촉에 정의 된대로 셀의 클래스를보십시오. 전화하십시오 [[CellClass alloc] initWithCoder:].
    2. -initWithCoder:방법을 통해 하위 뷰를 추가하고 펜촉에 정의 된 속성을 설정합니다. ( IBOutlet그것은에서 발생할 수 나는 것을 테스트하지 않았습니다하지만 아마뿐만 아니라 여기에 미혹 s의 -awakeFromNib)
  • 원하는대로 셀을 구성합니다.

여기서 주목해야 할 것은 셀 의 클래스 와 셀의 시각적 모양 사이에 차이가 있다는 것입니다 . 동일한 클래스의 두 개의 별도 프로토 타입 셀을 만들 수 있지만 하위 뷰는 완전히 다르게 배치됩니다. 실제로 기본 UITableViewCell스타일 을 사용하는 경우 이것이 정확히 발생합니다. 예를 들어 "기본"스타일과 "자막"스타일은 모두 같은 UITableViewCell클래스로 표시됩니다 .

이것은 중요하다 다음 클래스 셀의 특정와 일대일 상관 관계가없는 뷰 계층을 . 뷰 계층 구조는 전적으로이 특정 컨트롤러에 등록 된 프로토 타입 셀의 내용에 따라 결정됩니다.

또한 셀의 재사용 식별자는 일부 글로벌 셀 약국에 등록되지 않았습니다. 재사용 식별자는 단일 UITableView인스턴스 의 컨텍스트 내에서만 사용됩니다 .


이 정보가 주어지면 위의 시도에서 발생한 일을 살펴 보겠습니다.

컨트롤러 # 1에서 프로토 타입 셀을 추가하고 클래스를 내 UITableViewCell 서브 클래스로 설정하고 재사용 ID를 설정하고 레이블을 추가 한 후 클래스의 콘센트에 연결하십시오. 컨트롤러 # 2에서 빈 프로토 타입 셀을 추가하고 동일한 클래스로 설정 한 후 이전과 같이 id를 재사용하십시오. 셀이 컨트롤러 # 2에 표시 될 때 레이블이 표시되지 않습니다. 컨트롤러 # 1에서 잘 작동합니다.

이것은 예상됩니다. 두 셀 모두 동일한 클래스를 가지고 있지만 컨트롤러 # 2의 셀로 전달 된 뷰 계층 구조에는 하위 뷰가 전혀 없었습니다. 프로토 타입에 넣은 빈 셀이 있습니다.

각 셀 유형을 다른 NIB로 설계하고 적절한 셀 클래스에 연결했습니다. 스토리 보드에서 빈 프로토 타입 셀을 추가하고 클래스를 설정하고 ID를 재사용하여 내 셀 클래스를 참조하십시오. 컨트롤러의 viewDidLoad 메소드에서 재사용 ID에 해당 NIB 파일을 등록했습니다. 표시 될 때 두 컨트롤러의 셀은 프로토 타입처럼 비어있었습니다.

다시, 이것은 예상된다. 재사용 식별자는 스토리 보드 장면 또는 펜촉간에 공유되지 않으므로 이러한 개별 셀이 모두 동일한 재사용 식별자를 가졌다는 사실은 의미가 없습니다. 테이블 뷰에서 돌아온 셀은 스토리 보드의 해당 장면에서 프로토 타입 셀과 일치하는 모양을 갖습니다.

그러나이 솔루션은 가깝습니다. 언급했듯이 프로그래밍 방식으로 호출 하여 셀을 포함하는 셀을 -[UITableView registerNib:forCellReuseIdentifier:]전달하면 UINib동일한 셀을 다시 얻을 수 있습니다. (이것은 프로토 타입이 펜촉을 "재정의"했기 때문이 아니라, 단순히 테이블 뷰에 펜촉을 등록하지 않았기 때문에 스토리 보드에 내장 된 펜촉을 여전히보고있었습니다.) 불행히도이 접근법에는 결함이 있습니다. 스토리 보드 결과를 독립형 펜촉의 셀에 연결할 수있는 방법이 없습니다.

두 컨트롤러의 프로토 타입을 비우고 클래스를 설정하고 ID를 내 셀 클래스로 재사용하십시오. 코드로 셀의 UI를 완전히 구성했습니다. 셀은 모든 컨트롤러에서 완벽하게 작동합니다.

당연히. 바라건대, 이것은 놀라운 일이 아닙니다.


그래서 그것이 작동하지 않는 이유입니다. 독립형 펜촉으로 셀을 디자인하고 여러 스토리 보드 장면에서 사용할 수 있습니다. 당신은 현재 그 셀에 스토리 보드 segue를 연결할 수 없습니다. 그래도, 이것을 읽는 과정에서 무언가를 배웠기를 바랍니다.


아, 알겠다 당신은 나의 오해를 품었다 – 뷰 계층은 나의 클래스와 완전히 독립적이다. 회고하다. 큰 답변 주셔서 감사합니다.
Cliff W

더 이상 불가능, 그것은 보인다 : stackoverflow.com/questions/8574188/...
리치 아포 다카

7
@RichApodaca 나는 내 대답에서 그 해결책을 언급했다. 그러나 스토리 보드에는 없습니다. 별도의 펜촉에 있습니다. 그래서 당신은 segues를 배선하거나 다른 스토리 보드 같은 일을 할 수 없었습니다. 따라서 초기 질문을 완전히 다루지는 않습니다.
BJ 호머

XCode8부터 스토리 보드 전용 솔루션을 원하면 다음 해결 방법이 효과가있는 것 같습니다. 1 단계) ViewController # 1의 테이블 뷰에서 프로토 타입 셀을 만들고 사용자 정의 UITableViewCell 클래스와 연결하십시오. 2 단계) ViewController # 2의 테이블 뷰에서 해당 셀을 복사 / 붙여 넣기하십시오. 시간이 지남에 따라 스토리 보드에서 만든 사본을 삭제하고 업데이트 된 프로토 타입에 다시 붙여 넣어 셀 사본에 업데이트를 수동으로 전파해야합니다.
jengelsma

훌륭한 대답은 다음과 같습니다.> "스토리 보드는 본질적으로 .xib 파일 모음에 지나지 않습니다."이 경우 xib를 포함시키기가 어려운 이유는 무엇입니까? 스토리 보드?
willcwf

58

BJ 호머의 큰 대답에도 불구하고 해결책이있는 것 같습니다. 내 테스트가 진행되는 한 작동합니다.

개념 : xib 셀에 대한 사용자 정의 클래스를 작성하십시오. 거기에서 터치 이벤트를 기다리고 프로그래밍 방식으로 segue를 수행 할 수 있습니다. 이제 필요한 것은 Segue를 수행하는 컨트롤러에 대한 참조입니다. 내 해결책은에서 설정하는 것입니다 tableView:cellForRowAtIndexPath:.

나는 한 DetailedTaskCell.xib나는 여러 테이블 뷰에 사용하려는 테이블 셀을 포함 :

DetailedTaskCell.xib

TaskGuessTableCell해당 셀에 대한 사용자 정의 클래스가 있습니다 .

여기에 이미지 설명을 입력하십시오

이것은 마법이 일어나는 곳입니다.

// TaskGuessTableCell.h
#import <Foundation/Foundation.h>

@interface TaskGuessTableCell : UITableViewCell
@property (nonatomic, weak) UIViewController *controller;
@end

// TashGuessTableCell.m
#import "TaskGuessTableCell.h"

@implementation TaskGuessTableCell

@synthesize controller;

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSIndexPath *path = [controller.tableView indexPathForCell:self];
    [controller.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
    [controller performSegueWithIdentifier:@"FinishedTask" sender:controller];
    [super touchesEnded:touches withEvent:event];
}

@end

여러 개의 Segue가 있지만 모두 같은 이름을 갖습니다 "FinishedTask". 여기서 융통성을 발휘해야 할 경우 다른 속성을 추가하는 것이 좋습니다.

ViewController는 다음과 같습니다.

// LogbookViewController.m
#import "LogbookViewController.h"
#import "TaskGuessTableCell.h"

@implementation LogbookViewController

- (void)viewDidLoad
{
    [super viewDidLoad]

    // register custom nib
    [self.tableView registerNib:[UINib nibWithNibName:@"DetailedTaskCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"DetailedTaskCell"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    TaskGuessTableCell *cell;

    cell = [tableView dequeueReusableCellWithIdentifier:@"DetailedTaskCell"];
    cell.controller = self; // <-- the line that matters
    // if you added the seque property to the cell class, set that one here
    // cell.segue = @"TheSegueYouNeedToTrigger";
    cell.taskTitle.text  = [entry title];
    // set other outlet values etc. ...

    return cell;
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([[segue identifier] isEqualToString:@"FinishedTask"])
    {
        // do what you have to do, as usual
    }

}

@end

같은 것을 달성하는 더 우아한 방법이있을 수 있지만 작동합니다! :)


1
고마워, 나는 프로젝트 에서이 접근법을 구현하고있다. 대신 indexPath를 가져 와서 직접 행을 선택할 필요가 없도록이 메소드를 대체 할 수 있습니다.-(void) setSelected : (BOOL) selected animated : (BOOL) animated {[super setSelected : selected animated : animated]; if (선택) [self.controller performSegueWithIdentifier : self.segue 발신자 : self]; } [super touchesEnded : touches withEvent : event];를 호출 할 때 super가 셀을 선택한다고 생각했습니다. 선택되지 않은 경우 언제 선택되는지 알고 있습니까?
thejaz

9
이 솔루션을 사용하면 셀 내부에서 터치가 종료 될 때마다 segue가 트리거됩니다. 여기에는 실제로 셀을 선택하지 않고 단순히 스크롤하는 경우가 포함됩니다. -setSelected:셀을 재정의 하고에서 NO로 전환 할 때만 segue를 트리거하는 것이 더 좋습니다 YES.
BJ 호머

나는 setSelected:BJ 와 더 나은 운이있다 . 감사. 실제로, 그것은 우아하지 않은 해결책 이지만 (잘못된 느낌 ) 동시에 작동하므로 문제가 해결 될 때까지 (또는 Apple의 법원에서 변경 사항이 있음) 사용하고 있습니다.
벤 크리거

16

나는 이것을 찾고 있었고 Richard Venable의 대답 을 찾았습니다 . 그것은 나를 위해 작동합니다.

iOS 5에는 UITableView에 새로운 메소드가 있습니다 : registerNib : forCellReuseIdentifier :

사용하려면 UITableViewCell을 펜촉에 넣으십시오. 펜촉의 유일한 루트 대상이어야합니다.

tableView를로드 한 후 펜촉을 등록한 다음 dequeueReusableCellWithIdentifier :를 셀 식별자와 함께 호출하면 스토리 보드 프로토 타입 셀을 사용한 것처럼 펜촉에서 펜촉을 가져옵니다.


10

BJ 호머는 무슨 일이 일어나고 있는지에 대한 훌륭한 설명을했습니다.

실용적인 관점에서 볼 때 셀을 xib로 가질 수없고 segue를 연결할 수 없다면 셀을 xib로 사용하는 것이 가장 좋습니다. 전환은 여러 위치의 셀 레이아웃 및 속성보다 유지 관리가 훨씬 쉽습니다. , 그리고 어쨌든 당신의 segues는 다른 컨트롤러와 다를 수 있습니다. 테이블 뷰 컨트롤러에서 다음 컨트롤러로 직접 segue를 정의하고 코드로 수행 할 수 있습니다. .

추가 메모는 셀을 별도의 xib 파일로 사용하면 작업 등을 테이블 뷰 컨트롤러에 직접 연결할 수 없다는 것입니다 (어쨌든이 작업을 수행하지 않았습니다-파일 소유자를 의미있는 것으로 정의 할 수 없습니다 ). 셀의 테이블 뷰 컨트롤러가 준수 해야하는 프로토콜을 정의하고 cellForRowAtIndexPath의 대리자와 비슷한 약한 속성으로 컨트롤러를 추가 하여이 문제를 해결하고 있습니다.


10

스위프트 3

BJ Homer는 훌륭한 설명을 해주었습니다. 개념을 이해하는 데 도움이됩니다. To make a custom cell reusable in storyboard, 우리가 mix the Storyboard and xib접근 해야하는 모든 TableViewController에서 사용할 수 있습니다 . and에 CustomCell사용될 이름이 지정된 셀이 있다고 가정 합니다 . 단계적으로 만들고 있습니다. 1. 파일> 새로 만들기> 파일 클릭> Cocoa Touch Class 선택> 다음> 클래스 이름 지정 (예 : )> 하위 클래스를 UITableVieCell로 선택> XIB 파일 생성 확인란을 선택하고 다음을 누릅니다. 2. 원하는대로 셀을 사용자 정의하고 셀의 속성 관리자에서 식별자를 설정합니다 . 여기서는로 설정합니다 . 이 식별자는 ViewController에서 셀을 식별하고 재사용하는 데 사용됩니다. 3. 이제 우리는 단지TableViewControllerOneTableViewControllerTwo
CustomCell
CellIdentifier
register this cell우리의 ViewController에서 viewDidLoad. 초기화 방법이 필요 없습니다.
4. 이제이 사용자 정의 셀을 모든 tableView에서 사용할 수 있습니다.

TableViewControllerOne에서

let reuseIdentifier = "CellIdentifier"

override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: reuseIdentifier)
} 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier:reuseIdentifier, for: indexPath) as! CustomCell
    return cell!
}

5

segue를 테스트하지 않고 동일한 VC에 대해 셀을로드하는 방법을 찾았습니다. 이것은 별도의 펜촉으로 셀을 만드는 해결 방법이 될 수 있습니다.

하나의 VC와 2 개의 테이블이 있고 스토리 보드에서 셀을 디자인하여 두 테이블에서 모두 사용하려고한다고 가정합니다.

(예 : 결과에 대한 테이블이있는 UISearchController가있는 테이블 및 검색 필드이며 둘 다 동일한 셀을 사용하려고합니다)

컨트롤러가 셀을 요청할 때 다음을 수행하십시오.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * identifier = @"CELL_ID";

    ContactsCell *cell = [self.YOURTABLEVIEW dequeueReusableCellWithIdentifier:identifier];
  // Ignore the "tableView" argument
}

여기 스토리 보드의 셀이 있습니다


나는 이것을 시도했지만 세포가 재사용되지 않는 한 효과가있는 것 같습니다. 시스템은 항상 새로운 셀을 생성하고 매번 할당 해제합니다.
GilroyKilroy

이는 스토리 보드가있는 테이블 뷰에 검색 막대 추가에서 찾을 수있는 것과 동일한 권장 사항입니다 . 관심이 있으시면이 솔루션에 대한 자세한 설명이 있습니다 (검색 tableView:cellForRowAtIndexPath:).
Senseful

하지만이 덜 텍스트이며, 질문에 대한 대답
주앙 Nunes 보낸
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.