#박스오피스 PR
# 박스오피스 step3
@July911
줄라이 안녕하세요! 코낄이, 리지 입니다.
step3 PR 완성하여 올립니다!
매번 좋은 피드백 감사합니다. 이번에도 잘 부탁드립니다 ☺️
# 고민했던 점
## 1️⃣ listCell과 customCell
저희는 Mordern Collection View를 구현하는 방법으로 `UICollectionViewListCell`을 사용하는 방법(iOS 14 타겟)을 선택했었습니다.
하지만 줄라이의 조언을 듣고 listCell을 사용하지 않는 구현을 경험해보지 않는 것이 아쉬워서, iOS 13 타겟으로 listCell을 사용하지 않고 다시 구현해보았습니다.
<img src="https://i.imgur.com/sEQJ37Z.png" width=250>
listCell로 구현하는 방식은 [Implementing Modern Collection Views](https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views) 에서 제공하는 샘플 코드 일부를 사용하였습니다.
### listCell을 사용한 구현에서 느낀 점
- Layout이 편했습니다.
``` swift
private func createLayout() -> UICollectionViewLayout {
let config = UICollectionLayoutListConfiguration(appearance: .plain)
return UICollectionViewCompositionalLayout.list(using: config)
}
```
셀의 레이아웃을 따로 구현할 필요 없이 `UICollectionViewCompositionalLayout.list`만으로 가로 화면, 세로 화면에서 컬렉션뷰의 레이아웃이 리스트 형태로 완벽하게 구성되는 것을 화인할 수 있었습니다.
- 코드를 이해하기가 어려웠습니다.
``` swift
@available(iOS 14.0, *)
extension UIConfigurationStateCustomKey { ... }
@available(iOS 14.0, *)
extension UICellConfigurationState { ... }
@available(iOS 14.0, *)
final class DailyMovieListCell: UICollectionViewListCell {
override var configurationState: UICellConfigurationState { ... }
...
```
`UIConfigurationStateCustomKey`과 `UICellConfigurationState`, `DailyMovieListCell.configurationState`의 관계를 파악하기가 어려웠습니다. 또한 `UICollectionViewListCell`을 상속하는 클래스에서 구현하는
``` swift
override func updateConfiguration(using state: UICellConfigurationState)
```
메서드는 직접 호출되지 않고 `setNeedsUpdateConfiguration()`를 통해 호출 요청을 보내는데, 이러한 부분도 추적하기가 어려웠습니다.
listCell을 사용하기 위해서는 충분히 시간을 가지고 학습해야 할 것 같다고 생각했습니다.
## 2️⃣ snapshot을 찍어주는 주체
`NSDiffableDataSourceSnapshot` 공식문서를 확인해보니 Diffable data source가 snapshot을 사용해 collectionView에게 필요한 데이터를 제공한다는 것을 알 수 있었습니다. 처음엔 `collectionView`에 데이터를 받아서 띄우는 쪽으로 생각해서 `collectionView` 타입에 구현하였는데, 정확한 개념을 이해하니 MVC 관점에서 `collectionView`는 단순히 `View`를 담당하고 이 `View`에 데이터를 전달하는 즉, snapshot을 찍는 역할은 `viewController`에서 하는 것이 맞다고 생각하여 `viewController`에 구현하였습니다.
## 3️⃣ customCell로 구현시 AccessoryType 문제
`ListCell`로 구현했을 때는 `accessoryType`이 있어 쉽게 적용할 수가 있었는데,`customCell`로 구현하니 `accessoryType`을 찾지 못했습니다. 그래서 저희는 직접 imageView로 구현하였습니다.
- **적용한 방법**
`UIImage(systemName: "chevron.right")` 로 > 이미지를 넣은 후 색상을 변경하였고, `widthAnchor`, `heightAnchor`를 `mainStackView`의 배율로 크기를 지정하였습니다.
```swift=
private var accessoryImageView = UIImageView()
private func configureAccessoryImageView() {
accessoryImageView.image = UIImage(systemName: "chevron.right")?.withTintColor(.systemGray2, renderingMode: .alwaysOriginal)
accessoryImageView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
accessoryImageView.trailingAnchor.constraint(equalTo: mainStackView.trailingAnchor),
accessoryImageView.widthAnchor.constraint(equalTo: mainStackView.widthAnchor, multiplier: 0.025),
accessoryImageView.heightAnchor.constraint(equalTo: mainStackView.heightAnchor, multiplier: 0.2)
])
}
```
## 조언을 구하고 싶은 점
### 1️⃣ customCell로 구현시 Autolayout 문제
- 줄라이의 피드백을 받고 `customCell`을 활용해보고자 했습니다. cell을 구성하는 UI는 크게 `mainStackView` - `movieRankStackView`, `movieListStackView`,`accessoryImageView` 로 구성하였고, `movieRankStackView`, `movieListStackView` 안에는 `label`을 두개씩 배치하였습니다. 그리고 필요한 제약을 걸어 autolayout을 잡았는데, 아래와 같은 문제가 발생하였습니다.
- customCell로 구현시 문제된 화면
<img src="https://i.imgur.com/8YG5K4q.gif" width=250><img src="https://i.imgur.com/pMio0Mi.gif" width=250>
- 저희가 생각한 문제의 원인은 UICollectionViewLayout을 잡는 곳에서 itemSize와 관련있다고 생각했습니다. 따라서 `.fractionalHeight`,`absolute`, `estimated`로 확인을 해봤는데, 답을 찾지 못하였습니다.
- **시도한 방법**
- `.fractionalHeight(x)`: x 값을 0.1 보다 높여주어 회전했을 때 cell크기가 유지되도록 하였으나 세로 화면일 때 cell크기가 예시화면보다 큰 문제가 발생
- `absolute(44.0)`, `estimated(44.0)` : 첨부한 첫 번째 화면과 같이 아래로 스크롤 했을 때, cell의 크기가 줄어들어 있거나 화면 회전시 화면을 벗어난 cell의 크기가 다른 cell보다 커지는 현상 발생
```swift
func createMovieListLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
}
```
문제의 원인을 저희가 잘못 생각했을까요? 다른 어떤 방법을 시도해볼 수 있을까요? 🫠
## 2️⃣ 동일한 파일 내에서 extension을 하는 기준
`DailyBoxOfficeViewController` 내에서 메서드를 다음과 같이 구현하였습니다.
``` swift
final class DailyBoxOfficeViewController: UIViewController {
@objc private func refreshData()
private func updateDateToViewTitle()
private func updateDateToEndPoint()
// 서버에 데이터를 요청하고 받아온 데이터를 저장하는 기능
private func fetchDailyBoxOfficeData()
}
// dataSource를 통해 cell에 데이터를 전달하는 기능
extension DailyBoxOfficeViewController {
private func setupDataSource()
private func setupSnapshot()
}
// collectionView의 레이아웃을 생성하는 기능
extension DailyBoxOfficeViewController {
private func createMovieListLayout()
}
```
뷰컨트롤러가 많은 기능을 가지기 때문에, 유사한 기능을 묶어 가독성을 개선하고자 위와 같이 extension을 사용하였는데, 적절한 기준으로 분류를 했는지 의문이 들었습니다.
`fetchDailyBoxOfficeData`는 서버에 데이터를 요청하는 기능이고, `setupDataSource`과 `setupSnapshot`는 cell에 데이터를 전달하는 기능입니다. 따라서 두 기능을 별개로 분리하였지만, 사실 cell에 데이터를 전달하는 두 메서드는 `fetchDailyBoxOfficeData`내에서 호출됩니다. 이처럼 서로 다른 기능이지만 연관이 되어 있어 기능을 딱잘라 구분하기엔 어려움이 있었습니다. 이럴 때 기능을 구분하는 기준을 정하는 데 도움이 될만한 조언이 있을까요? :thinking: