20111222 :: [iOS] 저장이 가능한 간단 메모장 2 (2/2)

이번 시간에는 지난 글에 이어 앱을 작성된 메모를 테이블뷰에 보여주고, 이를 편집하고 삭제하는 기능을 추가해 보도록 하겠다. 사실 메모장 만들기의 핵심 파일에 데이터를 저장하는 것이고 맨 처음 글과의 차이점은 여러 개의 메모를 객체로 만들어서 파일에 저장하는 방법을 서술했다. 마지막 글은 앱의 모양새를 조금 다듬는 정도가 되겠지만, 테이블뷰 사용에 익숙하지 않은 초보자에게는 좋은 읽을 거리가 될 수 있도록 하겠다.

테이블 뷰

테이블 뷰는 iOS에서 주로 목록을 만들 때 많이 사용하게 된다. 가장 쉬운 예로는 연락처 앱의 메인 UI를 생각할 수 있으며, 조금 다른 모양새이기는 하나 설정 앱도 테이블 뷰를 기반으로 하고 있다. 테이블 뷰는 여러 개의 셀을 나열하는 조금 독특한 형태의 뷰로, 이 테이블뷰를 구현하기 위해 테이블 뷰 컨트롤러는 2가지의 프로토콜을 따라야 한다. 그것은 각각 UITableViewDelegateUITableViewDataSource이다.

데이터소스는 테이블뷰의 각 셀에 표시되는 내용을 만들어주는 역할을 한다. 즉 테이블 뷰에 섹션이 몇개있고, 각 섹션에는 몇 개의 셀이 있으며, 각 셀에는 어떤 내용이 들어가야 하는지를 알려주는 것이다. 테이블 뷰는 자신을 그려야할 때 데이터소스로 지정된 객체에 일련의 메시지를 보내어 자신이 렌더링 되어야 하는 모양을 알게 된다. (그리고 이 때 보내는 메시지들은 UITableViewDataSource에 선언되어 있다.)

테이블뷰 델리게이트는 테이블 뷰의 각 셀을 탭하거나 지우거나 편집하는 등의 액션과 관련이 있다. 단지 테이블뷰가 내용만을 보여주고 별다른 사용자 상호작용을 하지 않는 경우에는 특별히 구현해야 하는 필요가 없다.

이제, 테이블 뷰 컨트롤러로 만들어진 RootViewController를 수정하여 저장된 내용을 보여주고, 셀을 탭하여 기존 내용을 편집하는 방법에 대해 살펴보도록 하겠다.

인터페이스

실제 데이터는 앱 델리게이트가 가지고 있으며, 테이블 뷰 컨트롤러에 필요한 것은 각 셀의 내용을 담는 배열하나 뿐이다. 따라서 인터페이스 부분은 다음과 같이 만들어진다.

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
@interface RootViewController : UITableViewController
{
    NSArray *itemListArray;
    AppDelegate *appDelegate;
}
@end

구현부

