최초 작성일 : 2010/10/18 02:10 


3. 커스텀 셀


3-1. 커스텀 셀 만들기


커스텀 셀을 사용하는 방법은 생각보다 쉬웠습니다. 사실 커스텀 셀 뿐만 아니라 대부분의 아이폰

개발이 막상 닥쳐보면 생각보다 쉽습니다. 그만큼 애플에서 꼭 필요하다 싶은 것만을 SDK에

잘 담아 놓았기 때문이겠죠…^^(하지만 최근에는 많은 부분 접근 제약이 있음으로해서 보다 편의

적인 내용 구현이 막혀 있는 면을 보게 되어 좀 답답하긴 하더군요.)


본론으로 들어가서…

일단 화면상에 보여지는 대부분의 객체들은 모두 View기반이라고 생각하시면 되겠습니다.

테이블 뷰 셀 역시 UIView로 부터 상속받은 클래스이구요. 따라서 일단 셀 구성을 하는 것은

뷰에다가 객체를 배치하듯이 하면 됩니다.


일단 커스텀 셀로 사용할 클래스를 만들어야겠죠?


우선 New File 창에서 Cocoa Touch Class중 Objective-C class를 선택하시면 중앙에 있는

셀렉트 박스에서 UITableViewCell을 선택하실 수 있습니다. 이 것을 선택하시고 나머지

단계는 그냥 보통 하시던 대로 쭉쭉 나가시면 됩니다…^^;;;






기본적으로 구현되어있는 메서드는 초기화를 위한 initWithStyle:reuseIdentifier: 메서드와

셀의 선택여부를 설정할 수 있는 setSelected:animated: 메서드 그리고 dealloc메서드가

있습니다. 매우 단순합니다.


추가적으로 이 셀에서 발생한 이벤트와 관련하여 이 셀이 사용되는 TableView로 메시지를

전달하기 위해 protocol을 만들어야 하는 경우도 있으나 필수는 아닙니다. 만일 커스텀 셀에서의

이벤트를 테이블 뷰에서 처리할 일이 없다면 굳이 안만들어도 될 것 같습니다.


그리고 앞서 UIView에서 상속받은 클래스라고 하였기 때문에 미리 짐작을 하신 분들도 계시겠지만

viewDidLoad같은 메서드들이 없기 때문에 IB를 통해 셀 화면을 구성해야 하는데 자동으로

XIB 파일을 생성해주지 않습니다. 따라서 생성된 클래스 파일명과 같은 이름으로 별도로

XIB 파일을 만들어 주고 IB를 통해 디자인을 하도록 합니다.


New File 창에서 iOS의 User Interface를 선택하고 Empty XIB를 선택합니다.

다음 단계에서 파일명은 앞서 만든 클래스 파일명과 동일하게 만들어주고 Finish합니다.









다음 XIB파일을 열어서 File's Owner를 선택하고 Object Identify 창에서 Class를 이 커스텀 셀을

사용할 Table View Controller 클래스로 지정해줍니다.






마지막으로 Library Inspector에서 Table View Cell 객체를 메인 Inspector로 가져다 놓습니다.

그리고 Object Identify Inspector에서 Class를 앞서 만든 Table View Cell 클래스로 지정합니다.







마지막으로 자주 실수하는 부분인데요. 반드시 Attribute Inspector창의 맨 위에 있는

Identifier에 이름을 지정해주고 이 이름으로 코드 상에서 사용을 해야 합니다.

[tableView dequeueResusableCellWithIdentifier:identifier]에서 identifier의 값이 바로

여기서 지정한 이름이 되어야 합니다.






이제 준비는 다 끝났네요. 방금 가져다놓은 Table View Cell 객체를 더블클릭하면 작은 뷰

화면 하나가 나타납니다.









여기다가 필요한 컨트롤들을 가져다 놓으시면 됩니다.


이 과정으로 만들어진 제 커스텀 셀입니다. 아이 등록 화면의 첫번째 섹션에 사용되는 셀입니다.

사진 선택 및 출력용의 버튼 하나와 사진 프레임을 위한 이미지 뷰, 이름과 생년월일 입력을 위한

텍스트 필드 2개와 음력 Label 표시를 위한 Label, 그리고 양/음력 선택을 위한 Segment 컨트롤이

들어있습니다.









3-2. 커스텀 셀 사용하기


자, 구슬이 서말이라도 꿰어야 보배라죠…^^ 만든 커스텀 셀을 사용해보도록 하겠습니다.

단도직입! 바로 소스를 보죠.


제가 만든 커스텀 셀은 AddChildViewController라는 클래스에서 사용합니다.


우선 만일 커스텀 셀에서 protocol을 만들었다면 당연히 AddChildViewController 클래스는

delegate메서드를 구현해야겠죠. 저는 ChildBasicInfoCellDelegate를 구현하도록 코딩이 되어

있습니다. 사용하는 delegate 메서드는 생년월일 입력 텍스트 필드 터치시 데이트 피커를

불러오기 위한  movePickerView:(ChildBasicInfoCell *)basicCell 메서드입니다.


이 클래스는 당연히 UITableViewController를 상속 받았구요.

따라서 테이블 뷰를 구성하는 각종 delegate 메서드들이 있습니다.

그 중에 커스텀 셀을 화면에 보여주는 메서드는 바로


tableView:cellForRowAtIndexPath:


이 메서드입니다. 이 메서드는 내부적으로 Row 수만큼 Loop를 돌면서 테이블 뷰 위에

테이블 뷰 셀을 붙여줍니다.


다음은 AddChildViewController메서드에 앞서 보여드린 제 커스텀 셀을 붙이이 위해 구현한

내용만을 가져온 것입니다.


//첫번째 섹션인 경우에만 이 커스텀 셀을 사용합니다.

if (indexPath.section == BASIC_SECTION) {

                      

//바로 이 이름("ChildBasicInfo")이 IB의 Attribute Inspector의 identifier 항목에 입력한

              //그 이름이어야 합니다.                                                  

static NSString *CellIdentifier = @"ChildBasicInfo";

    

//커스텀 셀을 사용하지 않을 경우에는 이 때 만들어지는 셀의 클래스는 그냥

//UITableViewCell입니다. 지금은 제가 만든 커스텀 셀 클래스인 ChildBasicInfoCell의

//인스턴스를 받아옵니다.

ChildBasicInfoCell *cell = (ChildBasicInfoCell *)[tableView 

dequeueReusableCellWithIdentifier:CellIdentifier];

//셀의 재사용을 위한 조건문이죠. 이미 사용된 적이 있는 인스턴가 있으면 그 것을 그냥

// 사용하고 그렇지 않으면 새로운 인스턴스를 만듭니다.

if (cell == nil) {

//일단 메인 번들로부터 커스텀 셀들을 모두 가져와서 배열에 넣고

NSArray *nib = [[NSBundle mainBundle]

loadNibNamed:@"ChildBasicInfoCell" owner:self options:nil];

//가져온 커스텀 셀들의 배열을 Loop로 돌려서

for (id oneObject in nib) {

//사용하고자 하는 커스텀 셀의 클래스와 같은 객체가 있으면 그 것을 골라

//사용하게 됩니다. 여기서 제가 궁금한 것은 굳이 이렇게 모든 커스텀 셀들의

//배열을 가져와 루프를 돌려야 하는 이유가 뭐냐는 것입니다. 누가 답변좀

//해주세요…^^;;;

if ([oneObject isKindOfClass:[ChildBasicInfoCell class]]) {

cell = (ChildBasicInfoCell *) oneObject;

cell.delegate = self;

//이전 글에서도 말씀드렸듯이 전 같은 화면을 최초 등록 용도와 기존

//데이터의 수정 용도로 같이 사용합니다. 아래 if문은 수정 용도로

//사용될 경우 이전 데이터를 보여주기 위한 내용입니다.

if (childData != nil) {

cell.nameField.text = childData.childname;

cell.birthDay.text = [self.dateFormatter 

stringFromDate:childData.birthday];

cell.birthDate = childData.birthday;

cell.isLunar.selectedSegmentIndex =

[[childData valueForKey:@"islunar"]

 integerValue];

CGSize size = childData.thumbnailImage.size;

CGFloat ratio = 0;

if (size.width > size.height) {

ratio = 116.0 / size.width;

else {

ratio = 116.0 / size.height;

}

CGRect rect =

CGRectMake(0.0,

   0.0,

   ratio * size.width,

   ratio * size.height);

UIGraphicsBeginImageContext(rect.size);

[childData.thumbnailImage drawInRect:rect];

[cell.photoViewButton 

setImage:UIGraphicsGetImageFromCurrentImageContext()

forState:UIControlStateNormal];

UIGraphicsEndImageContext();

}

childBasicInfoCell = cell;

}

}

}

//이렇게 만들어진 커스텀 셀을 반환합니다.

return cell;

}


마지막으로 역시 빼먹기 쉬운 메서드인데요.

UITableViewDelegate에 있는 메서드인


- (CGFloat)tableView:(UITableView *)tableView 

           heightForRowAtIndexPath:(NSIndexPath *)indexPath


이 메서드입니다. 제가 만든 커스텀 셀은 높이가 120필셀입니다. 그냥 이상태로는 테이블 뷰는

셀의 높이까지는 파악을 하지 못합니다. 그래서 셀의 높이가 기본 높이로 그냥 표시되기 때문에

커스텀 셀이 잘려보입니다. 이렇게 기본 높이와 다르게 만들어진 커스텀 셀의 높이를 테이블 뷰에

알려주기 위해서는 위 메서드를 통해 커스텀 셀의 높이를 알려주어야 합니다.


코드는 아래와 같습니다.


- (CGFloat)tableView:(UITableView *)tableView 

           heightForRowAtIndexPath:(NSIndexPath *)indexPath

if (indexPath.section == BASIC_SECTION) {

return 120.0f;

else {

return 44.0f;

}

}


저는 첫번째 섹션만 셀의 높이가 다르기 때문에 이와 같이 코딩되어 있습니다.



4. 셀에 컨트롤 넣기


3절의 내용에서도 커스텀 셀에 segment 컨트롤이 들어가 있었습니다.

만일 모든 셀에 동일하게 컨트롤을 포함시키고자 한다면 이렇게 사용하면 됩니다.

하지만 같은 셀을 사용하면서 특정 인덱스에 있는 셀에만 컨트롤을 포함시키고자 할 경우도

있을 것입니다.


아래는 제 iPhotoDiary의 기념일을 등록하는 화면입니다.






화면을 보시면 이 테이블 뷰 셀들은 모두 같은 클래스로 만들어져 있는데요.

위의 2개의 셀에는 타이틀과 눈에 보이지는 않지만 값을 입력받을 Lable 그리고

Disclosure Indicator가 있는 반면 아래 2개의 셀은 타이틀과 스위치 컨트롤만 보입니다.






이렇게 유동적으로 컨트롤을 넣기 위해서는 역시 cellForRowAtIndexPath메서드에서 작업을

해야 하구요. 코드는 아래와 같습니다. 셀을 만드는 과정이 이미 잘 알고 계실 듯하여 핵심 코드만

적습니다.


if (indexPath.row == 0) {

cell.titleLabel.text = @"기념일 : ";

else if (indexPath.row == 1) {

cell.titleLabel.text = @"날짜 : ";

else if (indexPath.row == 2) {

cell.titleLabel.text = @"공휴일 : ";

cell.accessoryView =

[[[UISwitch allocinitWithFrame:CGRectZeroautorelease];

else if (indexPath.row == 3) {

cell.titleLabel.text = @"음력 : ";

cell.accessoryView =

[[[UISwitch allocinitWithFrame:CGRectZeroautorelease];

}


아주 간단하죠…^^

즉, cell 인스턴스의 accessoryView 속성에 사용하고자 하는 컨트롤의 인스턴스를 만들어

넣어주기만 하면 됩니다.


그리고 Disclosure Indicator는  IB를 통해 설정해주었기 때문에 코드상에는 설정하는 부분이

없는 것입니다.



5. 테이블 뷰의 배경


이 것은 일종의 팁이라고도 할 수 있겠지만 썩 좋으 방법 같지도 않아 그냥 간단하게

이런 방법도 있다는 것을 말씀드리는 선에서 넘어가겠습니다.


테이블 뷰를 사용하면서 기존에 자주 보이는 회색 스트라이프가 딱히 맘에 들지 않아

테이블 뷰에 배경을 넣고 싶었습니다. 그런데 테이블 뷰에 바로 배경을 넣으니 테이블 뷰

전체에 배경이 깔리는 것이 아니라 각각의 셀에 배경이 들어가게 됩니다.


즉, 셀의 높이만큼 이미지들이 잘려서 셀의 갯수만큼 보여지게 되죠.

배경 이미지가 단순하여 구분이 가실지는 모르겠지만 요렇게 됩니다.



뷰 컨트롤러의 뷰에 배경이 깔린 경우




테이블 뷰 컨트롤러의뷰에 배경이 깔린 경우









그래서 사용하는 방법은 UIViewController에 UITableViewController.view를 얹고 뷰 컨트롤러의

view 배경으로 이미지를 넣은 후 테이블 뷰와 커스텀 셀들의 배경색을 투명으로 지정하는 것입니다.

그럼 테이블 뷰를 통과해 밑에 있는 뷰의 배경 이미지가 전체적으로 보이게 됩니다.


코드는 아래와 같습니다.


anniversaryTableView = [[AddAnniversaryTableViewController alloc]

initWithStyle:UITableViewStylePlain];

//테이블 뷰의 배경색을 투명으로 지정합니다.

anniversaryTableView.view.backgroundColor = [UIColor clearColor];

anniversaryTableView.view.opaque = YES;

anniversaryTableView.delegate = self;

anniversaryTableView.tableView.allowsSelectionDuringEditing = YES;

//뷰 컨트롤러의 뷰에 배경 이미지를 지정합니다.

self.view.backgroundColor =

[UIColor colorWithPatternImage:

[UIImageimageNamed:@"anniversary_background.png"]];

anniversaryTableView.view.frame = CGRectMake(9010230450);


//그리고 뷰 컨트롤러의 뷰 위에 테이블 뷰 컨트롤러의 뷰를 붙입니다.

[self.view addSubview:anniversaryTableView.view];


하지만 이 방법은 테이블 뷰에 배경을 깔기 위해 불필요한 뷰 컨트롤러을 하나 더 사용해야 한다는

점에서 성능면으로나 구현의 난이도 면에서 그리 좋아 보이진 않습니다.


구현의 난이도라고 표현한 것은 테이블 뷰에서 발생한 이벤트를 통해 새로운 뷰를 띄우거나 할 경우

새로운 뷰가 뜨는 위치가 꼬임으로 해서 delegate를 잘 써줘야 한다는 점입니다.


혹시 더 좋은 방법을 알고 계신 분들이 있으시면 정보 부탁드립니다…^^;;;


오늘로 이번 장을 끝내볼까 했는데 또 내용이 길어져 다음주까지 계속해야겠네요.

이번 주는 이것으로 마치겠습니다.

긴 글 읽어주셔서 감사합니다.


6. 셀에 대한 접근과 인덱스 알아내기

7. 등록과 수정 화면 공유

8. 테이블 뷰의 스크롤

9. 요약

10. 마무리

블로그 이미지

마즈다

이제 반백이 되었지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

댓글을 달아 주세요