본문 바로가기

iOS/Deep dive!!!

[iOS/UIKit]UICollectionViewLayout vs UICollectionViewFlowLayout | No2. 컬랙션 뷰 탐구

안녕하세요. 이전 포스트에선 UICollectionVIew에 관련된 개념을 정리했었는데, 이번 포스트는 UICollectionViewLayout과 flowLayout을 공부하며 배운 개념을 정리하려고 합니다. 주로 이 링크를 통해 공부했습니다.

 

 

잠깐 collection view에 대해서 정리하자면, data source는 collection view에서 특정 section의 number of items 에 관한 정보를 반환할 책임이 있습니다. Data는 indexPath based protocol을 통해 data source에 의해 관리됩니다. delegate는 사용자에 관한 터치 이벤트가 발생했을 때 관련된 메서드(선택,highlighting, editmode etc...)를 호출해야 할 책임이 있습니다. 그리고 cell의 타입인 UICollectionReusableView를 통해 화면에 reuse queue로 관리되며 화면에 보여졌습니다. 

 

Collection view는 tableView와 다르게 한 line에서 여러 개의 cell이 존재할 수 있으므로 layout 지정이 반드시 필요합니다. 이 세 object를 사용해 collection view를 구성합니다.

1. UICollectionVIewLayout's concept

CollectionView 안에서, layout object는 collection view's views(cells, supplemetary, decoration views)의 배치를 결정하고 items의 visual styling을 할 책임이 있습니다. Layout object는 위에서 언급한 뷰들의 size, bounds, location, appearance-related attributes를 결정해야 합니다.

 

CollectionViewLayout은 추상 base class입니다. An abstract base classUICollectionViewLayout은 subclass로 사용할 수 있는 빈 캔버스와 같습니다. "나를 제발 subclass로 사용해서 활용해 줘!!!!" 아래의 views를 배치할 책임이 있습니다. 하지만 자신은 아무것도 구현되지 않았기에 누군가(custom class, flow, compositional layout) 이를 subclass 한 후 특정 함수들을 override 해서 다뤄야 합니다.

  • Cells. CollectionView의 메인 contents를 나타냅니다. 이는 data source object로부터 info를 받아 생성됩니다.
  • Supplementary views(header, footer...) 이들은 optional입니다. 마찬가지로 layout object에 의해 배치가 관리되며 flowLayout의 경우 header, footer를 지원합니다. 사용자에 의해 선택 지원 x.
  • Decoration views(badges) supplementary views의 다른 타입입니다. 본질적으로 collection view's data와 연관 x. 마찬가지로 사용자에 의해 선택 지원 x.

 

CollectionView의 내부입니다. 3개의 object를 갖고 있는데, collectionView를 구성하기 위해선 dataSource와 layout 인스턴스는 반드시 지정해야 합니다. 특히 layout object(다른 건 옵셔널인데 layout 객체만 옵셔널이 아니기에 초기화 시점에 반드시 초기화되도록 요구됩니다). 그리고 UICollectionView 타입의 객체를 사용할 때 layout지정 없이 코드를 구현하게 된다면 에러가 발생합니다.

 

Collection view는 layout object에게 위에서 언급된 collection view내부의 cells, views의 layout 정보를 요청하기(첫 생성, 선택, delete등에 의해 cell의 배치가 변경될 때 등에 의한 요청) 때문입니다. 물론 화면에 보이는 영역에 한해서만 views를 배치합니다.

 

 

그래서 collection view는 layout object 없이는 collection view 내부의 cells, views의 layout을 배치할 수 없습니다. 반드시 layout object 초기화를 해주어야 하는데 UICollectionViewLayout의 경우 abstract 기반 class이므로 구현된 게 없습니다. 추상클래스는 하위 클래스에서 이를 구체적으로 구현해서 사용해야 합니다. 그래서 UICollectionView를 초기화할 때 반드시 UICollectionViewLayout를 subclass로 하는!! 상속받은 custom class, flow layout, compositional layout 셋 중 하나를 사용해야 합니다.

 

Subclassing 할 때,

이들은 반드시 오버라이딩 해야 합니다. 추가적으로 커스텀으로 구현한 layout가 supplementary views나 decorationVIews를 지원할 때는 layoutAttributesForSupplementaryView(ofKind:at:), layoutAttributesForDecorationView(ofKind:at:)를 오버라이드 해야 합니다.

 

TableView는 기본적인 style을 지원합니다. 반면 collection view의 UICollectionVIewLayout은 특정 style을 부여하지 않는 an abstract base class이지만!!! 그 대신에 UICollectionViewFlowLayout을 통해 cells, supplementary, decoration views를 쉽게 정렬할 수 있게 해 줍니다.

