iOS6에서 UICollectionView 시작하기 : Part 1 / 2

Brandon Trebitowski Brandon Trebitowski

이 포스트는 중국어 간체, 영어, 스페인어 언어로도 제공됩니다.

Learn how to use UICollectionView in iOS 6!

Ray로 부터 : 이 글은 iOS6 feast 중 iOS6 네번째 튜토리얼이다. 이 튜토리얼은 우리 책인 iOS6 By Tutorials의 한챕터이다. 내 친구이자 튜토리얼 팀의 새로운 멤버, Brandon Trebitowski에 의해 쓰여졌다. 즐겨주길 바란다!

이 포스트는 iOS 튜토리얼 팀 멤버이자 소프트웨어 개발자, 그리고 brandontreb.com의 운영자,  Brandon Trebitowski에 의해 쓰여졌다.

애플이 iPad를 처음 출시했던 2010년, 디바이스에 내장 되어있는 사진 앱은 굉장히 인상적이였다. 다양한 레이아웃으로 이루어진 사진 디스플레이는 굉장히 유니크하고 스타일리쉬하다.  아래처럼 멋진 그리드 형식으로 볼수도 있고 :

혹은 스택 가장 상단에 있는 사진을 보여주는 앨범 형식의 뷰도 볼 수 있다 :

간단한 핀치 제스쳐 만으로 두 레이아웃을 자유롭게 전환할 수 있다. “우와, 내 앱도 저랬으면 좋겠어!”라고 생각하고 있지 않은가.

그리드 뷰와 다른 레이아웃들을 사용하는 것이 언뜻 쉬워 보이지만  하지만 사실 쫌 까다롭다. 이것은 엄청난 코드들을 필요로 하며 정확히 작동하게 하기도 어렵다. 그렇다면 쉬운 방법은 없을까?

여기 iOS6에서 좋은 소식이 있다. 애플은 UICollectionView라는 새로운 클래스를 소개하였다. 이것은 커스텀 레이아웃과 레이아웃 전환 (포토앱 안 레이아웃처럼) 을 놀라울 정도로 심플하게 만들 수 있게 해준다.

UICollectionView는 완전하게 커스터마이즈 할수 있기 때문에 스택과 그리드의 제한이 없다. 따라서 원형 레이아웃, 커버플로어 스타일 레이아웃 혹은 완전히 새로운 레이아웃이든 어떠한 레이아웃도 만들 수 있다.

요점은, 정렬된 데이터를 유저에게 보여줄 수 있는 파워풀한 새로운 방식이므로 반드시 배워야 한다는 것이다! 이것은 UITableView만큼이나 중요하고 또 도움이 많이 될 것이다. 좋은 소식 하나는 만약 UITableView에 익숙하다면 이 역시 어렵지 않을 것이다. 이것 역시 테이블뷰 데이터 소스와 델리게이트 패턴이 비슷하게 쓰인다.

이 튜토리얼에서는 UICollectionView를 이용하여 그리드 형식의 포토 브라우징 앱을 직접 만들어 볼 것이다. 튜토리얼이 끝날 때 쯤엔 UICollectionView의 기초를 알고 또 놀라운 기술을 자기 자신의 앱 안에서도 사용할 수 있게 될 것이다.

UICollectionViewController 파헤쳐보기

먼저 아주 기초적인 예제부터 봐 보자. UICollectionViewController 안에는 아래와 같은 몇가지 중요요소들이 있다.

한개씩 차근차근 알아보자 :

    1. UICollectionView – UITableView와 비슷하게 메인뷰에 보여지는 컨텐츠이다. 이것이 viewController안에 꽉차게 위치 할 필요가 없다는 것을 명심해라! 위 스크린샷을 보면 collectionview 위에 “search” 를 위한 부분이 있다.
    2. UICollectionViewCell – UITableView의 UITableViewCell과 비슷하다. 셀들은 뷰의 컨텐츠들로 이루어져 있고 UICollectionView의 subview로 더해진다. 셀은 코드로, 인터페이스 빌더로 혹은 두가지 방법을 모두 사용하여 만들 수 있다.
    3. Supplementary Views -만약 보여져야 할 내용이 많다면 이걸 꼭 쓸 필요는 없지만 UICollectionView를 쓸 때 가끔은 supplementary view를 써야 할 때도 있다. 이 뷰는 대부분 헤더나 풋터에 쓰인다.
    4. Decoration View – UICollectionView의 비쥬얼 적인 모습을 더 보여주고자 한다면 decoration view를 사용 할 수 있다.(하지만 유용한 데이터를 담고 있진 않다)배경이나 시각적 장식들이 decoration View의 좋은 예이다.
        위 시각적 요소들에 더 보태자면, UICollectionView는 보이지는 않지만 컨텐츠를 위차하는걸 돕는 요소들도 있다:
    1. UICollectionViewLayout – UICollectionView 자체는 화면에 cell들을 정렬해 주지 못한다. 그 작업을 하는 것이 바로 UICollectionViewLayout 클래스이다. 이 클래스는 cell들의 위치를 잡아주기 위해 UICollectionView의 델리게이트 메소드들을 사용한다. 레이아웃들은 런타임동안 바뀔수 있고 UICollectionView는 애니메이션을 사용하여 원래 레이아웃에서 다른 레이아웃으로 자동 스위칭이 되게 할 수도 있다.
    2. UICollectionViewFlowLayout – 커스텀한 레이아웃으로 UICollectionViewLayout을 만들어 사용할수있다 ( 이 내용은 다음 튜토리얼에서 다룰 예정이다) 하지만 애플에서는 고맙게도UICollectionViewLayout이라는 flow-based레이아웃을 제공하고 있다.이것으로 마치 그리드 뷰처럼 요소들을 사이즈별로 정렬한다. 이 레이아웃을 박스로 혹은 섭클래스로 사용함으로써 흥미로움과 비쥬얼적인 효과를 줄 수 있다.

