부스트코스 iOS 5차 프로젝트 ‘BoxOffice’ 과제에서는 아래와 같이 collectionView 형태로 영화 리스트 보여줘야 한다.
이렇게 세로 화면에서는 줄 당 아이템 2개가 보인다.
그런데 가로화면에서도 같은 비율로 줄 당 아이템 2개 보여준다면?
이상하다.
요구 사항
아이템의 크기가 더 작아지고 줄 당 보여주는 아이템 수가 많아져야 할 것 같다. 이런식으로.
따라서 세로, 가로 모드에 따른 셀 사이즈 조절이 필요하다.
해결 방법
UICollectionViewDelegateFlowLayout 라는 protocol에는 아래와 같은 함수가 속해있다.
해당 함수를 통해 아이템의 셀 사이즈를 직접 지정해줄 수 있다.
CGSize 를 반환하니까 저 함수 안에
return CGSize(width: 100, height: 100)
이런식으로 리턴하면 셀의 가로/세로 사이즈가 각각 100이 된다.
따라서 디바이스의 방향에 따라 return 하는 사이즈를 다르게 하면 되겠다.
UIDevice.current.orientation.isLandscape
위의 식에 따라 현재 디바이스의 상태가 가로 모드인지 세로 모드인지 판단할 수 있다. 이렇게.
참고로 저 안의 getCellSize 함수는 UICollectionView의 extension에 따로 구현한 함수이다. 한 줄에 들어가야할 아이템의 개수와 셀의 비율을 parameter로 넣어주면 적절한 cellSize를 반환한다.
새로운 문제 — 1
아이폰 xr에서는 디바이스 방향을 변경할때마다 collectionView(_:layout:sizeForItemAt:)
함수가 매번 불리고 잘 작동했다. 하지만 5s에서 실행할때는 가로모드로 변경될때만 해당 함수가 실행됐다.
따라서 화면을 돌릴때마다 시점을 캐치하고 레이아웃을 다시 그려줘야 했다.
해결 방법 — 1
이 함수는 container에 뷰의 사이즈가 바뀔 것이라고 알려준다.
따라서 디바이스 화면 방향이 바뀔때마다 동작하게 되고, 안에 collectionView의 layout을 업데이트 시켜주는 코드를 넣어주면 된다.
if let flowLayout = self.collectionView!.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.invalidateLayout()
}
flowLayout.invalidateLayout()
가 그런 기능을 하는데, invalidateLayout
는 현재 layout을 무효화하고 layout 업데이트를 작동시킨다.
새로운 문제 — 2
그런데 다음 화면으로 넘어가서 디바이스 방향을 변경할때도 저 함수가 불리면서 nil 값인 collectionView에 접근하려고 해서 크래시가 났다.
Unexpectedly found nil while implicitly unwrapping an Optional value
다른 뷰에서 디바이스 방향을 변경할때도viewWillTransition(to:with:)
가 불리는게 당황스러웠다.
구글링을 해보니 원래 해당 메소드는 이 메소드를 포함한 ViewController에서만 동작하지만 지금은 네비게이션 컨트롤러 쓰고있기 때문에 그 전의 뷰들이 메모리상에 존재하고 있기 때문에 viewWillTransition(to:with:)
가 동작하는 듯 하다.
해결 방법 — 2
다른 레퍼런스들에 의하면 현재 뷰의 방향이 변화할때 각각의 ViewController
에 있는 viewWillTransition(to:with:)
가 동작할 적절한 시점인지 판단해서 해결하는 듯 하다.
나는 collectionView를 optional 으로 만들어서 실제 값이 있을때만 동작하도록 처리했다.
if let flowLayout = self.collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
flowLayout.invalidateLayout()
}