CLOSE SEARCH

컬렉션 뷰(UICollectionView) #5. Layout, 그리고 Flow Layout

Separation

컬렉션 뷰는 데이터와 레이아웃을 구분해서 처리합니다. 다시 말하면, 데이터는 레이아웃에 영향을 주지 않고, 레이아웃은 데이터에 영향을 주지 않습니다. 이런 특징 때문에 컬렉션 뷰는 데이터를 여러가지 방식으로 자유롭게 표현할 수 있습니다. 데이터와 관련된 소스코드의 변경없이!!

 

Layout Object

모든 컬렉션 뷰는 자신의 데이터를 표현하기 위해서 레이아웃 객체를 하나씩 가지고 있습니다. UICollectionViewLayout 클래스의 인스턴스인 이 객체는 collectionViewLayout 라는 속성에 저장됩니다. 컬렉션 뷰는 이 속성에 저장된 레이아웃 객체에 저장되어 있는 셀의 크기, 간격, 섹션 여백 등에 대한 정보를 토대로 데이터를 표시합니다. 즉, 새로운 레이아웃 객체를 생성한 다음 이 속성에 할당하면 컬렉션 뷰는 새로운 레이아웃이 적용된 화면을 표시해줍니다. collectionViewLayout 객체에 직접 객체를 할당하는 경우에는 에니메이션 효과없이 레이아웃이 바로 변경되지만, setCollectionViewLayout:animated: 메소드를 사용해서 레이아웃 변경시에 에니메이션 효과를 적용할 수도 있습니다.
컬렉션 뷰는 기본적으로 Flow Layout을 사용합니다. 인터페이스 빌더에서 컬렉션 뷰를 뷰에 추가하면 자동적으로 플로우 레이아웃 객체를 추가해 줍니다. 일반적으로 플로우 레이아웃을 통해 대부분의 레이아웃을 구현할 수 있으며, 필요한 경우에는 자신만의 레이아웃 객체를 사용할 수도 있습니다. UICollectionViewLayout를 상속하여 원하는 방식의 레이아웃을 자유롭게 만들 수 있습니다.

 

Flow Layout

플로우 레이아웃은 UICollectionViewFlowLayout 클래스의 객체입니다. 이 레이아웃은 컬렉션 뷰 데이터를 그리드 형태의 선형 레이아웃으로 표시하는데 적절한 레이아웃입니다. 물론, 약간의 설정을 통해 다른 방식의 레이아웃을 구성할 수도 있습니다.
플로우 레이아웃 객체가 설정할 수 있는 속성은 다음과 같습니다. ( ) 안에 표시된 것은 실제 속성명입니다.

스크롤 방향 (scrollDirection)
셀 크기 (itemSize)
최소 라인 간격 (minimumLineSpacing)
최소 내부 여백 (minimumInteritemSpacing)
섹션 내부 여백 (sectionInset)
헤더 크기 (headerReferenceSize)
푸터 크기 (footerReferenceSize)

레이아웃에 할당한 속성은 모든 셀에 동일하게 적용됩니다. 예제 프로젝트에서 셀의 크기는 200×150으로 설정되어 있기 때문에 실제 그림의 크기에 관계없이 모든 셀이 동일한 크기로 표시됩니다. 만약 셀마다 서로 다른 크기를 지정하거나 라인 간격을 서로 다른 값으로 설정하고자 한다면 UICollectionViewDelegateFlowLayout 델리게이트를 구현해야 합니다. 컬렉션 뷰는 이 델리게이트가 컬렉션 뷰 델리게이트 내부에 구현되어 있는 것으로 가정하기 때문에 델리게이트가 구현된 곳에서 이 델리게이트를 구현해야 합니다. 특히, 크기와 관련된 속성들은 0보다 큰 값으로 설정해 주어야 정상적으로 화면에 표시되므로 주의해서 설정해야 합니다.

scrollDirection

스크롤 방향은 scrollDirection 속성을 통해 변경할 수 있습니다. 세로로 스크롤되는 경우 UICollectionViewScrollDirectionVertical, 가로로 스크롤되는 경우 UICollectionViewScrollDirectionHorizontal로 설정할 수 있습니다. 예제 프로젝트에서는 하단 툴바에 있는 버튼을 탭할 때마다 스크롤 방향을 변경하고 있습니다. 컬렉션 뷰에서 현재 레이아웃 객체를 요청하고, 이 레이아웃 객체의 scrollDirection 속성을 변경해주면 별다른 업데이트 메소드를 호출하지 않아도 바로 적용됩니다.

itemSize

셀의 크기는 레이아웃 객체의 itemSize 속성을 통해 변경할 수 있습니다. 기본적으로 설정된 크기는 모든 셀에 동일하게 적용됩니다. 셀을 서로 다른 크기로 표시하려면 반드시 UICollectionViewDelegateFlowLayout 델리게이트를 구현해야 합니다. 예제 프로젝트에서는 Size 버튼을 탭할 때마다 고정된 크기 또는 실제 크기로 셀을 표시하도록 구현하고 있습니다.

fixed_cell_size< 고정된 셀 크기 >

actual_cell_size < 실제 이미지 크기와 동일한 셀 크기 >

 

minimumLineSpacing

minimumLineSpacing 속성은 라인 사이의 최소간격을 나타냅니다. 최소 간격이라는 의미는 라인 사이의 간격이 설정한 값보다 클 수 있다는 것을 의미합니다.

minimum_line_spacing