이 튜토리얼과 그 다음 튜토리얼을 통해 이 것들을 더 깊이 배워보겠다. 하지만 지금은 프로젝트를 직접 만들어 볼 시간이다!

FlickrSearch 소개하기

이 후 튜토리얼에서는 FlickrSearch 란 멋진 포토 브라우징 앱을 만들 것이다. 이 앱에서는 Flickr 사이트 내에 사진을 검색하고 매칭되는 사진을 다운로드하여 코르크보드 테마의 그리드 뷰 위에 보여 줄 것이다:

시작하기 전, 이 튜토리얼중 쓰일 assets를 다운받아야 한다. 이것들 없이는 만들기 꽤 까탈스러울 것이다.

자 이제 준비되었나? Xcode를 열고 FileNewProject… 그리고 iOSApplicationSingle View Application 템플릿을 선택한다.

이 템플릿은 시작하기 위한 간단한 UIViewController와 스토리 보드를 제공할 것이다. 좋은 스타트다!

Next를 클릭하고 어플리케이션에 관한 정보를 입력하자. Product Name은 FlickrSearch, 디바이스 타입은 iPad, 그리고 Use StoryboardUse ARC 박스를 반드시 체크하자. Next를 눌러 프로젝트 저장할 위치를 선택하고 Create를 한다.

컴파일을 하고 런을 해 보면 아마 그냥 빈 싱글 뷰만 보이는 단순한 어플리케이션을 보게 될 것이다.

다음, assets을 임포트 해야한다- 미리 다운받아 놓은 압축을 푼 Assets 을 드래그하여 넣는다. 이때 Copy items into destination group’s folder (if needed) 가 체크되어있어야 한다. 그리고 Finish를 클릭하자

Pinning Up The Corkboard

이제 기본이지만 스타리쉬해 보이는 디자인을 시작 할 것이다. 간단한 디자인을 한 뒤 UICollectionView를 더해 나갈 것이다.

스토리보드를 열기 전에, 인터페이스 아이템들과 연결될 IBOutlet 과 IBAction 들을 선언해 줄 것이다.ViewController.m을 열고 밑의 코드와 같이 @interface 안을 코딩해 준다:

@interface ViewController () 
@property(nonatomic, weak) IBOutlet UIToolbar *toolbar; 
@property(nonatomic, weak) IBOutlet UIBarButtonItem *shareButton; 
@property(nonatomic, weak) IBOutlet UITextField *textField;
- (IBAction)shareButtonTapped:(id)sender; 
@end

또 ShareButtonTapped: 를 파일 끝부분에 써준다 (내용은 나중에 채워넣을 것이다) :

-(IBAction)shareButtonTapped:(id)sender { 
    // TODO
}

이outlet과 action 들은 ViewController에서 보여져야 하기때문에 implementation 파일 안에서 선언되어진다. 자 이제 연결할 차례다

MainStoryBoard.storyboard를 열어라. 오브젝트 라이브러리에서 Toolbar (오른쪽 아래 3번째 탭에 있다.) 를 메인뷰로 드래그 하고 더블클릭하여 텍스트를 “Share” 로 바꿔줘라 (아니면 attributes inspector에서 title 속성을 바꿔라)

다음은 “share button”을 control을 누른채 왼쪽에 있는 ViewController 로 드래그 한다. 리스트 중 shareButtonTapped: 를 선택하여 버튼과 메소드가 연결 되도록 한다.

다음, 검색 라벨과 검색 박스를 더해줘라. 메인뷰에 imageview를 넣고 이미지 속성을 “search_text.png”로 해줘라. 지금보기엔 이미지가 엉망이지만 바꿀 수 잇다.  mode속성을 center로 바꾸고 툴바 바로 아래에 배치시키자. 다른 방법으로는 Editorsize에서 컨텐츠에 맞게 이미지 뷰의 사이즈를 변경하여 맞추어 줄 수도 있다.

Note: 왜 라벨 대신 이미지 뷰를 쓰는 이유가 궁금할 수 있다. 이 부분에서는 텍스트의 특별한 모습을 보여주고자 한다. 이미지를 사용 함으로써 개발자가 보여주고자 하는 텍스트의 모습으로 보여준다. 명심해라. 이 방법은 지역화(localization)을 힘들게 만들수도 있다. 하지만 만약 한 언어만 지원 할 생각이라면 원하는 모습을 보여주기위한 가장 쉬운 방법이다.

