'cellForRowAtIndexPath'에 해당되는 글 1건

  1. 2010.07.12 [iPhone] - 시작하세요! 아이폰 3 프로그래밍 - Part 8. 테이블 뷰 입문 2

[iPhone] - 시작하세요! 아이폰 3 프로그래밍 - Part 8. 테이블 뷰 입문

iPhone/[위키북스]시작하세요! 아이폰3 프로그래밍 2010. 7. 12. 22:07

* 테이블
- 테이블은 데이터의 목록을 표시한다.
- 테이블 목록의 각각의 항목은 행(row).각 행마다 열(column)은 하나만 있다.
- 테이블 뷰테이블에 있는 데이터를 보여주는 뷰이고 UITableView클래스의 인스턴스이다. 테이블에 각각의 행은 UITableViewCell 클래스로 구현된다. 그래서 테이블 뷰는 테이블의 전체적인 모양을 담당하는 객체이고 테이블 뷰 셀(table view cell)이 테이블의 각각의 행을 그리는 일을 담당한다.
- 테이블 뷰는 테이블의 데이터를 저장하는 역할은 하지 않는다. 단지 현재 보여주는 행을 그릴때 필요한 데이터만 저장한다.
- 테이블 뷰의 설정 데이터는 UITableVewDelegate 프로토콜을 따르는 객체에서 구하고 각 행의 데이터는 UITableViewDataSource프로토콜을 따르는 객체로부터 얻는다.

- 테이블 뷰에는 두가지 기본 스타일이 존재.
---- 그룹으로 묶은 방식(Group Table). 그룹으로 묶은 테이블의 각각의 그룹은 가장 왼쪽 그림처럼 둥근 사각형에 둘러싸인 행의 집합이다.
----- 인덱스로 구분한 테이블(Indexed Table). 등근 사각형이 없는 테이블.

- 테이블의 나누어진 영역이 데이터소스에서는 섹션이다.
---- 그룹으로 묶은 테이블에서 각 그룹은 세견이다.
---- 인덱스로 묶은 테이블에서는 데이터를 인덱스별로 묶은 것이 섹션이다.

* TableView data source
// 이것은 특정 섹션에 몇개의 행이 있는지 질의하는데 사용한다.
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [self.listData count];
}

// 행을 그릴 필요가 있을 때 데이블 뷰가 이 메서드를 호출한다.
// NSIndexPath로부터 행이나 섹션을 얻을수 있다.
// 첫번째 인자인 tableView는 현재 메서드를 호출한 테이블의 레퍼런스
// 두번째 인자는 NSIndexPath의 객체
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 이 문자열은 테이블 셀의 종류를 나타내는 키로 쓰인다.
    static NSString *SimpleTableIdentifier = @"SimpleTableIdentifier";

    // 테이블 뷰의 셀들이 스크롤돼서 화면에서 사라지면 재사용 가능한 셀의 뷰(queue)에 들어간다.
    // 새로운 행이 이전에 사라졌던 행 중에서 다시 사용하게 된다면 시스템은 끊임없이 이러한 뷰를 만들고 해제하는 부담을 피할수 있다.
    // 이러한 방법을 사용하기 위해서 디큐(dequeue)된 셀중에서 필요한 타입을 테이블 뷰에서 얻어야 한다. (SimpleTableIdentifier)
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleTableIdentifier];
    if(cell == nil)
    {
        // 재사용 가능하게 동일한 셀로 만든다.
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewStyleDefault] reuseIdentifier:SimpleTableIdentifier] autorelease];
    }

    // 어떤 행의 값을 사용할 것인지 결정
    NSInteger row = [indexPath row];
    cell.textLabel.text = [listData objectAtIndex:row];
    return cell;
}
* 각행에 이미지 추가하기
-(UITableViewCell *)tableView:(UITableView *) cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *SimpleTableIdentifier = @"SimpleTableIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleTableIdentifier];
    if(cell == nil)
    {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero] reuseIdentifier:SimpleTableIdentifier] autorelease];
    }
    // cell에 이미지를 추가한다.    
    // UIImage는 파일 이름을 기반으로 캐시 기술을 사용하므로, 매번 새 이미지를 로딩하지 않을 것이다. 대신 캐쉬된 이미지를 사용할 것이다.
    UIImage *image = [UIImage imageName:@"star.png"];
    cell.imageView.image = image;
    // 해당 cell이 선택되었을때 이미지 추가
    // UIImage *highlightedImage = [UIImage imageName:@"star2.png"];
    // cell.imageView.highlightedImage = highlightedImage;
    NSInteger row = [indexPath row];
    cell.textLabel.text = [listData objectAtIndex:row];
    return cell;
}