테이블 뷰 컨트롤러의 구현 부 중 데이터 소스를 먼저 살펴보자. 데이터 소스는 테이블 뷰를 새로 그릴 때마다 “섹션은 몇 개”이며 “각 섹션에는 셀이 몇 개” 있고, “각 셀은 이러이러하다”는 내용을 테이블 뷰에게 알려주는 메소드들이다. 따라서 이 세가지 메소드는 필수적으로 구현해야 하며 각 메소드의 이름은 다음과 같다.

  • – (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  • – (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  • – (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

이 중 마지막 tableView:cellForRowAtIndexPath: 가 가장 중요한데, 테이블 뷰는 알려진 셀의 개수만큼 루프를 돌면서 이 메소드를 호출하게 되고, 매 호출시 마다 idnexPath에 담긴 정보에 따라 해당 셀을 반환받게 된다.  또 한가지 중요한 것은 테이블 뷰는 아주아주 큰 크기를 가질 수 있는데 예를 들어 5000개 정도의 길이를 갖는 테이블 뷰가 있다고 하면 이 뷰를 채울 테이블 뷰 셀 역시 5000개가 필요하나, 메모리에 큰 부담을 주게 되므로 테이블 뷰는 이미 만들어진 셀을 재활용하게 된다. 따라서 각 셀은 Reuse Identifier를 설정해 주어 이에 맞는 셀을 사용하도록 한다.

스토리보드에서 테이블 뷰의 셀을 선택하고 Reuse Identifier에 Cell 이라고 입력한다. 또한 셀의 스타일은 기본적으로 Custom으로 되어 있는데 따로 디자인을 변경할 것이 아니므로 Basic으로 변경한다. (Basic 은 텍스트라벨 하나만을 표시하는 기본 셀이다.)

다시 RootViewController.m 파일로 돌아와서 각 부를 구현한다. 테이블 뷰 컨트롤러 템플릿으로부터 만들게 되니 아마 필요한 메서드들은 기본적으로 타이핑이 되어 있다. 먼저 뷰가 로딩되었을 때이다.

-(void)viewDidLoad
{
    [super viewDidLoad];
    appDelegate = [[UIApplication sharedApplication] delegate];
    itemListArray = [[NSArray alloc] init];
}

우선 섹션은 1개만 사용하고, 셀의 개수는 메모의 개수와 같다.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    itemListArray = [[appDelegate memoListArray] copy];
    return [itemListArray count];
}

각 셀을 표시하도록 한다.

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"Cell"; //스토리보드에서 입력한 reuse identifier와 동일한 문자열
    UITableViewCell *cell = [tableView deque.... :cellIdentifier];
    // Cell의 내용을 정의
    [[cell textLabel] setText:[[itemListArray objectAtIndex:indexPath.row] title]];

    return cell;
}

상세 뷰에서 새 메모를 추가한 경우, 이를 테이블 뷰에 반영하기 위해서는 뷰가 보여질 때 테이블 뷰의 데이터를 갱신해야 한다.

-(void)viewWillAppear:(BOOL)animated
{
    [self.tableView reloadData];
}

이제 앱을 빌드하고 실행하면 기존에 생성했던 메모들이 표시되고, 새 메모를 작성하고 돌아오면 신규 메모가 리스트의 맨 상단에 표시되는 것을 볼 수 있다.

이번에는 기존 메모를 편집하는 부분이다.  먼저 스토리보드에서 테이블뷰의 셀을 선택해, 이를 디테일뷰와 연결한다. 연결 유형은 push로 한다. 이제 셀을 탭하면 디테일뷰로 이동하게 된다. 그런데 셀을 탭해도 신규 메모를 만드는 화면이 뜨게 되는데 이는 앱 델리게이트의 메모인덱스가 계속 -1 이기 때문이다. 이를 변경하는 절차가 있어야 한다.

기본적으로 테이블 뷰 델리게이트에는 셀을 선택했을 때 동작을 정의하는 메소드가 있지만, 우리는 스토리보드에서 segue를 통해 이동하므로 선택이 되기 직전에 인덱스를 바꾸는 작업을 해야 한다. 따라서 기존에 타이핑이 되어있는 메서드인 tableView:didSelectRowAtIndexPath: 를 지우고 새로운 메소드를 정의한다. 즉 선택이 되기 직전에 호출되는 메소드이다.

-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [appDelegate setMemoIndex:indexPath.row];
    return indexPath;
}

이제 기존 메모를 선택하면 해당 메모의 내용이 표시되는 것을 앱을 실행하여 확인할 수 있다.

다음은 셀을 지우는 부분이다. 이 중 많은 내용은 이미 타이핑이 되어 있을 것이다.

-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(editingStyle == UITableViewCellEditingStyleDelete){
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [[appDelegate memoListArray] removeObjectAtIndex:indexPath.row];
        [appDelegate saveData];
    }
}

여기까지 우리는 간단한 메모를 만들고 이를 편집/삭제하여 외부파일로 저장하는 앱을 만드는 방법을 살펴보았다. 언제가 될지는 모르겠지만 지금까지 만들어본 내용을 코어데이터를 사용하여 만드는 방법도 한 번 포스팅할 생각인데, 역시나 연재라는 건 부담이 크고 나는 너무 게으르기 때문에 그 언제가 언제가 될지는 모르겠다. 긴 글 읽어주셔서 감사하다.