위의 화면을 통해 최소 간격과 실제 간격의 차이를 알 수 있습니다. minimumLineSpacing 속성을 통해 지정하는 최소 라인 간격은 붉은색 화살표로 표시된 간격을 의미합니다. 컬렉션 뷰는 어떠한 경우에도 이 값보다는 큰 간격으로 셀 라인을 표시합니다. 그리고 하늘색 화살표로 표시된 간격과 같이 실제 라인 간격은 셀의 크기에 따라 달라질 수 있으며, 앞에서 설명한 것과 마찬가지로 항상 최소 간격보다는 큰 간격을 가집니다.
설정된 최소 라인 간격은 모든 섹션에 동일하게 적용됩니다. 만약 섹션별로 최소 라인 간격을 다르게 하고 싶은 경우에는 -(CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section 델리게이트를 구현하면 됩니다.

 

minimumInteritemSpacing

최소 내부 여백(minimumInteritemSpacing)은 실제로 하나의 라인내에 있는 셀 사이의 최소 간격을 나타냅니다. 앞에서 설명한 최소 라인 간격과 마찬가지로 셀 사이의 최소 간격이며, 실제 셀의 크기에 따라서 간격이 더 커질 수 있습니다.

minimum_interitem_spacing

위의 화면은 예제 프로젝트에서 최소 내부 여백 값을 80으로 지정한 후에 실행한 화면입니다. 컬렉션 뷰는 셀을 출력하기 전에 하나의 라인에 표시할 수 있는 최대 셀 개수를 계산합니다. 다크쉐도우, 휴고, 타워 영화 포스터의 너비와 최소 내부 여백 값을 더한 너비가 컬렉션 뷰의 섹션 너비보다 크기 때문에 모두 한 라인에 표시할 수 없다고 판단하고, 타크 쉐도우와 휴고만을 표시하고 나머지 공간은 모두 여백으로 처리합니다. 그래서 다크쉐도우와 휴고 사이의 간격은 80(붉은색 화살표)이 아닌 컬렉션 뷰 섹션의 너비에서 각 포스터의 너비를 뺀 값(하늘색 화살표)이 됩니다. 설정한 최소 내부 여백은 모든 섹션에 동일하게 적용됩니다. 만약 섹션별로 최소 내부 여백을 다른 값으로 설정하고 싶은 경우에는 -(CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section 델리게이트를 구현하면 됩니다.

 

sectionInset

모든 섹션은 내부 여백을 가지고 있습니다. 예제 프로젝트에서는 지금까지 섹션 내부 여백을 0으로 지정했기 때문에 각 섹션의 셀은 섹션 경계에서 여백이 없이 표시되었습니다.

zero_section_inset< 섹션 내부 여백이 모두 0인 경우 >

섹션의 내부 여백은 상하좌우 네방향에 대해 각각 설정할 수 있습니다. 소스코드를 통해 변경하는 경우 UIEdgeInsets 구조체를 사용합니다. 내부 여백 값을 모두 100으로 설정하면 다음과 같이 표시됩니다. 툴바에 있는 Section Inset 버튼을 탭하면 여백 값이 0 또는 100으로 설정됩니다.

section_inset< 섹션 내부 여백이 모두 100인 경우 >

하늘색 사각형 영역은 실제 셀이 표시되는 영역이고, 나머지 공간은 100 포인트 크기의 내부 여백입니다. 예제 프로젝트에서는 다음과 같은 코드로 섹션 내부 여백을 변경하고 있습니다. 만약, 섹션별로 내부 여백 크기를 다르게 지정하고 싶다면 -(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section 델리게이트를 구현해야 합니다.

 

headerReferenceSize,  footerReferenceSize

헤더와 푸터의 크기는 각각 headerReferenceSize, footerReferenceSize 속성을 통해 변경할 수 있습니다. 컬렉션 뷰 델리게이트에서 유효한 헤더나 푸터(즉, Supplementary View) 생성해서 리턴한다고 하더라도, 레이아웃 객체의 헤더나 푸터 크기 속성이 0으로 설정되어 있다면 화면에 표시되지 않습니다. 그러므로 적절한 크기를 설정해 주어야 합니다. 섹션별로 헤더나 푸터의 크기를 다양하게 설정하고자 한다면, -(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section 델리게이트 메소드와 -(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section 델리게이트 메소드 중 필요한 메소드를 구현해야 합니다.

 

Build & Run

collectionview_run_in_iphone

아이폰용 테스트 앱을 실행하는 경우에는 몇몇 그림이 크기로 인해 표시되지 않는 것을 확인하실 수 있습니다. 컬렉션 뷰는 셀의 크기(하늘색 영역)가 섹션의 내용 영역(붉은색 영역)을 벗어나는 경우에는 다음과 같은 오류 메시지를 로그 영역에 출력하고 셀을 표시하지 않습니다.

collectionview_log

 

그러므로, 셀의 크기가 셀에 표시가능한 적절한 크기를 갖도록 설정해주어야 합니다. 아이폰용 소스코드에서는 실제 이미지 크기의 1/2로 표시하고 있지만 여전히 몇몇 이미지는 표시가능한 영역을 벗어나는 크기를 가지고 있습니다. 직접 이 문제를 해결해보시면서 컬렉션 뷰 사용법에 더욱 익숙해 지시기를 바랍니다^^

최종 예제 프로젝트 >> CollectionView_final

Filed under: iOS

  1. 한세희
    감사합니다. 덕분에 쉽게 이해할수 있었네요