2. UICollectionVIewFlowLayout's concept

UICollectionViewFlowLayout을 간단히 flow layout로 부르겠습니다. 

 

https://developer.apple.com/documentation/uikit/uicollectionviewflowlayout

 

flow layout은 UICollectionVIewLayout를 subclass로 하는 추상클래스를 구체적으로 subclassing 한 layout 클래스입니다. 

https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/CollectionViewBasics/CollectionViewBasics.html#//apple_ref/doc/uid/TP40012334-CH2-SW1

 

Items를 grid 형식으로 배치할 수 있고, optional로 header, footer를 지원합니다. 또한 default size로 cell의 최소 사이즈를 지정합니다. 동시에 한 라인에 대해서 interitem 간 minimum spacing과 line 간 minimum spacing을 지원합니다. 위에서 보는 그림처럼 각 section마다 header, footer를 가지고 cell들의 디폴트 size도 적용되며 cell 간 spacing 또한 적용이 됩니다. 이에 관련된 것은 이전 포스트에서 잠깐 다뤘었습니다.

 

또한 CollectionView의 layout axis는 vertical or horizontal 중 한 스크롤 방향만 지원됩니다. 양쪽 다 지원되지 않습니다. properties를 통해 cells size를 지정할 수 있고 delegateFlow로 더 많은 속성을 지정할 수 있습니다. 

Steps for configuring the flow layout

  1.  flow layout object 생성 -> collection view에 할당
  2. cell의 height, width를 configure 해야 합니다.
  3. 필요시에 items와 lines를 위해 spacing option을 set 해야 합니다.
  4. section's header, footer를 원하면 이들의 size를 지정해야 합니다.
  5. layout을 위한 scroll direction을 선택해야 합니다. (horizontal or vertical 둘 중 하나)

Collection view's views 

https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html#//apple_ref/doc/uid/TP40012334-CH3-SW1

 

Flow layout의 특징은 line-based breaking 방식으로 layout 합니다. 예를 들어 처음 line(한 행)에 가능한 많은 cells를 배치합니다. 아직 cells가 남을 경우, 그리고 해당 line에 items를 배치할 공간이 없을 경우 그 아래 line을 새롭게 생성해 다시 선형으로 배치해 나갑니다. 위 그림을 통해 layout process 되는 과정을 알 수 있습니다. 추가로 한 section에 header, footer가 있다면 cells는 supplementary views 사이에 둘러 쌓입니다. Header, footer는 optional입니다.

 

func collectionView(_ collectionView: UICollectionView,
					layout collectionViewLayout: UICollectionViewLayout,
                    sizeForItemAt indexPath: IndexPath) -> CGSize {}

 

이는 cells의 size를 동적으로 지정하는 것이고 이 함수를 사용하지 않고 layout.itemsize 를 사용해서 변경할 경우에는 모든 cells의 기본 사이즈가 설정됩니다.

 

 

한 가지 특징은 위에서 보이는 cells 각각의 크기는 동일합니다.

https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html#//apple_ref/doc/uid/TP40012334-CH3-SW1

 

하지만 cells 각각의 size를 다르게 할 수 있습니다. 델리게이트 flow layout 프로토콜을 통해 특정 cell의 size를 IndexPath를 통해 각기 다르게 지정할 수 있기 때문입니다. 이는 비대칭적으로 보이긴 하지만 그럼에도 line 간 minimum spacing은 존재하기 때문에 linear flow를 가집니다. 한 줄의 cell 개수가 다를 수 있습니다.

 

cells의 size나 spacing을 dynamically 변경하고 싶다면 UICollectionViewDelegateFlowLayout를 통해 특정 메서드를 사용해야 합니다. 이 델리게이트의 모든 함수는 optional입니다.

Specifying the Space Between Items and Lines

cell의 size를 선정했다면, 특정 line에서 interitems!! items 간 minimum spacing과 line간 minimum spacing에 대해 고려해야 합니다. 물론 최소 spacing을 지정했지만 contents를 배치하면서 실제 spacing은 제각각 다를 수 있습니다.

 

https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html#//apple_ref/doc/uid/TP40012334-CH3-SW1

 

Layout process 과정 중에, flow layout object는 현재 line에 items를 더해갑니다. 더 이상 채울 수없을 경우 새로운 line을 생성해서  채워 나갑니다. 이때 line에서 items간 spacing은 minimum spacing과 동일할 수도 있고, 생각보다 공간에 여유가 있을 때 중간 items spacing을 균등할 때까지 늘릴 수 있습니다. 위 사진은 inter-item spacing 방법입니다. 

 

https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html#//apple_ref/doc/uid/TP40012334-CH3-SW1

 