* UITableViewCellStyle에 종류
- 하위 제목 스타일 적용 : 하위 제목은 작은 글자로 텍스트 레이블을 설명하는 글을 담고 있으면, 텍스트 레이블 밑에 회색 컬러를 사용하여 출력된다.
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:SimpleTableIdentifier] autorelease];
cell.detailTextLabel.text = @"XXXXXX";

- 텍스트 레이블과 상세 레이블을 한 줄에 배치하고 서로 대칭되도록 정렬 : 텍스트 레이블은 검은색으로 셀의 왼쪽에 나타나고, 상세 텍스트는 파란색으로 셀의 오른쪽에 나타난다.
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1reuseIdentifier:SimpleTableIdentifier] autorelease];
cell.detailTextLabel.text = @"XXXXXX";

- 셀에 대한 정보를 설명하기 위해 사용되는 레이블과 나란히 출력할 때 사용된다.
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:SimpleTableIdentifier] autorelease];
cell.detailTextLabel.text = @"XXXXXX";

* Delegate를 사용해서 몇몇 행은 들여쓰기(Indent)
#pragma mark -
#pragma mark Table Delegate Methods
-(NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSUInteger row = [indexPath row];
    return row;
}

* 특정 행 선택 불가능하게 하기
- 델리게이트는 두개의 메서드를 사용해서 사용자가 특정행을 선택했는지 알수 있다. 그중 한개가 행이 선택되기 전에 호출되고 이 메서드에서 행이 선택되는 것을 막거나 심지어 선택되는 행을 바꿀수도 있다.
-(NSIndexPath *)tableView:(NSTableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSUInteger row = [indexPath row];
    // 선택하려는 항목이 첫번째이라면 nil를 리턴하여 선택하지 못하게 한다.
    if(row == 0)
        return nil;
    
    return indexPath;
}

* 선택한 특정 행 알아내기
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSUInteger row = [indexPath row];
    NSString *rowValue = [listData objectAtIndex:row];
    NSString *message = [[NSString alloc] initWithFormat:@"You selected %@", rowValue];
     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Row Selected" message:message delegate:nil cancelButtonTitle:@"Yes I Did" otherButtonTitles:nil];
     [alert show];
    [message release];
    [alert relase];
    [tableView deselectRowAtIndexPath:IndexPath animated:YES];
}

* 폰트 크기 바꾸기
cell.TextLabel.font = [UIFont boldSystemFontOfSize:50];

* 델리케이트를 에서 테이블 높이 지정
-(CGFolat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 70;
}

* 맞춤형 테이블 뷰 만들기
- UITableViewCell이 지원하는 방식이 아닌 다른 방식
---- UITableViewCell에 하위뷰를 추가 하는것
---- UITableViewCell의 하위클래스를 만드는것