검색창을 위해 textField를 뷰 위로 드래그 하고 검색라벨 오른쪽에 위치하게 한다. 이때 border 스타일은 none (inspector에 점선으로 둘러쌓여있는 아이콘)으로 해준다.나중에 코드로 커스텀화 할 것이다.
텍스트필드 삽입 후, Control을 누른 상태에서 텍스트 필드를 왼쪽 사이드바에 있는 viewController로 드래그 한 뒤 “delegate”를 선택하여준다. 이를 통해 ViewController 클래스안에 텍스트필드의 델리게이트를 생성해 주고 리턴 선택시 키보드가 사라지는 코드를 작성할 수 있다.

또 다른 이미지 뷰를 넣어 검색창 아래에 결과창과 구분되는 라인을 넣어주자. 이미지의 property는 divider_bar.png로 설정하고 알맞게 사이즈를 정하고 센터에 위치하게 한다(혹은 컨텐츠에 딱맞게 사이즈를 조정한다). 이제 아래와 같은 인터페이스 처럼 보일 것이다:

마지막 단계는 IBOutlet들과 연결해주는 것이다. 왼쪽 사이드바에 있는 ViewController를 클릭하고 Connections Inspector을 선택한다.(오른쪽 위 사이드바에 가장 마지막 탭) 그리고 만들어 놓은 IBOutlet들(shareButton, textField, toolbar)을 드래그하여 각각의 인터페이스 속성들과 연결시킨다.

이제 드디어 재미있는 파트다. 스토리보드를 덜 따분하게 꾸며줄 것이다. ViewController.m을 열고 아래의 코드를 viewDidLoad:의 끝부분에 넣어준다.