만약 items size가 같을 경우 minimum line spacing value를 존중해 주고, line안에 있는 items 간  spacing은 균등하게 spaced되어 화면에 draw됩니다. 만약 items간 size가 다를 경우 actural spacing은 다를 수 있습니다.

 

위 그림은 line 간 itmes size가 다른 경우에 속합니다. 이때 각각의 line에 대해서 minimum line spacing이 어떻게 발생되는지를 보여줍니다. 각각의 line에서 Item sizea가 다르다면! Flow layout object는 각각의 line에서 스크롤 방향을 기준으로 가장 큰 item을 선정합니다. 그리고 나서 이들의 items 간 minimum value를 line spacing으로 설정합니다. 근데 actual line spacing은 다를 수 있습니다.

 

interitem spacing, line spacing은 아래 두 함수를 통해 지정할 수 있습니다.

Using section inset

https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html#//apple_ref/doc/uid/TP40012334-CH3-SW1

 

Cell의 size, interitem spacing, line spacing 뿐 아니라 contents를 감싸는 section(header, footer사이)에 대한 inset도 할 수 있습니다. 다만 insets를 적용하면 line에 cells를 배치하기 위해 이용가능한 공간의 양을 줄이기 때문에, line의 cells 개수가 제한될 수 있습니다.

3. Knowing when to subclass the flow layout!!

case 1 : 새로운 supplementary or decoration views를 추가해야 할 때

Flow layout은 기본으로 header, footer를 지원합니다. Decoration views는 지원하지 않습니다. 이땐 요구에 맞는 layout attributes 속성의 특정 메서드를 오버라이딩하면 됩니다.

case 2 : flow layout에 의해 반환되는 layout attributes를 수정하고 싶은 경우

이때도 마찬가지로 제공되는 layout attributes의 특정 메서드를 override!!

case 3 : cells, views를 위해 새로운 layout attributes를 추가해야 할 때

case 4 : initial, final locations(삽입, 삭제가 되는 Items를 위한)를 지정하길 원할 때

기본적으로 simple fade animation은 inserted, deleted 되는 items를 위해 생성됩니다. custom animation을 만들고 싶으면 아래의 메서드 일부 or 전체를 override 해야 합니다.

  • initialLayoutAttributesForAppearingItem(at:) // 컬랙션 뷰 안으로 삽입되는 item을 위한 시작 layout info 검색
  • initialLayoutAttributesForAppearingSupplementaryElement(ofKind:at:) // 컬랙션 뷰에 삽입되는 supplementary에 대한 시작 layout info 검색
  • initialLayoutAttributesForAppearingDecorationElement(ofKind:at:) // decoration에 대해 위와 같습니다.
  • finalLayoutAttributesForDisappearingItem(at:) // 삭제되려는 an item에 대한 최종 layout info 검색
  • final... SupplementaryElement(ofKind:at:)
  • final... DecorationElement(ofKind:at:)

애니메이션 커스텀할 때 사용하면 좋은 메서드들입니다. 원하는 각각의 view가 우선 삽입, or 삭제된 이후에 attributes를 지정합니다. flow layout object는 위에서 override 한 메서드들을 이용해서 insertions and deletions 때 애니메이션을 커스텀할 수 있습니다.

 

prepare(forCollectionViewUpdates:), finalizeCollectionVIewUpdates() 메서드 오버라이딩해서 같이 사용하는 게 좋습니다. (current cycle에 inserted, deleted 되는 items 추적할 수 있게 됩니다.)

 

 

지금까지 UICollectionViewLayout의 개념과 이를 subclass 하는 UICollectionViewFlowLayout, UICollectionViewDelegateFlowLayout에 대한 개념을 적어봤는데요.. 기본적으로 많이 사용되는 layout은 flow layout이구,, 이 모든 것은 custom 가능합니다. flow layout의 단점은 scrolling 방향이 한 방향으로 제한되어 있습니다. Grid나 line-based breaking layout일 땐 flow layout 사용이 베스트지만, 그렇지 않을 경우 커스텀으로 만드는 게 오히려 좋을 수 있습니다.

https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/CreatingCellsandViews/CreatingCellsandViews.html#//apple_ref/doc/uid/TP40012334-CH7-SW1

 

References:

 

https://developer.apple.com/documentation/uikit/uicollectionviewlayout/

https://developer.apple.com/documentation/uikit/uicollectionviewflowlayout

https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html#//apple_ref/doc/uid/TP40012334-CH3-SW1

https://developer.apple.com/library/archive/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/CollectionViewBasics/CollectionViewBasics.html#//apple_ref/doc/uid/TP40012334-CH2-SW1