* UITableViewCell에 하위뷰를 추가하는방식
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellTableIdentifier = @"CellTableIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellTableIdentifier];
    if(cell == nil)
    {
        // 새로 추가될 새로운 셀을 만든다.
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellTableIdentifier] autorelease];
        
        CGRect nameLabelRect = CGRectMake(0,5,70,15);
        UILabel *nameLabel = [[UILabel alloc] initWithFrame:nameLabelRect];
        nameLabel.textAlignment = UITextAlignmentRight;
        nameLabel.text = @"Name";
        nameLabel.font = [UIFont boldSystemFontOfSize:12];
        [cell.contentView addSubview:nameLabel];
        [nameLabel release];
        
        CGRect colorLabelRect = CGRectMake(0, 26,70,15);
        UILabel *colorLabel = [[UILabel alloc] initWithFrame:colorLabelRect];
        UILabel.textAlignment = UITextAlignmentRight;
        UILabel.text = @"Color";
        UILabel.font = [UIFont boldSystemFontOfSize:12];
        [cell.contentView addSubview:colorLabel];
        [colorLabel release];
        
        CGRect nameValueRect = CGRectMake(80,5,200,15);
        UILabel *nameValue = [[UILabel alloc] initWithFrame:nameValueRect];
        nameValue.tag = kNameValueTag;        // 나중에 해당 레이블에게 값을 할당할 수 있도록 이 필드를 찾을 방법을 추가 
        [cell.contentView addSubview:nameValue];
        [nameValue release];
        
        CGRect colorValueRect = CGRectMake(80,25,200,15);
        UILabel *colorValue = [[UILabel alloc] initWithFrame:colorValueRect];
        colorValue.tag = kColorValueTag;            // 나중에 해당 레이블에게 값을 할당할 수 있도록 이 필드를 찾을 방법을 추가
        [cell.contentView addSubview:colorValue];
        [colorValue release];
    }
    NSUInteger row = [indexPath row];
    NSDictionary *rowData = [self.computers objectAtIndex:row];
    UILabel *name = (UILabel *)[cell.contentView viewWithTag:kNameValueTag];    // 해당 레이블 찾기
    name.text = [rowData objectForKey:@"Name"];
    UILabel *color = (UILabel *)[cell.contentView viewWithTag:kColorValueTag];        // 해당 레이블 찾기 
    color.text = [rowData objectForKey:@"Color"];
    return cell;
}

* UITableViewCell의 하위클래스 만들어 사용하기
- 인터페이스 빌더를 사용해서 테이블 셀 뷰를 설계하기
- UITableViewCell의 하위클래스와 테이블 뷰 셀을 담을 새 nib 파일을 만든다. 그런 후에 테이블 뷰 셀이 행을 표현할 때 테이블 뷰 셀에 하위뷰를 추가하는 대신에 단순히 nib 파일에서 구현한 하위클래스를 로드하고 두개의 새 아웃렛을 사용해서 이름과 색을 걸정할것

1. Xcode에서 class폴더에 Cocoa Touch Classes를 고르고 Objective-C class 를 선택한 다음 하위 클래스로 UITableViewCell을 선택하고 새로운 파일을 추가 한다. 파일명은 "CustomCell". Also create가 선택되었는지 확인..

2. Xcode에서 resources폴더에 User Interfaces를 클릭하고 Empty XIB를 추가한다. 파일명은 "CustomCell"

3. CustomCell.h파일에 아래 코드 추가
#import <UIKit/UIKit.h>
 
@interface CustomCell:UITableViewCell
{
    UILabel *nameLabel;
    UILabel *colorLabel;
}
@property (nonatomic, retain) IBOutlet UILabel *nameLabel;
@property (nonatomic, retain) IBOutlet UILabel *colorLabel;
 
@end


4. CustomCell.m파일에 추가.
@synthesize nameLabel;
@synthesize colorLabel;
 
-(void)delloc {
    [nameLabel release];
    [colorLabel release];
    [super dealloc];
}


5. CustomCell.xib를 더블클릭해서 인터페이스 빌더를 연다.
- Table View Cell을 라이브러리에서 찾아서 nib 메인윈도우에 끌어 놓는다.
- 테이블 뷰 셀을 선택했는지 확인하고 아이덴티티 인스펙터를 띄우고 ClassIdentify에서 Class를 CustomCell로 변경.
- View size에서 높이를 65로 변경.
- Table View Cell에서 Identifier를 CustomCellIdentifier로 변경
- 라이브러리에서 view를 찾아서 Custom Cell윈도우를 추가 한다.
- View의 크기를 x를 0, y를 0, w를 320, h를 65로 변경
- 라이블러리에서 Label를 4개 추가하여 위치를 맞추고 모양을 결정한다.
- Custom Cell 아이콘에서 드래드 해서 nameLabel과 colorLabel를 아웃렛에 할당한다.
- 이 테이블 셀은 데이터를 보여주기 위해서 사용하지만 사용자와의 상호작용은 테이블 뷰가 수행하므로 독자적인 컨트롤러 클래스가 필요없다.