self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"bg_cork.png"]];
UIImage *navBarImage = [[UIImage imageNamed:@"navbar.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(27, 27, 27, 27)];
[self.toolbar setBackgroundImage:navBarImage forToolbarPosition:UIToolbarPositionAny
barMetrics:UIBarMetricsDefault];
UIImage *shareButtonImage = [[UIImage imageNamed:@"button.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(8, 8, 8, 8)];
[self.shareButton setBackgroundImage:shareButtonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
UIImage *textFieldImage = [[UIImage imageNamed:@"search_field.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
[self.textField setBackground:textFieldImage];


UIColor:colorWithPatternImage 메소드로 간편히 뷰의 배경을 코르트보드 이미지로 바꿔주고 toolbar, share button 그리고 textfield 이미지들을 세팅해준다.
빌드하고 실행해 보면 아래와 같은 유저 인터페이스가보일것이다:

나쁘지 않다!이제 시작하기 좋은 환경이 구축되었다. 여러 가지 멋진 사진들을 붙이고 싶게 만드는 게시판처럼 보인다. 남은 튜토리얼에서는 UICollectionView를 이용해 이것을 앱답게 만들 것이다.

Fetching Flickr Photos

이 섹션에서 처음 할일은 섹션의 이름을 빠르게 10번 말하기다. 물론 농담!
Flickr 는 멋진 이미지 공유 서비스다. 누구나 접근할수 있고 쓰기 쉬운 엄청 심플한 API다. 이 API로 사진을 찾고 더하고 리플을 다는 등 여러가지를 할수 있다.
Flickr API를 사용하기 위해 먼저 API key가 필요하다 .만약 실제 프로젝트중이라면 http://www.flickr.com/services/api/keys/apply/에 가입하는 것을 추천한다.
하지만 테스트용 프로젝트 중이라면 Flickr는 가입절차 없이 여러명이서 사용 가능한 샘플 키가 있다. http://www.flickr.com/services/api/explore/?method=flickr.photos.search로 가서 아무거나 검색한 뒤 맨 아랫줄에 있는 URL에서 API키 값을 가져온다 – “&api_keuy =”부터 다음 “&”까지의 값이다.

예를들어 아래와 같은 URL이 있다면:
http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=6593783 efea8e7f6dfc6b70bc03d2afb&format=rest&api_sig=f24f4e98063a9b8ecc8b522b238 d5e2f
API키는 : 6593783efea8e7f6dfc6b70bc03d2afb  이다.

Note: 만일,샘플 API키를 사용한다면 이것이 주기적으로 바뀐다는 것을 명심해야한다. 그러므로 만약 며칠에 걸쳐 이 튜토리얼을 한다면 자주 키를 찾아야 할 것이다. 이러한 이유때문에 오래동안 이 프로젝트를 할 예정이라면 자기자신의 Flickr API 키를 받는게 더 나을 것이다.

이 튜토리얼은 UICollectionView에대한 것이지 FlickrAPI에 관한것이 아니기 때문에 클래스로 구성된 요약된 Flickr 코드를 제공 할 것이다. 여기서 다운 받아라.
4개의 파일을 프로젝트에 드래그 해라. 이때  Copy items into destination group’s folder (if needed)를 반드시 체크하고 finish를 클릭한다.
두개의 클래스가 임포트 된다 :

  • Flickr : 사진 검색 기능과 Flickr 사진 정렬을 리턴시키는 블락으로 구성 된 API를 제공한다.
  • FlickrPhoto : Flickr로부터 받는 사진 데이터- 섬네일, 이미지 그리고 아이디와 같은 메타데이터들

부담갖지 말고 코드 보길 바란다. 이건 굉장히 심플하고 프로젝트에서 Flickr를 사용하기 쉽게 도와줄 것이다!
이제 준비 되었다면 다음 섹션으로 넘어가자. 이제 Flickr 사용전 몇가지 준비를 해보겠다.

 데이터 스트럭쳐 준비하기

이제 매 검색 후에 검색결과를 collectionView위에 새로운 섹션으로 뜨게 하는 디자인을 할 것이다. (이것이 이전 섹션을 바꾸는것보다 더 심플하다) 다시 말해, 만약 당신이 “ninjas”라고 검색 하고 “pirates”를 검색한다면 ninjas에 대한 섹션과 pirates에 관한 섹션이 tableview에 생길 것이다. 이제 어떻게 구현해 나갈 것인지 이야기해보자.
먼저 각각 섹션에 대한 데이터를 저장할 수 있는 데이터 스트럭쳐를 만들어야 한다. 만약 NSMutableDictionary를 생각하고 있다면 정답이다! 딕셔너리의 key는 검색단어 가 될 것이고 value 는 검색단어와 연관되는 이미지들, FlickrPhoto의 정렬 일 것이다.

검색단어와 결과가 저장될 수 있는 array와 dictionary, 그리고 검색할 Flickr 객체도 만들자. ViewController.m을 열고 아래의 클래스를 임포트 하자:

#import "Flickr.h" 
#import "FlickrPhoto.h"

다음 @interface  안에 property들을 선언해 준다.

@property(nonatomic, strong) NSMutableDictionary *searchResults; 
@property(nonatomic, strong) NSMutableArray *searches; 
@property(nonatomic, strong) Flickr *flickr;

그리고 ViewDidLoad 끝에 아래와 같이 property들을 초기화 해준다.

self.searches = [@[] mutableCopy]; 
self.searchResults = [@{} mutableCopy]; 
self.flickr = [[Flickr alloc] init];

searches is an array that will keep track of all the searches made in the app, and searchResults will associate each search term to a set of results.
Next up, you’ll learn how to populate these properties based on the user’s input.

searches 정렬은 모든 서치에 관한 이력을 저장할 array이고 searchResults 는 각각 검색의 set들과 연관있다.
다음으로는 유저의 입력을 기본으로 한 property들을 나타내는 방법에 대해 배워보겠다.

좋은 결과 얻기

Flickr 검색 하기 전에 먼저 API key값을 넣어야 한다. Flickr.m을 열고 kFlickrAPIKey대신에 전에 얻은 API key값을 넣어주자. 아래와 같은 모습이여야 한다.

#define kFlickrAPIKey @"ca67930cac5beb26a884237fd9772402"

이제 Flickr 검색할 준비가 되었다! ViewController.m으로가서 아래의 코드를 파일 아랫부분에 넣어주자!( 물론 @end 위에)

#pragma mark - UITextFieldDelegate methods
- (BOOL) textFieldShouldReturn:(UITextField *)textField {
    // 1
    [self.flickr searchFlickrForTerm:textField.text completionBlock:^(NSString *searchTerm, NSArray *results, NSError *error) {
    if(results && [results count] > 0) {
        // 2
        if(![self.searches containsObject:searchTerm]) {
            NSLog(@"Found %d photos matching %@", [results count],searchTerm);
            [self.searches insertObject:searchTerm atIndex:0];
            self.searchResults[searchTerm] = results; }
            // 3
            dispatch_async(dispatch_get_main_queue(), ^{
            // Placeholder: reload collectionview data
            }); 
        } else { // 1
        NSLog(@"Error searching Flickr: %@", error.localizedDescription);
    } }];
    [textField resignFirstResponder];
    return YES; 
}

유저가 키보드에서 엔터를 칠때, 이 함수가 불러져 올 것이다. ( 왜냐하면 전에 만들어놓은 viewController가 textField의 델리게이트이기 때문이다.). 코드에 대해 설명하자면,

  1. 제공된 Flickr 사진 검색 클래스를  사용하여 검색 단어와 매칭되는 사진을 비동기식으로 나타내준다. 검색이 완료 되었을때 completion block이 검색단어와 결과값 Flickr사진들, 그리고 에러를 참조한다.
  2. 또, 전에 같은 검색어를 사용했는지 체크해 준다. 만약 아니라면 searches 정렬의 가장 상단에 더해주고 searchResults 딕셔너리에서는 검색어 즉, key와 함께 검색어를 저장한다.
  3. 이 단계에서는 새로운 데이터를 가져오고 UI를 새로고침 해주어야한다. CollectionView에서 새로운 데이터를 반영하기 위해 리로드 해주어야 한다. 하지만 아직 우리는 collectionView를 implement하지 않았으므로 일단 코맨트로만 남겨두자.
  4. 마지막으로 로그들과 에러를 콘솔창에 띄어준다. 실제 앱에서 이것들은 유저에게 보여져야 한다.

계속하여 이제 앱을 실행해보자. 텍스트박스에서 검색을 하게되면 콘솔창에 검색 결과의 수를 보여주는 긴 메세지가 보일 것이다:

2012-07-10 21:44:16.505 Flickr Search[11950:14f07] Found 18 photos matching 1337 h4x
2012-07-10 21:44:32.069 Flickr Search[11950:14f0b] Found 20 photos matching cat pix

로드 시간을 줄이기 위해 Flickr 클래스에 의해 결과는 20개 까지로 제한되어 있다.
이제 보여줄 사진들의 리스트를 얻었다. 이제 UICollectionView를 사용하여 스크린에 뿌려줄 차례다.

UICollectionView 준비하기

알다시피 UITableView를 사용할때, 데이터와 이벤트를  다루기위해 data source와 delegate를 세팅해야한다.
비슷하게 UICollectionView를 사용할 때도 data source와 delegate를 생성해야 한다. 그리고 다음과 같은 룰을 따른다.

  • data source(UICollectionViewDataSource)는 CollectionView 그리고 그 뷰들의 아이템 수에 대한 정보를 리턴시킨다.
  • delegate (UICollectionViewDelegate)는 셀이 선택되거나, 하이라이트 되거나 지워졌을 때와 같은 이벤트가 나타났을 때를 알려준다.

그리고 UICollectionView에 있는 새로운 기능을 통해 세번째 프로토콜을 사용할수 있습니다. 이 프로토콜은 collectionView사용시에 레이아웃을 관리할 수 있게 해준다. 이 튜토리얼에서는 미리 만들어진 UICollectionViewFlowLayout 레이아웃 매니져를 사용 할 것이기 때문에 UICollectionViewDelegateFlowLayout 을 반드시 상속받아야 한다. 이 것을 통해 layout을 마음대로 조정할수 있다. 예를 들어 셀 간격, 스크롤 방향 등등.
이 섹션에서는 viewContoller에 있는 UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelateFlowLayout을 상속받아  collection view가 제대로 작동되도록 코딩 할 것이다.
먼저, ViewController.m 내 가장 위에 @interface 안에 UICollectionViewDelegate와 UICollectionViewDataSource 프로토콜을 상속받는 코드를 작성한다. @interface 라인은 다음과 같이 보여야한다:

@interface ViewController () <UITextFieldDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>

Note: 아마 UICollectionViewDelegate가 아니라 UICollectionViewDelegateFlowLayout이 왜 리스트 되있는 궁금할 것이다. 그 이유는 UICollectionViewDelegateFlowLayout은 사실 UICollectionViewDelegate의 sub-protocol(UICollectionViewDelegate를 상속받은 protocol)이기때문에 둘 다 리스트화 할 필요 없다.

자 이제 protocol들을 코딩해보자!

UICollectionViewDataSource

먼저 Datasource부터 하자. 아래의 코드를 viewController.m아래 부분에 넣자.

#pragma mark - UICollectionView Datasource
// 1
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section {
    NSString *searchTerm = self.searches[section];
    return [self.searchResults[searchTerm] count]; 
}
// 2
- (NSInteger)numberOfSectionsInCollectionView: (UICollectionView *)collectionView {
    return [self.searches count]; 
}
// 3
- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"FlickrCell " forIndexPath:indexPath];
    cell.backgroundColor = [UIColor whiteColor];
    return cell; 
}
// 4
/*- (UICollectionReusableView *)collectionView:
(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
    return [[UICollectionReusableView alloc] init];
}*/

좋다. 근데 이 메소드들은 무엇인가? 내가 장담하는데 다 유용한 것이다. 하나씩 보자:

  1. collectionView:numberOfItemsInSection: 주어진 섹션에 나타낼 셀의 수를 리턴해 준다. 이 앱에서는 각 검색어가(그리고 사진 결과 리스트들) 각자의 섹션 안에 있다. 그러므로 이 메소드는 먼저 searches array안에서 검색어를 찾고 결과값에서 사진결과를 찾는다.
  2. numberOfSectionsInCollectionView: 이름에서 알 수 있듯이 전체 섹션의 수를 리턴시켜준다. 이것은 searches 의 전체 수를 리턴 시켜준다.
  3. collectionView:cellForItemAtIndexPath:는 주어진 index path에서의 셀을 리턴 시켜주는 메소드이다. 테이블뷰 셀과 마찮가지로, collection view cell들도 reuse identifier를 이용하여 queue와 dequeue를 해준다.  여기에서 주어진 reuse identifier로 특정 셀 클래스가 어떻게 등록되는지 볼 수 있을 것이다. UITableViewCell과는 달리, UICollectionViewCell은 default 셀 스타일이 없다. 그러므로 이 셀의 레이아웃은 당신에게 달려있다. 지금은 그냥 빈 UICollectionViewCell을 리턴해주자.
  4. collectionView:viewForSupplementaryElementOfKind:atIndexPath 는 괴상한 시그니쳐들이 있음에도 매우 간단하다. 이 메소드는 각 세션의 UICollectionView의 해더와 풋터뷰를 리턴해준다 “kind” 변수는 클래스가 요청하는 view(헤더 혹은 풋터)를 알려주는 NSString 값이다. 여기까지 코드 작성한 경우 아마 이슈가 발생할 것이다. 하지만 나머지 튜토리얼 안에서 다시 코딩 할 것이다!

UICollectionViewDelegate

이제 UICollectionViewDataSource가 완성되었으니, UICollectionViewDelegate에 집중해 보자. ViewController.m 아랫부분에 다음 코드를 넣어라.

#pragma mark - UICollectionViewDelegate
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath 
{
    // TODO: Select Item
}
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
    // TODO: Deselect item
}

이제부터  이 메소드 들을 그대로 놔 둘 것이다.. 주석에 써져있듯이, 이 메소드들은 셀을 선택할때 혹은 선택을 해제할 때 실행된다. UICollectionView가 다중선택을 허락할때만이 collectionView:didDeselectItemAtIndexPath: 가 불린다는 것을 명심해라. 나중에 직접 이것을 볼 수 있을 것이다.

UICollectionViewFlowLayoutDelegate

섹션 앞쪽에 말했듯이 모든 UICollectionView는 레이아웃과 관련잇다. 우리는 이 프로젝트에서 이미 준비된 UICollectionViewFlowLayout을 사용했다. 왜냐하면 쓰기 좋고 쉬우며 또 그리드뷰 형식을 제공하기 때문이다.
ViewController.m 마지막에 아래의 코드를 넣어보자 :

#pragma mark – UICollectionViewDelegateFlowLayout
// 1
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    NSString *searchTerm = self.searches[indexPath.section]; FlickrPhoto *photo =
    self.searchResults[searchTerm][indexPath.row];
    // 2
    CGSize retval = photo.thumbnail.size.width > 0 ? photo.thumbnail.size : CGSizeMake(100, 100);
    retval.height += 35; retval.width += 35; return retval;
}
// 3
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(50, 20, 50, 20); 
}

사실 이것보다 더 많은 델리게이트 메소드가 상속될 수 있다. 하지만 이 프로젝트를 위한 것들은 이것들이 전부다.

  1. collectionView:layout:sizeForItemAtIndexPath 는 주어진 셀의 사이즈를 리턴해준다. 이 메소드를 위해 찾고있는 FlickrPhoto를 먼저 정의해 줘야한다. 각각의 사진들이 다른  치수를 가지고 있을 수 있기 때문이다.
  2. 세 연산자가 리턴된 사이즈를 정의하기 위해 사용된다. 왜냐하면 Flickr 사진들이 비동기식으로 로딩되기 때문에 생길 수 있는 이슈들 때문이다. 이 말은 가끔 사진이 nil일수도 혹은 가로세로 0 일수 있다는 뜻이다. 이 경우에는 100*100 사이즈의 빈 사진이 띄워질 것이다. 마지막으로 35px씩 높이가 더해 줌으로써 사진은 멋진 테두리를 가질 수 있게 된다.
  3. collectionView:layout:insetForSectionAtIndex: 셀, 헤더, 그리고 풋터간의 간격을 리턴해 준다.

이 기본적 스트럭쳐를 가지고 UICollectionview를 시작할 것이고 이 모든것들은  subview들과 관련있다.

UICollectionView와 친구들

UICollectionView의 뛰어난 점 중 하나는 tableview와 유사하다는 것이다. 애플은 storyboard 에디터로 CollectionView를 시각적으로 정말 쉽게 세팅 할 수 있도록 만들었다. UICollectionView들을 viewController에  drag & drop 할 수 있고 스토리보드 에디터를 이용해 UICollectionViewCell의 레이아웃도 디자인할 수 있다. 어떻게 하는지 보자.

UICollectionView 더하기

스토리 보드에 collection view더하기 전에 참조할수 있는 IBOutlet을 생성해야한다. ViewController.m에 다음 코드를 @interface 섹션안에 넣어라 :

@property(nonatomic, weak) IBOutlet UICollectionView *collectionView;

이제 MainStoryboard.storyboard 를 열고 object library에 있는 Collectionview 객체를 드래그 하여 넣어보자. (주의: CollectionViewController가 아니다)  라인 이미지 바로 아래부터 하여 꽉차게 위치하도록 하자:

나는 위치를 잘 보이게 하기위해 collectionView의 배경을 파란색으로 했지만 배경색을 transparent/clear로 지정해라. 그렇지 않으면 코르크보드의 배경이 보이지 않을 것이다.


다음, collectionview 의 델리게이션과 데이터 소스 property를 만들어야한다.inspector에 있는 viewController객체에 collectionView를 ctrl+드래그 해주고 datasource를 선택한다. 같은 방법으로 delegate도 선택해 준다.
마지막으로, scene inspector에서 viewController 를 선택하고 connections inspector를 열어라. (오른쪽 위 사이드바에서 가장 마지막탭 혹은 viewUtilitiesshow connections inspecter 클릭) CollectionView 에서 스토리 보드 안의 collectionview 로 드래그 해서 연결이 되도록 한다.
자 이제 커넥션이 만들어 졌다. 이제 데이터가 화면에 보여지는게 보고싶을 것이다. 다행히도 딱 두 스텝만이 남았다. 첫번째는 cell을 만들기 위한 클래스를 UICollectionView에 선언해 주는 것이다.
viewController.m 안에 viewDidLoad의 끝부분에 다음 코드를 넣어라:

[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"FlickrCell"];

이제, collectionView가 cell을 만들려고 할때 항상 UICollectionViewCell 클래스가 쓰인다.  커스텀된 셀을 사용 할때는 이부분을 커스텀 된 셀로만 바꾸어 주면 되지만 지금은 빠르게 넘어가 보기로 한다.
마지막 스텝은 새로운 결과가 나왔을 때, collectionView를 새로 로드해 주는 것이다. ViewController.m 안에 있던 textFiledShouldReturn: 으로 돌아가 “// Placeholder: reload collectionview data” 를 아래 코드로 바꿔줘라:

[self.collectionView reloadData];

이제 빌드 하고 실행하고 검색해 봐라. 검색하면 하얀 박스들이 띄워지는 것을 볼 것이다. 다중검색 할 시에는 hearder가 들어갈 자리에 갭이 생겨있을 것이다.

축하한다! 이제 collectionView는 각각의 줄에 대한 결과를 보여주고 있다!
결과값이 보고 싶은 맘과는 다르게 아직 앱은 검색에 대한 진짜 이미지는 보여주고 있지 않다. 이제 이미지들은 불러오고 커스텀된 UICollectionViewCell위에 나타나게 할 시간이다.

커스텀 UICollectionViewCell 만들기

기본적으로, UICollectionViewCell은  배경색을 바꾸는 것을 넘어서는 다른 커스터마이즈는 허락이 되지 않는다.하지만 아무래도 자기 자신만의 UICollectionViewCell 클래스를 만들고 싶을 것이다.
시작하기 전에 ViewController.m 안 viewDidLoad 안에 아래코드를 넣자.

[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"FlickrCell"];

이것은 스탭중 하나이며 아마 에러를 낼 것이다

Filenewfile..로 가서 iOSCocoa TouchObjective-C 클래스 탬플릿을 선택하고 Next를 클릭한다. 클래스의 이름을 FlickrPhotoCell 이라 하고 subclass를 UICollectionViewCell로 세팅한다음 Next를 클릭하자. 마지막으로 위치를 지정하여 파일을 저장하고 Create를 누른다.
FlickPhotoCell은 Flickr로부터 받은 이미지를 보여주는 UIImageView가 있는 싱글 섭뷰로 이루어 질 것이다. 유저인터페이스를 만들기 전에 class를 보자. FlickrPhotoCell.h 내 소스를 아래와 같이 바꿔보자:

#import <QuartzCore/QuartzCore.h>
@class FlickrPhoto;
@interface FlickrPhotoCell : UICollectionViewCell
@property (nonatomic, strong) IBOutlet UIImageView *imageView; 
@property (nonatomic, strong) FlickrPhoto *photo;
@end

UIImageView outlet은 public으로 생성하였다 왜냐하면 다른 클래스에서도 사진이 비동기식으로 로딩된 후 이미지를 디스플레이 해 줄 수 있어야 하기 때문이다. 또 나중에 사진 정보를 필요로 하기때문에, 뿌려줄 사진에 대해서도 참조를 해주어야 한다. 자 이제 뷰를 만들 준비가 되었다.
 메인뷰에 UICollectionView를 더해줄때 인터페이스 빌더가 UICollectionViewCell을 자동으로 생성한다. 인터페이스 빌더 안에있는 MainStoryboard.xib를 선택해 보아라. Collection View를 선택하면 가려져있던 셀들이 보일 것이다.그 안에는 셀의 class와 identifier를 세팅하기 위한 준비된 두 스텝이 있다.
CollectionViewCell를 클릭하고 identity inspector을 열어라. 클래스 박스안에 FlickrPhotoCell이라고 입력해라. 


자 이제 Attributes Inspector를 열고 identifier박스에 FlickrCell이라고 적어넣자. 이것은 cellForItemAtIndexPath메소드에서 쓰이는 reuse identifier이다.


다음으로는 cell을 드래그하여 대략 300×300 사이즈로 조정해준다. 델리게이트로 다이나믹하게 사이즈 조정이 되므로 실제 사이즈는 상관없다.

메인뷰에 이미지뷰를 그래그해 넣어준다. 셀뷰에 맞게 사이즈 조정을 해주자. layout이 정확히 보여지기위해 블루 가이드에 모든 사이드가 잘 맞게 조정하여라. 더 정확한 레이아웃을 위해 user constraint를 하나 더해줘야 한다. imageView를 선택 후 user constraint icon 선택, 그리고 “Bottom space to superview” 를 선택해라

자 이제 Attribute inspector를 열고 모드를 Flickr사진이 알맞도록 Aspect Fit으로 바꿔보자.
왼쪽 사이드 바 (Connections Inspector) 에서 FlickrPhotoCell를 선택하여라 그리고 UIImageView에서 imageView 아울렛을 드래그 하여 연결 시키자.
커스텀 뷰를 만드는 마지막 단계는 상단에 pushpin을 삽입하는 것이다 . 뷰에 또다른 imageView를 드래그 하여 넣고 셀의 가장 위 센터에 위치하게 한다. 그리고 Attributes Inspector에서 모드를 center로 바꾼뒤 이미지를 pushpin.png로 변경하여라. 그 결과물은 아래와 같다 :


마무리로는 이 뷰위에 컨텐츠를 함께 띄워져야 한다.


우선, UICollectionView에서  기본 UICollectionView 클래스 대신에 우리가 만든 FlickrPhotoCell 클래스로 바꿔줘야한다. ViewController.m을 열어서 아래 임포트를 더해줘라:

#import "FlickrPhotoCell.h"

그리고 collectionView:cellForItemAtIndexPath: 안을 다음과 같이 바꿔준다 :

- (UICollectionViewCell *)collectionView:(UICollectionView *)cv cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    FlickrPhotoCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"FlickrCell" forIndexPath:indexPath];
    NSString *searchTerm = self.searches[indexPath.section]; 
    cell.photo = self.searchResults[searchTerm]        
    [indexPath.row];
    return cell; 
}

