문제는 간단합니다. UITableViewCell
Xib 파일에서 사용자 정의 를 어떻게로드 합니까? 그렇게하면 Interface Builder를 사용하여 셀을 디자인 할 수 있습니다. 메모리 관리 문제로 인해 대답은 간단하지 않습니다. 이 글 에서는이 문제에 대해 언급하고 해결책을 제시하지만, NDA 이전 버전이며 코드가 부족합니다. 다음 은 명확한 답변을 제공하지 않고 문제를 논의 하는 긴 글 입니다.
내가 사용한 코드는 다음과 같습니다.
static NSString *CellIdentifier = @"MyCellIdentifier";
MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
cell = (MyCell *)[nib objectAtIndex:0];
}
이 코드를 사용하려면 새 하위 클래스 인 MyCell.m / .h를 만들고 원하는 구성 요소를 UITableViewCell
추가하십시오 IBOutlets
. 그런 다음 새 “Empty XIB”파일을 작성하십시오. IB에서 Xib 파일을 열고 UITableViewCell
오브젝트를 추가 하고 ID를 “MyCellIdentifier”로 설정 한 후 클래스를 MyCell로 설정하고 구성 요소를 추가하십시오. 마지막으로을 IBOutlets
구성 요소에 연결하십시오 . IB에서 파일 소유자를 설정하지 않았습니다.
다른 메소드는 파일 소유자 설정을 옹호하고 Xib가 추가 팩토리 클래스를 통해로드되지 않은 경우 메모리 누수에 대해 경고합니다. 위의 Instruments / Leaks에서 테스트했으며 메모리 누수가 없음을 확인했습니다.
Xibs에서 셀을로드하는 정식 방법은 무엇입니까? File ‘s Owner를 설정합니까? 우리는 공장이 필요합니까? 그렇다면 공장 코드는 어떻게 생겼습니까? 여러 솔루션이있는 경우 각 솔루션의 장단점을 명확히하십시오.
답변
IB 엔지니어가 원 저자가 추천 한 두 가지 방법은 다음과 같습니다 .
자세한 내용은 실제 게시물을 참조하십시오. 더 간단한 것처럼 방법 # 2를 선호합니다.
방법 # 1 :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
if (cell == nil) {
// Create a temporary UIViewController to instantiate the custom cell.
UIViewController *temporaryController = [[UIViewController alloc] initWithNibName:@"BDCustomCell" bundle:nil];
// Grab a pointer to the custom cell.
cell = (BDCustomCell *)temporaryController.view;
[[cell retain] autorelease];
// Release the temporary UIViewController.
[temporaryController release];
}
return cell;
}
방법 # 2 :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
if (cell == nil) {
// Load the top-level objects from the custom cell XIB.
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
// Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
cell = [topLevelObjects objectAtIndex:0];
}
return cell;
}
업데이트 (2014) :
방법 # 2는 여전히 유효하지만 더 이상 설명서가 없습니다. 이전에는 공식 문서에 있었지만 이제 스토리 보드를 위해 제거되었습니다.
나는 Github에 실제 예제를 게시했다 :
https://github.com/bentford/NibTableCellExample
스위프트 4.2 편집
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.tblContacts.register(UINib(nibName: CellNames.ContactsCell, bundle: nil), forCellReuseIdentifier: MyIdentifier)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: MyIdentifier, for: indexPath) as! ContactsCell
return cell
}
답변
올바른 해결책은 다음과 같습니다.
- (void)viewDidLoad
{
[super viewDidLoad];
UINib *nib = [UINib nibWithNibName:@"ItemCell" bundle:nil];
[[self tableView] registerNib:nib forCellReuseIdentifier:@"ItemCell"];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Create an instance of ItemCell
PointsItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ItemCell"];
return cell;
}
답변
레지스터
iOS 7 이후이 프로세스는 ( swift 3.0 ) 으로 단순화되었습니다 .
// For registering nib files
tableView.register(UINib(nibName: "MyCell", bundle: Bundle.main), forCellReuseIdentifier: "cell")
// For registering classes
tableView.register(MyCellClass.self, forCellReuseIdentifier: "cell")
( 주 ) 프로토 타입 셀로
.xib
또는.stroyboard
파일에 셀을 만들어서 달성 할 수도 있습니다 . 클래스를 첨부 해야하는 경우 셀 프로토 타입을 선택하고 해당 클래스를 추가 할 수 있습니다 (UITableViewCell
물론 의 자손이어야 함 ).
대기열에서 제외
그리고 나중에 ( swift 3.0 )을 사용하여 대기열에서 제외했습니다 .
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Hello"
return cell
}
차이점은이 새로운 방법은 셀을 대기열에서 제외시킬뿐만 아니라 존재하지 않는 경우 (즉 if (cell == nil)
, shenanigans 를 수행 할 필요가 없음 ) 생성하며 셀은 위의 예와 같이 사용할 준비가 된 것입니다.
( 경고 )
tableView.dequeueReusableCell(withIdentifier:for:)
에는 새로운 동작이 있습니다. 다른 동작을 호출하면 ()없이indexPath:
기존 동작을 확인할nil
수UITableViewCell?
있습니다.
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? MyCellClass
{
// Cell be casted properly
cell.myCustomProperty = true
}
else
{
// Wrong type? Wrong identifier?
}
물론 셀의 연관된 클래스 유형은 UITableViewCell
서브 클래스 의 .xib 파일에서 정의한 유형 이거나 다른 레지스터 메소드를 사용하여 정의 된 유형입니다 .
구성
이상적으로는 셀을 등록 할 때의 모양 및 내용 위치 (레이블 및 이미지보기 등)와 셀을 cellForRowAtIndexPath
채우는 방법 에 따라 셀이 이미 구성되어 있습니다.
모두 함께
class MyCell : UITableViewCell
{
// Can be either created manually, or loaded from a nib with prototypes
@IBOutlet weak var labelSomething : UILabel? = nil
}
class MasterViewController: UITableViewController
{
var data = ["Hello", "World", "Kinda", "Cliche", "Though"]
// Register
override func viewDidLoad()
{
super.viewDidLoad()
tableView.register(MyCell.self, forCellReuseIdentifier: "mycell")
// or the nib alternative
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return data.count
}
// Dequeue
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "mycell", for: indexPath) as! MyCell
cell.labelSomething?.text = data[indexPath.row]
return cell
}
}
물론 이것은 ObjC에서 모두 같은 이름으로 제공됩니다.
답변
Shawn Craver의 답변을 가져 와서 약간 정리했습니다.
BBCell.h :
#import <UIKit/UIKit.h>
@interface BBCell : UITableViewCell {
}
+ (BBCell *)cellFromNibNamed:(NSString *)nibName;
@end
BBCell.m :
#import "BBCell.h"
@implementation BBCell
+ (BBCell *)cellFromNibNamed:(NSString *)nibName {
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:NULL];
NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
BBCell *customCell = nil;
NSObject* nibItem = nil;
while ((nibItem = [nibEnumerator nextObject]) != nil) {
if ([nibItem isKindOfClass:[BBCell class]]) {
customCell = (BBCell *)nibItem;
break; // we have a winner
}
}
return customCell;
}
@end
내 UITableViewCell의 BBCell 하위 클래스를 모두 만든 다음 표준을 바꿉니다.
cell = [[[BBDetailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"BBDetailCell"] autorelease];
와:
cell = (BBDetailCell *)[BBDetailCell cellFromNibNamed:@"BBDetailCell"];
답변
나는 bentford의 방법 # 2를 사용했다 :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
if (cell == nil) {
// Load the top-level objects from the custom cell XIB.
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
// Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
cell = [topLevelObjects objectAtIndex:0];
}
return cell;
}
작동하지만 사용자 정의 UITableViewCell .xib 파일에서 File ‘s Owner 에 연결 되어 있는지 확인하십시오.
전달하여 owner:self
귀하의 loadNibNamed
성명, 당신은 설정 UITableViewController
당신의 파일의 소유자로 UITableViewCell
.
IB의 헤더 파일로 끌어서 놓아 조치 및 아울렛을 설정하면 기본적으로 파일 소유자로 설정됩니다.
에서 loadNibNamed:owner:options
, 애플의 코드는에 속성을 설정하려고합니다 UITableViewController
그 소유자이기 때문에. 그러나 해당 속성이 정의되어 있지 않으므로 키 값 코딩 호환에 대한 오류가 발생 합니다 .
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<MyUITableViewController 0x6a383b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key myLabel.'
이벤트가 대신 트리거되면 NSInvalidArgumentException이 발생합니다.
-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0'
*** First throw call stack:
(0x1903052 0x15eed0a 0x1904ced 0x1869f00 0x1869ce2 0x1904ec9 0x5885c2 0x58855a 0x62db76 0x62e03f 0x77fa6c 0x24e86d 0x18d7966 0x18d7407 0x183a7c0 0x1839db4 0x1839ccb 0x1f8b879 0x1f8b93e 0x585a9b 0xb904d 0x2c75)
terminate called throwing an exceptionCurrent language: auto; currently objective-c
쉬운 해결 방법은 UITableViewCell
파일 소유자 대신 인터페이스 빌더 연결을 지정하는 것입니다.
- File ‘s Owner를 마우스 오른쪽 버튼으로 클릭하여 연결 목록을 불러옵니다.
- Command-Shift-4로 화면을 캡처합니다 (끌어서 캡처 할 영역을 선택합니다)
- 파일 소유자의 연결을 x
- 오브젝트 계층에서 UITableCell을 마우스 오른쪽 단추로 클릭하고 연결을 다시 추가하십시오.
답변
이 답변 중 어느 것도 마음에 들지 않기 때문에 게시하기로 결정했습니다. 일이 항상 더 간단 할 수 있으며 이것이 내가 찾은 가장 간결한 방법입니다.
1. 원하는대로 Interface Builder에서 Xib를 빌드하십시오.
- 파일 소유자를 NSObject 클래스로 설정
- UITableViewCell을 추가하고 클래스를 MyTableViewCellSubclass로 설정하십시오-IB가 충돌하는 경우 (이 글에서 Xcode> 4로 발생) Xcode 4에서 UIView를 사용하십시오.
- 이 셀 안에 하위 뷰를 배치하고 .h 또는 .m에서 IBOutlet 연결을 @interface에 연결하십시오 (.m이 선호)
2. UIViewController 또는 UITableViewController 서브 클래스에서
@implementation ViewController
static NSString *cellIdentifier = @"MyCellIdentier";
- (void) viewDidLoad {
...
[self.tableView registerNib:[UINib nibWithNibName:@"MyTableViewCellSubclass" bundle:nil] forCellReuseIdentifier:cellIdentifier];
}
- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyTableViewCellSubclass *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
...
return cell;
}
3. MyTableViewCellSubclass에서
- (id) initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
...
}
return self;
}
답변
Interface Builder를 사용하여 셀을 만드는 경우 Inspector에서 식별자를 설정했는지 확인하십시오. 그런 다음 dequeueReusableCellWithIdentifier를 호출 할 때 동일한 지 확인하십시오.
실수로 테이블이 많은 프로젝트에서 일부 식별자를 설정하는 것을 잊어 버렸고 성능 변경은 밤낮과 같습니다.