6. 새 테이블 뷰 셀 사용하기
#import "CustomCell.h"
 
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CustomCellIdentifier = @"CustomCellIdentifier";
    CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:CustomCellIdentifier];
    if(cell == nil)
    {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
        
        for( id oneObject in nib )
            if([oneObject isKindOfClass:[CustomCell class]])
                cell = (CustomCell *)oneObject;
 
    }
 
    NSUInteger row = [indexPath row];
    NSDictionary *rowData = [self.computers objectAtIndex:row];
    cell.colorLabel.text = [rowData objectForKey:@"Name"];
    cell.nameLabel.text = [rowData objectForKey:@"Color"];
    return cell;
}


7. 이 델리케이트 메서드는 셀이 생기기 전에 호출돼서 필요한 값을 셀에서 얻지 못하므로 그 값을 하드코딩해야 한다.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 66;
}


* 그룹으로 묶은 세션과 인덱스로 구분한 섹션
- 인터페이스 빌더에서 추가한 TableView에서 Table View Style을 Grouped으로 변경한다.

### SectionsViewController.h ####
#import <UIKit/UIKit.h>
 
@interface SectionsViewController:UIViewController <UITableViewDelegate, UITableViewDataSource>
{
    NSDictionary *names;
    NSArray *keys;
}
 
@property (nonatomic, retain) NSDictionary *names;
@property (nonatomic, retain) NSArray *keys;
 
@end


### SectionsViewController.m ####
#import "SectionViewController.h"
 
@implementation SectionsViewController
@synthesize names;
@synthesize keys;
 
-(void)viewDidLoad
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"sortednames" ofType:@"plist"];
    NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
 
    self.names = dict;
    [dict release];
 
    NSArray *array = [[names allKeys] sortedArrayUsingSelector:@selector(compare:)];
    self.keys = array;
}
 
-(void)viewDidUnload
{
    self.names = nil;
    self.keys = nil;
}
 
-(void)delloc
{
    [names release];
    [keys release];
    [super delloc];
}
 
#pragma mark -
#pragma mark Table View Data Source Methods
// 세션의 갯수가 몇개인지를 리턴한다.
 
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [keys count];
}
 
// 특정 섹션의 행의 갯수가 몇개인지를 리턴한다.
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSString *key = [keys objectAtIndex:section];
    NSArray *nameSection = [names objectForKey:key];
    return [nameSection count];
}
 
// 테이블 셀 생성
// indexPath에서 section과 row를 뽑아서 딕셔너리의 어떤 값에 해당하는지 찾을때 이용한다.
// section을 사용해서 names Dictionary에서 어떤 배열을 가져올지 알수 잇고 그래서 row를 그 배열에서 어떤 값을 이용할지 알아내는 데 사용할수 있다.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSUInteger section = [indexPath section];
    NSUInteger row = [indexPath row];
 
    NSString *key = [keys objectAtIndex:section];
    NSArray *nameSection = [names objectAtIndex:key];
 
    static NSString *SelectionsTableIdentifier = @"SelectionsTableIdentifier";
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SelectionsTableIdentifier];
 
    if(cell == nil)
    {
        cell = [[[UITableView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SelectionsTableIdentifier] autorelease];
    }
 
    cell.textLabel.text = [nameSection objectAtIndex:row];
    return cell;
}
 
// 각 섹션의 헤더 값을 설정할수 있다.
-(NSString *)viewTable:(UIViewTable *)viewTable titleForHeaderInSection:(NSInteger)section
{
    NSString *key = [keys objectAtIndex:section];
    return key;
}
 
// 다음 메서드를 추가해서 Table View style이 Plain (인덱스 정렬)일 경우 Index를 추가 할수 있다.
-(NSArray *)sectionIndexForTitlesTableview:(UITableView *)tableView
{
    return keys;
}

* 검색 창 구현하기
- 디자인 다시 생각하기
--- 델리케이트와 데이터소스는 수정 가능한 딕셔너리에서 읽고 검색 기준이 바뀌거나 검색을 취소하면 수정 가능한 딕셔너리를 수정 불가능한 딕셔너리의 내용으로 고치면 될듯