이 코드에서 우선적으로 알수 있는 것은 UIColectionViewCell 대신 FlickrPhotoCell이 dequeue된다는 것이다. 이것은 스토리보드 내에서 FlickrCell identifier를 이용해 만들었다는 것을 보여준다.다음으로 참조하는 사진을 정의 하고 사진 속성에 따라 세팅해주어야한다.

이제 앱을 빌드하고 실행하여 검색해 보면 결과물을 볼 수 있을 것이다.


자, 이제 원했던 결과물과 많이 가까워 졌다. 이제 적어도 커스텀된 UICollectionViewCell을 사용할 수 있게 되었다. 하지만 왜 사진은 안뜨는 걸까?
그 이유는 셀의 사진속성을 설정할 때 UIImageView의 이미지를 업데이트 안해줬기 때문이다. 이것을 고치기 위해 FlickrPhotoCell에 있는 사진 property의 setter를 오버라이드 받아야 한다. 먼저 아래의 코드를 FlickrPhotoCell.m 맨 위에 넣어줘러.

#import "FlickrPhoto.h"

그리고 파일 가장 아래에 다음 코드를 넣어라 (물론 @end 위에):

-(void) setPhoto:(FlickrPhoto *)photo { 
    if(_photo != photo) {
        _photo = photo; 
    }
    self.imageView.image = _photo.thumbnail; 
}

그리고 앱을 돌려 검색해 보자. 그러면 각 셀에 검색된 사진이 나타나는걸 볼 수 있을 것이다 .

드디어 성공이다! 사진들이 각 셀안에 정확히 잘 맞게 들어가있는지 확인해 보자. 오토레이아웃을 조정한 것 처럼, 전에 코딩하였던 sizeForAtIndexPath 안에서 모든 셀의 크기를 사진크기에서 35 pixel 씩 늘렸기 때문에 정확한 것이다.

Note: 만약 위 사진과 같지 않거나 혹은 뭔가 이상하다면 아마 오토레이아웃 세팅에 문제가 있을 수 있다.  만약 막혔다면 이 프로젝트 솔루션과 너의 것을 비교해 보아라

sizeForItemAtIndexPath 코드는 셀을 이미지보다 35pixel 넓고 높게 조정해 주고 오토레이아웃은 새로운 셀 프레임에 맞게 이미지뷰의 크기를 조절해주고 센터에 자리잡게 해준다.
이제 UICollectionView 예제를 완전히 끝마쳤다.(그것도 완전 멋있게) 자신 스스로에게 칭찬해보자!

앞으로..

해야 할것들이 더 남아 있다. 이 튜토리얼 파트2에서 배울 것들은 :

  • collection View에 커스텀 헤더를 넣는 방법
  • 셀을 선택했을때 디테일뷰로 넘어가는 방법
  • 셀 다중선택 하는 방법까지!!

만약 더 많은 이벤트 들을 배우고 싶다면 iOS6 by Tutorials 를 한번 체크해 봐라. 이 책에는 UICollectionView와 커스텀 레이아웃을 이용한 상급의 튜토리얼이 들어있다.
그동안 배운 것에 대한 궁금증 혹은 코멘트가 있다면 밑에 포럼에 가입해주길 바란다!

이 포스트는 iOS 튜토리얼 팀 멤버이자 소프트웨어 개발자, 그리고 brandontreb.com의 운영자,  Brandon Trebitowski에 의해 쓰여졌다.

Brandon Trebitowski
Brandon Trebitowski

Brandon Trebitowski is a software developer and author from Albuquerque, New Mexico. Brandon holds a BS in Computer Science from The University of New Mexico and has been developing software for the last 10 years. In 2010, he coauthored the book iPhone & iPad In Action published by Manning publishing.

Brandon is currently the Director of Mobile Engineering for ELC Technologies and a regularly blogs at http://brandontreb.com.

User Comments

0 Comment

Other Items of Interest

Ray의 월간 뉴스레터

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Our Books

Our Team

Tutorial Team

  • Matt Galloway

... 50 total!

Update Team

  • Riccardo D'Antoni

... 15 total!

Editorial Team

  • Alexis Gallagher

... 23 total!

Code Team

  • Orta Therox

... 3 total!

번역 팀

  • Di Peng
  • Heejun Han

... 33 total!

Subject Matter Experts

  • Richard Casey

... 4 total!