* 깊은 뮤터블 복사

- NSDictionary는 NSMutableDictionary 프로토콜을 따르기 때문에 얕은 복사본인 NSMutableDictionary를 리턴한다. 이말은 mutableCopy 메서드를 호출 했을때 원래 딕셔너리가 가진 객체 모두를 가지는 새 NSMutableDictionary 객체를 만드는것을 위미한다.
- 얕은 복사는 해당 메모리 영역을 가리키는 주소 값만을 복사하는것을 의미한다. 보통 얇은 복사를 하게 되면 한 메모리 영역을 두 개의 포인터가 가리키게 되므로, 한쪽에서 할당된 메모리를 해제하면 다른 한쪽에서 사용할 수 있는 메모리 영역을 잃게 되는것이다.
- 깊은 복사는 메모리 영역 전체를 별도로 복사하는것을 의미한다.

* 카테고리 사용
- 카테고리를 사용하면 하위클래스를 만들지 않고 이미 존재하는 객체에 메서드를 추가할 수 있다.

### NSDictionary-MutableDeepCopy.h ####
#import 

@interface NSDictionary (MutableDeepCopy)
-(NSMutableDictionary *)mutableDeepCopy
@end

### NSDictionary-MutableDeepCopy.m ####
#import "NSDictionary-MutableDeepCopy.h"
-(NSMutableDictionary *)mutableDeepCopy
{
	NSMutableDictionary *ret = [[NSMutableDictionary alloc] initWithCapacity:[self count]];
	
	NSArray *keys = [self allKeys];
	
	for( id key in keys )
	{
		id oneValue = [self valueForKey:key];
		id oneCopy = nil;
		
		if([oneValue respondsToSelector:@selector(mutableDeepCopy)])
			oneCopy = [oneValue mutableDeepCopy];
		else if([oneValue respondsToSelector:@selector(mutableCopy)])
			oneCopy = [oneValue mutableCopy];
		
		if(oneCopy == nil)
			oneCopy = [oneValue copy];
		
		[ret setValue:oneCopy forKey:key];
	}
	
	return ret;

}








### SectionsViewController.h ###
#import 

@interface SectionsViewController : UIViewController 
{
	// 검색 결과에 따라서 테이블에게 자신의 내용을 갱신하라고 전달할 필요가 있기 때문에 
	UITableView *table;
	// 검색할 때 필요한 컨트롤
	UISearchBar *search;
	// 원본 데이터가 들어 있는 NSDictionary
	NSDictionary *allNames;
	// 검색된 결과가 들어 있는 NSMutableDictionary
	NSMutableDictionary *names;
	// 검색된 결과가 들어가는 NSMutableArray
	NSMutableArray *keys;
	// 인덱스가 cancel 버튼위에 있는 것을 막기 위해서
	BOOL isSearching;	
}

@property (nonatomic, retain) IBOutlet UITableView *table;
@property (nonatomic, retain) IBOutlet UISearchBar *search;
@property (nonatomic, retain) NSDictionary *allNames;
@property (nonatomic, retain) NSMutableDictionary *names;
@property (nonatomic, retain) NSMutableArray *keys;

-(void)resetSearch;
-(void)handleSearchForTerm:(NSString *)searchForm;

@end


### SectionsViewController.m ###
#import "SectionsViewController.h"
#import "NSDictionary-MutableDeepCopy.h"

@implementation SectionsViewController
@synthesize names;		// NSMutableDictionary
@synthesize keys;		// NSMutableArray
@synthesize table;		// UITableView
@synthesize search;		// UISearchBar
@synthesize allNames;	// NSDictionary

#pragma mark -
#pragma mark Custom Methods
// 이 메소드는 검색을 취소하거나 검색어가 바뀌면 호출된다.
// allNames의 뮤터블 복사본을 만들어서 names에 할당하고 그런 후에 keys 배열을 갠신해서 알파벳의 모든 문자를 포함하게 한다.
-(void)resetSearch
{
	self.names = [self.allNames mutableDeepCopy];
	
	// 검색 결과에 한 섹션의 모든 값이 필요 없다면 그 섹션 자체도 제거해야 하기 때문에 수정가능한 배열로 선언한다.
	NSMutableArray *keyArray = [[NSMutableArray alloc] init];
	
	// 테이블뷰 인덱스 위에 검색창을 나타내기 위한 돋보기 아이콘 추가를 위한 값
	[keyArray addObject:UITableViewIndexSearch];
	
	[keyArray addObjectsFromArray:[[self.allNames allKeys] sortedArrayUsingSelector:@selector(compare:)]];
	self.keys = keyArray;
	[keyArray release];
	
}

// 비록 검색을 검색창 델리게이트 메서드에서 시작되지만 실제 아래 메소드가 검색을 담당한다.
// -(void)searchBarSearchButtonClicked:searchBar
// -(void)searchBar:textDidChange
-(void)handleSearchForTerm:(NSString *)searchTerm
{
	// 이 배열을 나중에 빈 섹션(검색 결과가 없는)을 지울때 사용한다.
	NSMutableArray *selectionsToRemove = [[NSMutableArray alloc] init];
	[self resetSearch];
	
	// 새로 복구한 keys 배열의 모든 키에 빠른 열거를 수행한다.
	for(NSString *key in self.keys)
	{
		// 매번 루프를 돌면서 현재 키에 해당하는 names배열을 구한다.
		NSMutableArray *array = [names valueForKey:key];
		// names 배열에서 지울 값들을 담은 배열을 선업한다.
		NSMutableArray *toRemove = [[NSMutableArray alloc] init];
		
		// 현재 배열의 전체 이름을 훑어보기 위해서 빠른 열거를 수행한다.
		for(NSString *name in array)
		{
			// NSString의 메서드 중 하나인 문자열 안의 부분 문자열의 위치를 리턴하는 메서드를 사용한다.
			// location과 length인 멤버 두개가 있는 NSRange struct인 rangeOfString을 사용한다.
			// 만약 섬색어가 없으면 location은 NSNotFound로 설정된다.
			// NSCaseInsensitiveSearch 옵션을 사용하여 대소문자 구분을 무시한다.
			// 검색된 결과가 없다면 추후에 지우기 위해서 지운는 배열에 추가한다.
			if([name rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location == NSNotFound)
				[toRemove addObject:name];
		}
		
		// 만약 검색된 결과가 전부 없다면 해당 섹션을 삭제하기 위해서 배열에 추가해 놓는다.
		if([array count] == [toRemove count])
			[selectionsToRemove addObject:key];
		
		// 원래 값에서 검색 결과가 없는 배열을 삭제 한다.
		[array removeObjectsInArray:toRemove];
		[toRemove release];
	}
	
	// 필요없는 섹션을 삭제한다.
	[self.keys removeObjectsInArray:selectionsToRemove];
	[selectionsToRemove release];
	[table reloadData];
	
}

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
	
	NSString *path = [[NSBundle mainBundle] pathForResource:@"sortednames" ofType:@"plist"];
	
	NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
	self.allNames = dict;
	[dict release];
	
	[self resetSearch];
	
	// 일반적으로 애플리케이션이 실행될 때는 사용자가 테이블을 눈으로 확인하기 전에 reloadData가 실행된다.
	// 그래서 호출할 필요는 없지만 setContentOffset를 호출하기 위해서는 테이블의 데이터들이 올바르게
	// 로딩된 상태인지를 반드시 확인해야 하기 때문에 reloadData를 호출한다.
	// reloadData가 호출되면 데이터들이 로딩되었다는 것을 보장한다.
	[table reloadData];
	// 테이블의 컨텐츠를 검색창의 높이인 44픽셀만큼 제외하고 계산하다. 그래서 첨에는 검색창이 보이지 않는다.
	[table setContentOffset:CGPointMake(0.0f, 44.0f) animated:NO];
	
    [super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
	// Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
	
	// Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
	// Release any retained subviews of the main view.
	// e.g. self.myOutlet = nil;

	self.table = nil;
	self.search = nil;
	self.allNames = nil;
	self.names = nil;
	self.keys = nil;
	
}


- (void)dealloc {
	[table release];
	[search release];
	[allNames release];
	[keys release];
	[names release];
    [super dealloc];
}

#pragma mark -
#pragma mark Table View Data Source Methods
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
	// 테이블 뷰는 최소한 하나의 섹션을 가져야 하기 때문에 검색된 결과가 하나도 없을수 있기 때문에
	// 검색된 결과가 없을때는 section의 값을 1개로 보장한다.
	return ([keys count] > 0) ? [keys count] : 1;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
	if([keys count] == 0)
		return 0;
	
	NSString *key = [keys objectAtIndex:section];
	NSArray *nameSection = [names objectForKey:key];
	
	return [nameSection count];
	
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
	NSUInteger section = [indexPath section];
	NSUInteger row = [indexPath row];
	
	NSString *key = [keys objectAtIndex:section];
	NSArray *nameSection = [names objectForKey:key];
	
	static NSString *SectionTableIdentifier = @"SectionTableIdentifier";
	
	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SectionTableIdentifier];
	
	if(cell == nil)
		cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SectionTableIdentifier] autorelease];
	
	cell.textLabel.text = [nameSection objectAtIndex:row];
	return cell;
	
}

// 각섹션에 헤더값을 설정할수 있는 메서드
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
	if([keys count] == 0)
		return nil;
	
	NSString *key = [keys objectAtIndex:section];
	// 섹션 헤더 제목을 나타날때 특정 값을 숨겨야 한다.
	// UITableViewIndexSearch 값이 들어온다면 nil 값을 리턴한다.
	if(key == UITableViewIndexSearch)
		return nil;
	
	return key;
}

// 테이블뷰 옆에 인덱스를 추가한다.
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
	// 사용자가 검색창을 사용하고 있다면 nil를 리턴한다.
	if(isSearching)
		return nil;
	
	return keys;
}

#pragma mark -
#pragma mark Table View Delegate Methods
// 사용자가 행을 선택할때 선택되기 전에 미리 호출된다.
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
	// 사용자가 행을 선택하면 검색창에 대한 키보드를 없앤다.
	[search resignFirstResponder];
	isSearching = NO;
	search.text = @"";
	[tableView reloadData];
	return indexPath;
}

#pragma mark -
#pragma mark Search Bar Delegate Methods
// 사용자가 리턴이나 키보도의 search 키를 탭하면 호출되는 메서드
// 검색창을 사용할때는 항상 구현해야 하는 메서드 이다.
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
	NSString *searchTerm = [searchBar text];
	[self handleSearchForTerm:searchTerm];
}

// 사용자가 검색창에 새로운 검색어를 입력할때마다 검색을 진행한다.
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchTerm
{
	// 검색어가 없을경우 초기화를 하고 데이터를 다시 로드한다.
	if([searchTerm length] == 0)
	{
		[self resetSearch];
		[table reloadData];
		return;
	}
	
	[self handleSearchForTerm:searchTerm];
	
}

// 사용자가 cancel을 클릭했을 때 호출되는 메서드
-(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
	// 사용자가 검색이 마치면 isSearching을 NO로 설정하여 테이블뷰에 인덱스가 나오게 한다.
	isSearching = NO;
	// 검색창을 초기화 시키고 키보드를 삭제 시킨다.
	search.text = @"";
	[self resetSearch];
	[table reloadData];
	[searchBar resignFirstResponder];
}

// 검색이 시작되면 isSearching에 YES를 할당하도록 설정
-(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
	isSearching = YES;
	[table reloadData];
}

// 테이블 뷰 옆에 인덱스가 선택된다면 호출되는 메서드
-(NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
	NSString *key = [keys objectAtIndex:index];
	// 만약 돋기기 라면
	if(key == UITableViewIndexSearch)
	{
		// 컨텐츠의 오프셋을 옆에야 한다. 그리고 NSNotFound를 리턴한다면 화면 상단으로 이동한다.
		[tableView setContentOffset:CGPointZero animated:NO];
		return NSNotFound;
	} else 
		return index;
}

@end
사용자 삽입 이미지
: