From 113405e5548d299a41a662e2b39f7b85a6763357 Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Fri, 11 Aug 2023 10:32:59 +0900 Subject: [PATCH 01/24] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=A6=84=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20setUp=20->=20setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/View/BoxOfficeCell.swift | 12 +++---- BoxOffice/View/MovieDetailView.swift | 32 +++++++++---------- .../BoxOfficeViewController.swift | 4 +-- BoxOfficeTests/BoxOfficeResultTests.swift | 4 +-- BoxOfficeTests/BoxOfficeTests.swift | 4 +-- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/BoxOffice/View/BoxOfficeCell.swift b/BoxOffice/View/BoxOfficeCell.swift index 4457adfa..2947b7bf 100644 --- a/BoxOffice/View/BoxOfficeCell.swift +++ b/BoxOffice/View/BoxOfficeCell.swift @@ -67,7 +67,7 @@ final class BoxOfficeCell: UICollectionViewListCell { extension BoxOfficeCell { private func configureCell() { configureUI() - setUpConstraints() + setupConstraints() } private func configureUI() { @@ -80,13 +80,13 @@ extension BoxOfficeCell { rankStackView.addArrangedSubview(rankInformationLabel) } - private func setUpConstraints() { + private func setupConstraints() { updateSeparatorConstraint() - setUpInformationStackViewConstraints() - setUpRankStackViewConstraints() + setupInformationStackViewConstraints() + setupRankStackViewConstraints() } - private func setUpInformationStackViewConstraints() { + private func setupInformationStackViewConstraints() { NSLayoutConstraint.activate([ informationStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), informationStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), @@ -95,7 +95,7 @@ extension BoxOfficeCell { ]) } - private func setUpRankStackViewConstraints() { + private func setupRankStackViewConstraints() { NSLayoutConstraint.activate([ rankStackView.leadingAnchor.constraint(equalTo: informationStackView.leadingAnchor), rankStackView.widthAnchor.constraint(equalTo: informationStackView.widthAnchor, multiplier: 0.15), diff --git a/BoxOffice/View/MovieDetailView.swift b/BoxOffice/View/MovieDetailView.swift index 9ac28f7d..61c2bec4 100644 --- a/BoxOffice/View/MovieDetailView.swift +++ b/BoxOffice/View/MovieDetailView.swift @@ -88,7 +88,7 @@ final class MovieDetailView: UIView { self.init(frame: CGRectZero) configureUI() - setUpConstraints() + setupConstraints() } override init(frame: CGRect) { @@ -115,19 +115,19 @@ final class MovieDetailView: UIView { [actorTitleLabel, actorDetailLabel].forEach { actorsContentStackView.addArrangedSubview($0) } } - private func setUpConstraints() { - setUpScrollViewConstraints() - setUpContentViewConstraints() - setUpPosterImageViewConstraints() - setUpTotalStackViewConstraints() - setUpTitleLabelsAlign() - setUpDetailLabelsAlign() + private func setupConstraints() { + setupScrollViewConstraints() + setupContentViewConstraints() + setupPosterImageViewConstraints() + setupTotalStackViewConstraints() + setupTitleLabelsAlign() + setupDetailLabelsAlign() } } // MARK: - Constraints extension MovieDetailView { - private func setUpScrollViewConstraints() { + private func setupScrollViewConstraints() { NSLayoutConstraint.activate([ scrollView.frameLayoutGuide.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor), scrollView.frameLayoutGuide.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor), @@ -136,7 +136,7 @@ extension MovieDetailView { ]) } - private func setUpContentViewConstraints() { + private func setupContentViewConstraints() { NSLayoutConstraint.activate([ contentView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor), contentView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor), @@ -150,7 +150,7 @@ extension MovieDetailView { contentViewHeightConstraints.isActive = true } - private func setUpPosterImageViewConstraints() { + private func setupPosterImageViewConstraints() { NSLayoutConstraint.activate([ posterImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8), posterImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8), @@ -158,7 +158,7 @@ extension MovieDetailView { ]) } - private func setUpLoadingImageViewConstraints() { + private func setupLoadingImageViewConstraints() { NSLayoutConstraint.activate([ loadingImage.centerXAnchor.constraint(equalTo: centerXAnchor), loadingImage.centerYAnchor.constraint(equalTo: posterImage.centerYAnchor), @@ -167,7 +167,7 @@ extension MovieDetailView { ]) } - private func setUpTotalStackViewConstraints() { + private func setupTotalStackViewConstraints() { NSLayoutConstraint.activate([ totalStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8), totalStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8), @@ -176,7 +176,7 @@ extension MovieDetailView { ]) } - private func setUpTitleLabelsAlign() { + private func setupTitleLabelsAlign() { NSLayoutConstraint.activate([ productionYearTitleLabel.centerXAnchor.constraint(equalTo: directorTitleLabel.centerXAnchor), openDateTitleLabel.centerXAnchor.constraint(equalTo: directorTitleLabel.centerXAnchor), @@ -188,7 +188,7 @@ extension MovieDetailView { ]) } - private func setUpDetailLabelsAlign() { + private func setupDetailLabelsAlign() { NSLayoutConstraint.activate([ productionYearDetailLabel.leadingAnchor.constraint(equalTo: directorDetailLabel.leadingAnchor), openDateDetailLabel.leadingAnchor.constraint(equalTo: directorDetailLabel.leadingAnchor), @@ -205,7 +205,7 @@ extension MovieDetailView { extension MovieDetailView { func startLoadingImage() { addSubview(loadingImage) - setUpLoadingImageViewConstraints() + setupLoadingImageViewConstraints() } func stopLoadingImage() { diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 5cdc4727..842107ef 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -29,7 +29,7 @@ final class BoxOfficeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - setUpRightBarButton() + setupRightBarButton() showLoadingView() setTitle() configureBackgroundColor() @@ -37,7 +37,7 @@ final class BoxOfficeViewController: UIViewController { } // MARK: - UI Configuration - private func setUpRightBarButton() { + private func setupRightBarButton() { navigationItem.rightBarButtonItem = UIBarButtonItem(title: "날짜선택", primaryAction: showCalendarViewController()) } diff --git a/BoxOfficeTests/BoxOfficeResultTests.swift b/BoxOfficeTests/BoxOfficeResultTests.swift index 52b8b25d..abe10c9f 100644 --- a/BoxOfficeTests/BoxOfficeResultTests.swift +++ b/BoxOfficeTests/BoxOfficeResultTests.swift @@ -12,8 +12,8 @@ final class BoxOfficeResultTests: XCTestCase { var sut: BoxOfficeResult? var assetFileName: String = "box_office_sample" - override func setUpWithError() throws { - try super.setUpWithError() + override func setupWithError() throws { + try super.setupWithError() assetFileName = "box_office_sample" do { diff --git a/BoxOfficeTests/BoxOfficeTests.swift b/BoxOfficeTests/BoxOfficeTests.swift index 1787e3db..295e860e 100644 --- a/BoxOfficeTests/BoxOfficeTests.swift +++ b/BoxOfficeTests/BoxOfficeTests.swift @@ -11,8 +11,8 @@ import XCTest final class BoxOfficeTests: XCTestCase { var sut: BoxOffice? - override func setUpWithError() throws { - try super.setUpWithError() + override func setupWithError() throws { + try super.setupWithError() } override func tearDownWithError() throws { From c849b5f27e2d876845491f4f860b079df4c10d22 Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Fri, 11 Aug 2023 10:42:43 +0900 Subject: [PATCH 02/24] =?UTF-8?q?test:=20override=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EC=98=A4=ED=83=88=EC=9E=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOfficeTests/BoxOfficeResultTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BoxOfficeTests/BoxOfficeResultTests.swift b/BoxOfficeTests/BoxOfficeResultTests.swift index abe10c9f..52b8b25d 100644 --- a/BoxOfficeTests/BoxOfficeResultTests.swift +++ b/BoxOfficeTests/BoxOfficeResultTests.swift @@ -12,8 +12,8 @@ final class BoxOfficeResultTests: XCTestCase { var sut: BoxOfficeResult? var assetFileName: String = "box_office_sample" - override func setupWithError() throws { - try super.setupWithError() + override func setUpWithError() throws { + try super.setUpWithError() assetFileName = "box_office_sample" do { From 2258a6ca735b2a7e7caacdcf192f0c52e48ce2cd Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Fri, 11 Aug 2023 10:43:37 +0900 Subject: [PATCH 03/24] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20set=20->=20setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setCalendarRange는 limitCalendarRange로 변경 --- .../ViewController/BoxOfficeViewController.swift | 6 +++--- .../ViewController/CalendarViewController.swift | 4 ++-- .../ViewController/MovieDetailViewController.swift | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 842107ef..1618e9c8 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -31,7 +31,7 @@ final class BoxOfficeViewController: UIViewController { setupRightBarButton() showLoadingView() - setTitle() + setupTitle() configureBackgroundColor() loadDailyBoxOfficeData() } @@ -53,7 +53,7 @@ final class BoxOfficeViewController: UIViewController { activityIndicatorView.removeFromSuperview() } - private func setTitle() { + private func setupTitle() { self.title = DateManager.formattedDateWithHyphen } @@ -149,7 +149,7 @@ extension BoxOfficeViewController: UICollectionViewDelegate { // MARK: - CalendarViewController Delegate extension BoxOfficeViewController: CalendarViewControllerDelegate { func updateBoxOfficeDate() { - setTitle() + setupTitle() loadDailyBoxOfficeData() } } diff --git a/BoxOffice/ViewController/CalendarViewController.swift b/BoxOffice/ViewController/CalendarViewController.swift index ac869828..e325b71f 100644 --- a/BoxOffice/ViewController/CalendarViewController.swift +++ b/BoxOffice/ViewController/CalendarViewController.swift @@ -22,7 +22,7 @@ final class CalendarViewController: UIViewController { override func viewDidLoad() { configureBackgroundColor() - setCalendarRange() + limitCalendarRange() showSelectedDate() } @@ -30,7 +30,7 @@ final class CalendarViewController: UIViewController { view.backgroundColor = .systemBackground } - private func setCalendarRange() { + private func limitCalendarRange() { calendarView.availableDateRange = DateInterval(start: .distantPast, end: DateManager.yesterday) } diff --git a/BoxOffice/ViewController/MovieDetailViewController.swift b/BoxOffice/ViewController/MovieDetailViewController.swift index 62e2fb89..326b65c8 100644 --- a/BoxOffice/ViewController/MovieDetailViewController.swift +++ b/BoxOffice/ViewController/MovieDetailViewController.swift @@ -34,14 +34,14 @@ final class MovieDetailViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - setTitle() + setupTitle() configureBackgroundColor() loadMovieDetail() loadMoviePoster() movieDetailView.startLoadingImage() } - private func setTitle() { + private func setupTitle() { self.title = dailyBoxOffice.movieName } @@ -63,14 +63,14 @@ final class MovieDetailViewController: UIViewController { if let cachedImage = ImageCacheManager.shared.object(forKey: cacheKey) { DispatchQueue.main.async { - self.setPosterImage(imageDocument, cachedImage) + self.setupPosterImage(imageDocument, cachedImage) } } else { if let imageURL = URL(string: imageDocument.imageURL), let data = try? Data(contentsOf: imageURL), let image = UIImage(data: data) { DispatchQueue.main.async { - self.setPosterImage(imageDocument, image) + self.setupPosterImage(imageDocument, image) ImageCacheManager.shared.setObject(image, forKey: cacheKey) } @@ -91,7 +91,7 @@ final class MovieDetailViewController: UIViewController { switch result { case .success(let movie): DispatchQueue.main.async { - self.setLabels(movie) + self.setupLabels(movie) } case .failure(let error): DispatchQueue.main.async { @@ -102,7 +102,7 @@ final class MovieDetailViewController: UIViewController { } extension MovieDetailViewController { - private func setPosterImage(_ imageDocument: ImageDocument, _ image: UIImage) { + private func setupPosterImage(_ imageDocument: ImageDocument, _ image: UIImage) { usleep(500000) movieDetailView.stopLoadingImage() let ratio = self.movieDetailView.posterImage.frame.width / CGFloat(integerLiteral: imageDocument.width) @@ -111,7 +111,7 @@ extension MovieDetailViewController { self.movieDetailView.posterImage.image = image } - private func setLabels(_ movie: Movie) { + private func setupLabels(_ movie: Movie) { let movieInfo = movie.movieInformationResult.movieInformation let directors = movieInfo.directors.map{ $0.peopleName }.joined(separator: ", ") var formattedOpenDate = movieInfo.openDate From 49aef8a7a2cd07c3cffe42580ce64120acd05838 Mon Sep 17 00:00:00 2001 From: serena <101619749+serena0720@users.noreply.github.com> Date: Fri, 11 Aug 2023 17:08:08 +0900 Subject: [PATCH 04/24] =?UTF-8?q?docs:=203=EC=A3=BC=EC=B0=A8=20README=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 318 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 281 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index e3367180..0e43f2ac 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ 1. [소개](#소개) 2. [팀원](#팀원) 3. [타임라인 및 핵심경험](#타임라인-핵심경험) -4. [UML & 파일트리](#UML-파일트리) +4. [파일트리](#파일트리) 5. [실행 화면](#실행-화면) 6. [트러블 슈팅](#트러블-슈팅) 7. [주요 학습 내용](#주요-학습-내용) @@ -20,17 +20,21 @@ ## 1. 📢 소개 -`오늘의 일일 박스오피스`가 궁금하신가요? +`일일 박스오피스`가 궁금하신가요? 혹은 `영화 개별 상세 조회`를 원하시나요? 저희에게 물어보세요! -✔️ 전날 기준 1~10위 박스오피스를 제공해드립니다! -✔️ 새로고침을 원하시면 리스트를 아래로 잡아 끌어주세요! +✔️ 캘린더에서 원하시는 날짜를 선택해주세요 📅
+✔️ 해당 날짜의 1️⃣~🔟위 박스오피스를 제공해드립니다!
+✔️ 새로고침을 원하시면 리스트를 아래로 잡아 끌어주세요!
+✔️ 영화 별 상세정보도 확인 가능하니 놓치지 마시고 확인해 보세요😆 > **핵심 개념** -> 오픈 API / URLSession / JSON Decoding / CodingKeys / UNIT Test / -> CollectionView / ModernCollectionView / UIActivityIndicatorView / -> UIRefreshControl / NSMutableAttributedString +> 오픈 API / URLSession / JSON Decoding / CodingKeys / UNIT Test /
+> CollectionView / ModernCollectionView / UIActivityIndicatorView /
+> UIRefreshControl / NSMutableAttributedString /
+> API KEY 발급 및 노출 방지 / Image Fetch / NSCache /
+> Modal / UICalendarView / DateManager / Image Loading View
@@ -47,7 +51,7 @@ ## 3. ⏱️ 타임라인 및 핵심경험 -> 프로젝트 기간 : 2023-07-24 ~ 2023-08-04 +> 프로젝트 기간 : 2023-07-24 ~ 2023-08-18 **타임라인** @@ -61,6 +65,11 @@ | **2023.08.02** |◽️ 스토리보드 제거 및 코드베이스 UI 구현
◽️ 일벽 박스오피스 `Cell` 생성
◽️ 일별 박스오피스 `CollectionView`로 구현 | | **2023.08.03** |◽️ `CollectionView` 의 `DataSoruce`를 `DiffableDataSource`로 변경
◽️ `CollectionView`에 `refresh control` 추가 | | **2023.08.04** |◽️ 접근성 향상을 위해 `adjustsFontSize` 적용
◽️ 에러 발생시 `Alert` 출력 | +| **2023.08.06** |◽️ `BoxOfficeService`를 사용하는 곳은 모두 의존성 주입을 받아 사용하도록 변경
◽️ 공통 `Alert` 중복 메서드 분리
◽️ 영화 상세정보 `ViewController` 생성 및 구현
◽️ 다음 이미지 검색 관련 `DTO` 추가 | +| **2023.08.07** |◽️ `NetworkManager` 로직 변경 및 싱글톤 클래스로 변경
◽️ `Dynamic Type` 적용 | +| **2023.08.08** |◽️ `Kakao Developer`에 팀 앱 생성
◽️ `KakaoAPIKey`를 `plist`에 등록 | +| **2023.08.09** |◽️ 이미지 로드 애니메이션 생성
◽️ 이미지 캐시 저장 로직 추가 | +| **2023.08.10** |◽️ 박스오피스 날짜 선택 `ViewController` 추가
◽️ `BoxOfficeService`의 날짜 로직을 `DateManager` 싱글톤 클래스로 분리 | **핵심경험** @@ -68,14 +77,13 @@ > - `Model`을 활용하여 `URLSession`으로 `JSON` 파일을 `Fetch` > - `JSON` 파일 `Decode`에 대한 `Unit Test` 작성 > - iOS 14.0 미만 버전을 위한 `CollecionView` / iOS 14.0 이상 버전을 위한 `ModernCollecionView` 구성 +> - `Kakao API Key`를 활용하여 영화 포스터 `fetch`하기 +> - `fetch`한 이미지 및 데이터를 `StackView`와 `ScrollView`에 넣기
- -## 4. 📊 UML & 파일트리 - -### UML -> 추후 업로드 예정 + +## 4. 🌲 파일트리 ### 파일트리 ``` @@ -86,18 +94,26 @@ BoxOffice ├── Base.lproj │   └── LaunchScreen.storyboard ├── Error +│   ├── AlertManager.swift │   ├── JSONDecoderError.swift │   └── NetworkManagerError.swift ├── Extension +│   ├── Bundle+.swift │   ├── JSONDecoder+.swift -│   └── String+.swift +│   ├── String+.swift +│   └── UIFont+.swift ├── Info.plist +├── KakaoAPIKey.plist ├── Model │   ├── DTO │   │   ├── BoxOffice │   │   │   ├── BoxOffice.swift │   │   │   ├── BoxOfficeResult.swift │   │   │   └── DailyBoxOffice.swift +│   │   ├── DaumSearch +│   │   │   ├── DaumSearchMainText.swift +│   │   │   ├── DaumSearchMeta.swift +│   │   │   └── ImageDocument.swift │   │   └── Movie │   │   ├── Audit.swift │   │   ├── Company.swift @@ -111,16 +127,30 @@ BoxOffice │   └── Section.swift ├── NameSpace │   ├── CustomDateFormatStyle.swift +│   ├── KakaoNameSpace.swift │   ├── KobisNameSpace.swift -│   └── MimeType.swift +│   ├── MimeType.swift +│   └── MovieDetailNameSpace.swift +├── Protocol +│   ├── CalendarViewControllerDelegate.swift +│   └── DaumSearchDocumentable.swift ├── Service │   └── BoxOfficeService.swift ├── Util +│   ├── DateManager.swift +│   ├── ImageCacheManager.swift │   └── NetworkManager.swift ├── View -│   └── BoxOfficeCell.swift +│   ├── BoxOfficeCell.swift +│   ├── Custom +│   │   ├── DetailLabel.swift +│   │   ├── LabelsStack.swift +│   │   └── TitleLabel.swift +│   └── MovieDetailView.swift └── ViewController - └── ViewController.swift + ├── BoxOfficeViewController.swift + ├── CalendarViewController.swift + └── MovieDetailViewController.swift ```
@@ -130,6 +160,9 @@ BoxOffice | 박스오피스 로딩 화면 | 박스오피스 리스트 새로고침| | :--------: | :--------: | | | | +| 이미지 로딩 화면 | 캘린더로 날짜 선택하기 | +| | | +
@@ -277,6 +310,138 @@ BoxOffice
+## API Key를 git에 노출시키지 않는 방법 +### 🔥 문제점 +- `이미지 검색 API`를 사용하기 위해 `Kakao Developer`에서 앱을 생성하여 `REST API Key`를 발급받았습니다. 발급받은 `REST API Key`를 이용해 `이미지 검색 API`를 이용하는데 성공했고, 해당 내용을 커밋하려고 했습니다. +- 변경 내역을 확인하던 중 `API Key`가 포함된 코드가 커밋된다면 이후 별도 관리를 위해 해당 코드를 제거하더라도 깃 커밋 이력에 `API Key`가 그대로 노출 되는 상황이 발생하게 됩니다. + +### 🧯 해결방법 +- 저희는 이러한 상황이 발생하지 않도록 하기 위해 `KakaoAPIKey.plist` 파일을 만들고 `gitignore`에 추가했습니다. 해당 파일은 깃을 통해 받을 수 없게 되었기 때문에 팀원에게 직접 파일 전달을 하는 방식으로 작업하게 됩니다. +- `plist`내부의 데이터는 `Bundle`을 확장하여 읽기전용 프로퍼티를 통해 가져오도록 했습니다. + ```swift + extension Bundle { + var kakaoApiKey: String { + guard let file = self.path(forResource: "KakaoAPIKey", ofType: "plist") else { return "" } + guard let resource = NSDictionary (contentsOfFile: file) else { return "" } + guard let key = resource["Authorization"] as? String else { + fatalError("KakaoAPIKey.plist에 Authorization를 설정해주세요.") + } + + return key + } + } + + enum KakaoNameSpace { + ... + static let authorization = "Authorization" + static let apiKey = Bundle.main.kakaoApiKey // Bundle에 등록된 Key를 NameSpace로 관리 + } + + // 이후 URLRequest에 header에 필요한 정보를 주입 + let headers = [ + KakaoNameSpace.authorization : KakaoNameSpace.apiKey + ] + + ``` + > `Bundle`은 실행 가능한 코드와 해당 코드의 자원을 포함하는 디렉토리입니다. + > `Bundle`은 여러가지가 있는데, 그 중 `main`은 앱이 실행되는 코드가 있는 `Bundle` 디렉토리에 접근할 수 있는 `bundle`입니다. + +
+ +## UIImageView의 Height를 동적으로 입력 +### 🔥 문제점 +- 이미지 검색 API를 통해 어떤 사이즈의 이미지를 가지 와도 이미지의 `width`는 `contentView`의 `width`와 맞추면 되었습니다. 하지만 `UIImage.contentMode`를 어떻게 조정해도 가로 혹은 세로 사이즈의 요구조건을 맞출 수 없었습니다. + +### 🧯 해결방법 +- `UIImageView.contentMode`와 상관없이, 비율을 계산하여 세로 사이즈를 조정해주기로 했습니다. 다행히도 이미지 검색 시 가로, 세로 사이즈 정보가 함께 제공되었기 때문에 어렵지 않게 높이를 동적으로 입력할 수 있었습니다. + ```swift + private func setPosterImage(_ imageDocument: ImageDocument, _ image: UIImage) { + // 비율 = UIImage 프레임 가로 ÷ 로드된 이미지 실제 가로 사이즈 + let ratio = self.movieDetailView.posterImage.frame.width / CGFloat(integerLiteral: imageDocument.width) + // 높이 = 비율 × 로드된 이미지 실제 세로 사이즈 + let height = ratio * CGFloat(integerLiteral: imageDocument.height) + self.movieDetailView.posterImage.heightAnchor.constraint(equalToConstant: height).isActive = true + self.movieDetailView.posterImage.image = image + } + ``` + +
+ +## UIFont Extension을 활용하여 Dynamic Bold Font 구현 +### 🔥 문제점 +- 특정 문자의 두께를 변경하고자 할 때 어떤 방법을 사용할 지 고민하였습니다. +- swift 기본 제공 메서드를 활용하는 방법이 있지만, 이는 `Font`의 **사이즈가 고정** 된다는 단점이 존재했습니다. + ```swift + .systemFont(ofSize: 17, weight: .bold) + ``` +- `Label`과 `Button`은 `Dynamic Type`에 대한 대응이 되어야한다고 생각했기 때문에, `Font`의 사이즈가 고정되지 않으면서 특정 `Font`의 두께를 조절할 수 있는 방법을 찾고자 하였습니다. + + +### 🧯 해결방법 +- `UIFont`를 `extension`하여 폰트를 `Custom`할 수 있다는 것을 알게되어 이를 활용하였습니다. + ```swift + extension UIFont { + static func preferredFont(for style: TextStyle, weight: Weight) -> UIFont { + let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: style) + let font = UIFont.systemFont(ofSize: descriptor.pointSize, weight: weight) + let metrics = UIFontMetrics(forTextStyle: style) + return metrics.scaledFont(for: font) + } + } + ``` + +
+ +## Singleton 구조의 DateManager로 Date 관리 로직 분리 +### 🔥 문제점 +- 이전 Step에서는 어제의 날짜 기준으로 모든 데이터를 로드하면 되었으나, 이번 Step부터는 다양한 날짜를 대응해야 했습니다. 여러 `ViewController`에서 지정 날짜를 공유해야 하는 상황에서 어떤 방식으로 대응할지 고민을 했습니다. +- `ViewController`간 날짜 정보를 주고 받을 수 있지만, 날짜 정보를 가지고 있는 `ViewController`가 모두 메모리에서 해제되는 경우 날짜 정보가 초기화 되는 위험이 있었습니다. +- 기존에 생성한 `BoxOfficeService`는 앱의 생명주기와 함께하는 구조체이기 때문에, 기존 날짜 관련 로직을 이곳에서 관리하였습니다. 하지만 날짜 관련 로직이 증가하면서 이전과 같이 `BoxOfficeService`에서 이를 모두 관리하는 것은 적절하지 않다고 생각했습니다. + +### 🧯 해결방법 +- `DateManager`를 생성하여 날짜 관련 프로퍼티를 해당 클래스에서 처리하도록 했습니다. + ```swift + class DateManager { + static private let dateFormatter = DateFormatter() + static let yesterday: Date = .now - (24 * 60 * 60) + static var selectedDate: Date = yesterday + ... + + private init() {} + } + ``` + +
+ +## UICalendarView 선택된 날짜 +### 🔥 문제점 +- 날짜선택 화면의 달력에는 현재 선택된 날짜가 미리 선택되어 있어야 한다는 내용이 있었습니다. 해당 요구사항을 구현하기 위해 `UICalendarView.Decoration`를 이용했습니다. + > 커스텀 데코레이션에 적용한 코드 + ```swift + private func customDecoration() -> UIView { + let view = UIView() + view.backgroundColor = .red + view.clipsToBounds = false + view.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + return view + } + ``` + +- 결과는 날짜의 하단 일부 영역에만 커스텀을 할 수 있을 뿐, 날짜 자체가 선택된 효과를 줄 수 없었습니다. + +### 🧯 해결방법 +- `UICalendarSelectionSingleDate`를 인스턴스화 하고(`UICalendarSelectionSingleDateDelegate`도 채택합니다.) 아래 코드를 적용하면, 원하는 효과가 적용되는 것을 확인할 수 있었습니다. + ```swift + let dateSelection = UICalendarSelectionSingleDate(delegate: self) + calendarView.selectionBehavior = dateSelection + ``` +- 추가로 아래 코드를 작성하여 캘린더뷰가 열릴 때부터 날짜가 선택된 효과를 적용할 수 있었습니다. + ```swift + dateSelection.selectedDate = DateComponents(year: year, month: month, day: day) + ``` + +
+ ## 7. 📚 주요 학습 내용 @@ -333,6 +498,32 @@ BoxOffice
+## ✏️ animatedImage를 활용하여 Loading 화면 구성 +- 이미지 로딩 화면을 구성 시 어떤 방법을 사용할 지 고민이 많았습니다. `BoxOfficeViewController`처럼 `activityIndicatorView`를 사용할 수도 있었지만, 다른 종류의 로딩화면도 구현해보고자 하였습니다. 고민을 하던 중 통상적인 앱에서 로딩화면서 움직이는 이미지를 참고하여 이와 비슷하게 구현을 해보고자 하였습니다. +- `asset`에 `gif` 이미지를 `frame`별 `png`파일로 분리하여 저장하였습니다. 이를 `UIImage`에서 `animatedImage`를 활용하여 임의의 `duration`을 지정하여 자연스럽게 움직이는 형상을 보여줄 수 있도록 하였습니다. +- 현재 저희 프로젝트에서 로딩하는 이미지는 빠른 속도로 처리가 되기 때문에 저희가 구성한 `Image Loading`화면이 짧은 찰나에 깜빡이고 사라지는 형상을 띄게 되었습니다. 저희는 오히려 이렇게 짧은 로딩화면이 `user`에게 오류가 나는 형상처럼 보여질 수 있다 생각하였습니다. 하여 이미지가 로딩 중이라는 것을 `user`에게 명시하기 위해 `usleep(500000)`을 주어 `Image Loading`의 과정이 보다 저희의 의도와 맞게끔 조정하였습니다. + +
+ +## ✏️ URLCache in Memory +- 저희는 프로젝트에 `NSCache`를 적용했지만, `URLCache`도 공부해 보았습니다. +- `URLCache`는 기본적으로 캐시 저장이 `ondisk`인 것을 확인했고, 이것을 변경하기 위해 `StoragePolicy`를 `allowedInMemoryOnly`로 지정해 보았습니다. 하지만 저희의 예상과 달리 `StoragePolicy`를 변경하였음에도 캐시 데이터가 `Memory`에 저장 되지 않았습니다. +- 아래와 같이 여러 실험 끝에 `(30 * 1024 * 1024)` 부터는 `URLCache`가 `메모리`에 저장이 되는 것을 확인할 수 있었습니다. + ``` + ------------------------------------------------------------------------------ + URLCache.shared의 memoryCapacity: 512,000 bytes + diskCapacity: 10,000,000 bytes + + CachedURLResponse의 storagePolicy가 .allowedInMemoryOnly일 때, + memoryCapacity: 10, 20 (* 1024 * 1024)일 때는 실패함. 30부터 성공. 31,457,280 bytes + 첫번째 data - 1,469,837 bytes + 두번째 data - 1,078,478 bytes + ------------------------------------------------------------------------------ + ``` +- 때문에 저희는 저장되어야하는 데이터 보다 지정 `memoryCapacity`가 클 때만 `URLCache`의 `inmemory Policy`가 적용된다고 추측했습니다. + +
+ ## 8. 💭 팀 회고 @@ -351,24 +542,77 @@ BoxOffice ## 9. 🔗 참고 링크 -- [🍎 Developer Apple - XCTFail](https://developer.apple.com/documentation/xctest/xctfail) -- [🍎 Developer Apple - URLSession](https://developer.apple.com/documentation/foundation/urlsession) -- [🍎 Developer Apple - Fetching Website Data into Memory](https://developer.apple.com/documentation/foundation/url_loading_system/fetching_website_data_into_memory) -- [🍎 Developer Apple - Escaping Closures](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/#Escaping-Closures) -- [🍎 Developer Apple: UICollectionView](https://developer.apple.com/documentation/uikit/uicollectionview) -- [🍎 Developer Apple: Modern cell configuration](https://developer.apple.com/videos/play/wwdc2020/10027/) -- [🍎 Developer Apple: Lists in UICollectionView](https://developer.apple.com/videos/play/wwdc2020/10026) -- [🍎 Developer Apple: Implementing Modern Collection Views](https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views) -- [🍎 Developer Apple: UIAlertController](https://developer.apple.com/documentation/uikit/uialertcontroller) -- [🍎 Developer Apple: Hashable](https://developer.apple.com/documentation/swift/hashable) -- [🍎 Developer Apple: Sendable](https://developer.apple.com/documentation/swift/sendable) -- [📒 Blog: SwiftUI : @escaping](https://seons-dev.tistory.com/entry/SwiftUI-escaping) -- [📒 Blog: XCTAssert Failure Messages](https://www.basbroek.nl/xctassert-asterisk) -- [📒 Blog: [Swift] 예외처리 (throws, do-catch, try) 하기](https://twih1203.medium.com/swift-예외처리-throws-do-catch-try-하기-c0f320e61f62) -- [📒 Blog: do-try-catch 유닛테스트 하기 위한 코드 + +
+🍎 Developer Apple + +- [XCTFail](https://developer.apple.com/documentation/xctest/xctfail) +- [URLSession](https://developer.apple.com/documentation/foundation/urlsession) +- [Fetching Website Data into Memory](https://developer.apple.com/documentation/foundation/url_loading_system/fetching_website_data_into_memory) +- [Escaping Closures](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/#Escaping-Closures) +- [UICollectionView](https://developer.apple.com/documentation/uikit/uicollectionview) +- [Modern cell configuration](https://developer.apple.com/videos/play/wwdc2020/10027/) +- [Lists in UICollectionView](https://developer.apple.com/videos/play/wwdc2020/10026) +- [Implementing Modern Collection Views](https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views) +- [UIAlertController](https://developer.apple.com/documentation/uikit/uialertcontroller) +- [Hashable](https://developer.apple.com/documentation/swift/hashable) +- [Sendable](https://developer.apple.com/documentation/swift/sendable) +- [Bundle](https://developer.apple.com/documentation/foundation/bundle) +- [NSCache](https://developer.apple.com/documentation/foundation/nscache) +- [URLCache](https://developer.apple.com/documentation/foundation/urlcache) +- [URLRequest.CachePolicy](https://developer.apple.com/documentation/foundation/urlrequest/cachepolicy) +- [URLCache.StoragePolicy](https://developer.apple.com/documentation/foundation/urlcache/storagepolicy) +- [UICalendarView](https://developer.apple.com/documentation/uikit/uicalendarview) +- [UICalendarView.Decoration](https://developer.apple.com/documentation/uikit/uicalendarview/decoration) +- [addTarget](https://developer.apple.com/documentation/uikit/uicontrol/1618259-addtarget) +- [addAction](https://developer.apple.com/documentation/uikit/uicontrol/3600490-addaction) +- [UIRefreshControl](https://developer.apple.com/documentation/uikit/uirefreshcontrol) + +
+ +
+ + +
+📒 Blog + +- [🌳 Cache](https://green1229.tistory.com/57) +- [🌳 NSCache vs URLCache](https://green1229.tistory.com/268) +- [이미지 캐시 처리와 NSCache](https://beenii.tistory.com/187) +- [URLSession Cahce Policy](https://inuplace.tistory.com/1232) +- [SwiftUI : @escaping](https://seons-dev.tistory.com/entry/SwiftUI-escaping) +- [XCTAssert Failure Messages](https://www.basbroek.nl/xctassert-asterisk) +- [예외처리 (throws, do-catch, try) 하기](https://twih1203.medium.com/swift-예외처리-throws-do-catch-try-하기-c0f320e61f62) +- [do-try-catch 유닛테스트 하기 위한 코드 ](https://oingbong.tistory.com/213) -- [📒 Blog: Xcode13 HTTP 통신 방법](https://jerry-bakery.tistory.com/entry/iOS-Xcode13에서-HTTP-통신-사용하는-방법Use-HTTPS-instead-or-add-Exception-Domains-to-your-apps-Infoplist) -- [📒 Blog: DiffableDataSource](https://ios-development.tistory.com/717) -- [📒 Blog: UIActivityIndicator](https://calmone.tistory.com/entry/iOS-UIKit-in-Swift-4-UIActivityIndicator-사용하기) -- [📒 Blog: UIActivityIndicatorView](https://ios-development.tistory.com/985) -- [📒 Blog: 일치하는 모든 문자열의 Attribute를 바꾸고 싶을 때](https://zeddios.tistory.com/462) \ No newline at end of file +- [Xcode13 HTTP 통신 방법](https://jerry-bakery.tistory.com/entry/iOS-Xcode13에서-HTTP-통신-사용하는-방법Use-HTTPS-instead-or-add-Exception-Domains-to-your-apps-Infoplist) +- [DiffableDataSource](https://ios-development.tistory.com/717) +- [UIActivityIndicator](https://calmone.tistory.com/entry/iOS-UIKit-in-Swift-4-UIActivityIndicator-사용하기) +- [UIActivityIndicatorView](https://ios-development.tistory.com/985) +- [일치하는 모든 문자열의 Attribute를 바꾸고 싶을 때](https://zeddios.tistory.com/462) +- [github에 올리면 안되는 APIKEY 숨기기](https://nareunhagae.tistory.com/44) +- [Dynamic Type을 지원하되, weight는 커스텀하기](https://dev-dain.tistory.com/244) +- [달력 UICalendarView Custom 예제](https://ohwhatisthis.tistory.com/23) + +
+ +
+ + +
+👾 Git + +- [토요스터디ClassC - DasanKim](https://github.com/WhalesJin/FireSaturdayStudyClassC/blob/dasan/2_week6_cache/2_week6_cache/ViewController.swift) +- [달력 로딩 이미지 애니메이션 팝업 쉽게 만들기](http://minsone.github.io/mac/ios/easy-make-loading-animation-popup-view-in-swift) + +
+ +
+ + +
+🌊 stack overflow + +- [change size of UICalanderView Decofration](https://stackoverflow.com/questions/75470155/how-to-change-size-of-customview-passed-as-uicalanderview-decoration) + +
From 2f8710c84311b967bba744adf222c44c23f070d1 Mon Sep 17 00:00:00 2001 From: Serena Date: Tue, 15 Aug 2023 10:06:54 +0900 Subject: [PATCH 05/24] =?UTF-8?q?refactor:=20BoxOfficeCell=EC=97=90?= =?UTF-8?q?=EC=84=9C=20BoxOfficeListCell=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice.xcodeproj/project.pbxproj | 8 ++++---- .../{BoxOfficeCell.swift => BoxOfficeListCell.swift} | 10 +++++----- BoxOffice/ViewController/BoxOfficeViewController.swift | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) rename BoxOffice/View/{BoxOfficeCell.swift => BoxOfficeListCell.swift} (93%) diff --git a/BoxOffice.xcodeproj/project.pbxproj b/BoxOffice.xcodeproj/project.pbxproj index 96b99d58..651709f3 100644 --- a/BoxOffice.xcodeproj/project.pbxproj +++ b/BoxOffice.xcodeproj/project.pbxproj @@ -35,7 +35,7 @@ D20B9F502A8214500035DEC4 /* KakaoNameSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20B9F4F2A8214500035DEC4 /* KakaoNameSpace.swift */; }; D20B9F522A8245130035DEC4 /* ImageCacheManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20B9F512A8245130035DEC4 /* ImageCacheManager.swift */; }; D20B9F5B2A8356A20035DEC4 /* TitleLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D20B9F5A2A8356A20035DEC4 /* TitleLabel.swift */; }; - D22F99682A79ECEE002EAE5E /* BoxOfficeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22F99672A79ECEE002EAE5E /* BoxOfficeCell.swift */; }; + D22F99682A79ECEE002EAE5E /* BoxOfficeListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D22F99672A79ECEE002EAE5E /* BoxOfficeListCell.swift */; }; D236E1952A7342FD003D0F7A /* Movie.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E1942A7342FD003D0F7A /* Movie.swift */; }; D236E1972A73430E003D0F7A /* MovieInfoResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E1962A73430E003D0F7A /* MovieInfoResult.swift */; }; D236E1992A73431B003D0F7A /* MovieInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D236E1982A73431B003D0F7A /* MovieInfo.swift */; }; @@ -101,7 +101,7 @@ D20B9F4F2A8214500035DEC4 /* KakaoNameSpace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KakaoNameSpace.swift; sourceTree = ""; }; D20B9F512A8245130035DEC4 /* ImageCacheManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCacheManager.swift; sourceTree = ""; }; D20B9F5A2A8356A20035DEC4 /* TitleLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleLabel.swift; sourceTree = ""; }; - D22F99672A79ECEE002EAE5E /* BoxOfficeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeCell.swift; sourceTree = ""; }; + D22F99672A79ECEE002EAE5E /* BoxOfficeListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeListCell.swift; sourceTree = ""; }; D236E1942A7342FD003D0F7A /* Movie.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Movie.swift; sourceTree = ""; }; D236E1962A73430E003D0F7A /* MovieInfoResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieInfoResult.swift; sourceTree = ""; }; D236E1982A73431B003D0F7A /* MovieInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieInfo.swift; sourceTree = ""; }; @@ -279,7 +279,7 @@ isa = PBXGroup; children = ( D20B9F592A8356810035DEC4 /* Custom */, - D22F99672A79ECEE002EAE5E /* BoxOfficeCell.swift */, + D22F99672A79ECEE002EAE5E /* BoxOfficeListCell.swift */, D2D027CF2A7F293A00456444 /* MovieDetailView.swift */, ); path = View; @@ -462,7 +462,7 @@ CBEF1EE12A6F8909005459DD /* BoxOffice.swift in Sources */, CB289C0D2A81C897007EBADA /* Bundle+.swift in Sources */, D20B9F4E2A820C800035DEC4 /* AlertManager.swift in Sources */, - D22F99682A79ECEE002EAE5E /* BoxOfficeCell.swift in Sources */, + D22F99682A79ECEE002EAE5E /* BoxOfficeListCell.swift in Sources */, D266DEC22A72269200DBB07F /* NetworkManager.swift in Sources */, D236E1972A73430E003D0F7A /* MovieInfoResult.swift in Sources */, CB31F7C62A738376001E9B21 /* CustomDateFormatStyle.swift in Sources */, diff --git a/BoxOffice/View/BoxOfficeCell.swift b/BoxOffice/View/BoxOfficeListCell.swift similarity index 93% rename from BoxOffice/View/BoxOfficeCell.swift rename to BoxOffice/View/BoxOfficeListCell.swift index 2947b7bf..612985ac 100644 --- a/BoxOffice/View/BoxOfficeCell.swift +++ b/BoxOffice/View/BoxOfficeListCell.swift @@ -7,8 +7,8 @@ import UIKit -final class BoxOfficeCell: UICollectionViewListCell { - static let identifier = "boxOfficeCell" +final class BoxOfficeListCell: UICollectionViewListCell { + static let identifier = "boxOfficeListCell" // MARK: - Separator private var separatorConstraint: NSLayoutConstraint? @@ -55,7 +55,7 @@ final class BoxOfficeCell: UICollectionViewListCell { override init(frame: CGRect) { super.init(frame: frame) - configureCell() + configureListCell() } required init?(coder: NSCoder) { @@ -64,8 +64,8 @@ final class BoxOfficeCell: UICollectionViewListCell { } // MARK: - Constraints -extension BoxOfficeCell { - private func configureCell() { +extension BoxOfficeListCell { + private func configureListCell() { configureUI() setupConstraints() } diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 1618e9c8..89f6d012 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -110,7 +110,7 @@ extension BoxOfficeViewController { collectionView?.delegate = self collectionView?.refreshControl = refresher collectionView?.refreshControl?.addAction(refreshData(), for: .valueChanged) - collectionView?.refreshControl?.transform = CGAffineTransformMakeScale (0.6, 0.6); + collectionView?.refreshControl?.transform = CGAffineTransformMakeScale (0.6, 0.6) view.addSubview(collectionView ?? UICollectionView()) } From a4fe9c51485826971f6dcdfcf3d6a0cda195cb1e Mon Sep 17 00:00:00 2001 From: Serena Date: Tue, 15 Aug 2023 10:11:30 +0900 Subject: [PATCH 06/24] =?UTF-8?q?feat:=20NavigationController=20Toolbar=20?= =?UTF-8?q?Button=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20Action=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewController/BoxOfficeViewController.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 89f6d012..69f0a084 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -43,6 +43,13 @@ final class BoxOfficeViewController: UIViewController { private func showLoadingView() { view.addSubview(activityIndicatorView) + navigationController?.isToolbarHidden = false + + let modeButton = UIBarButtonItem(title: "화면 모드 변경", primaryAction: showSelector()) + let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil) + + let items = [flexibleSpace, modeButton, flexibleSpace] + toolbarItems = items activityIndicatorView.center = view.center activityIndicatorView.startAnimating() @@ -167,6 +174,14 @@ extension BoxOfficeViewController { return action } + // TODO: 화면 모드 변경 구현하기 + private func showSelector() -> UIAction { + let action = UIAction() { _ in + } + + return action + } + private func refreshData() -> UIAction { let action = UIAction { _ in self.loadDailyBoxOfficeData() From 676316c0533dda772b95e8aa604f8d9712908fc2 Mon Sep 17 00:00:00 2001 From: Serena Date: Tue, 15 Aug 2023 10:15:49 +0900 Subject: [PATCH 07/24] =?UTF-8?q?refactor:=20CellRegistration=20=EC=8B=9C?= =?UTF-8?q?=20BoxOfficeCell=EC=97=90=EC=84=9C=20BoxOfficeListCell=EB=A1=9C?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/ViewController/BoxOfficeViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 69f0a084..d5285355 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -125,7 +125,7 @@ extension BoxOfficeViewController { private func configureDataSource() { guard let collectionView else { return } - let cellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in + let cellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in cell.rankLabel.text = dailyBoxOffice.rank cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) cell.detailLabel.text = self.makeDetailLabelText(in: dailyBoxOffice) From bab06cd9a152a225e0fa2b8903347b99863ea3cf Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Tue, 15 Aug 2023 10:36:23 +0900 Subject: [PATCH 08/24] =?UTF-8?q?feat:=20BoxOfficeColumnCell=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice.xcodeproj/project.pbxproj | 4 ++ BoxOffice/View/BoxOfficeColumnCell.swift | 74 ++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 BoxOffice/View/BoxOfficeColumnCell.swift diff --git a/BoxOffice.xcodeproj/project.pbxproj b/BoxOffice.xcodeproj/project.pbxproj index 651709f3..39d1ed67 100644 --- a/BoxOffice.xcodeproj/project.pbxproj +++ b/BoxOffice.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ CB30DA0D2A8480B7002DE804 /* CalendarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA0C2A8480B7002DE804 /* CalendarViewController.swift */; }; CB30DA0F2A850C1B002DE804 /* CalendarViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA0E2A850C1B002DE804 /* CalendarViewControllerDelegate.swift */; }; CB30DA112A8517C6002DE804 /* DateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA102A8517C6002DE804 /* DateManager.swift */; }; + CB30DA842A8B0976002DE804 /* BoxOfficeColumnCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA832A8B0976002DE804 /* BoxOfficeColumnCell.swift */; }; CB31F7C62A738376001E9B21 /* CustomDateFormatStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB31F7C52A738376001E9B21 /* CustomDateFormatStyle.swift */; }; CB31F7C82A738397001E9B21 /* MimeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB31F7C72A738397001E9B21 /* MimeType.swift */; }; CB4CFDD32A6FA25200288EFA /* BoxOfficeResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4CFDD22A6FA25200288EFA /* BoxOfficeResultTests.swift */; }; @@ -88,6 +89,7 @@ CB30DA0C2A8480B7002DE804 /* CalendarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarViewController.swift; sourceTree = ""; }; CB30DA0E2A850C1B002DE804 /* CalendarViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarViewControllerDelegate.swift; sourceTree = ""; }; CB30DA102A8517C6002DE804 /* DateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateManager.swift; sourceTree = ""; }; + CB30DA832A8B0976002DE804 /* BoxOfficeColumnCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeColumnCell.swift; sourceTree = ""; }; CB31F7C52A738376001E9B21 /* CustomDateFormatStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDateFormatStyle.swift; sourceTree = ""; }; CB31F7C72A738397001E9B21 /* MimeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MimeType.swift; sourceTree = ""; }; CB4CFDD12A6FA16900288EFA /* BoxOfficeTestPlan.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = BoxOfficeTestPlan.xctestplan; sourceTree = ""; }; @@ -280,6 +282,7 @@ children = ( D20B9F592A8356810035DEC4 /* Custom */, D22F99672A79ECEE002EAE5E /* BoxOfficeListCell.swift */, + CB30DA832A8B0976002DE804 /* BoxOfficeColumnCell.swift */, D2D027CF2A7F293A00456444 /* MovieDetailView.swift */, ); path = View; @@ -459,6 +462,7 @@ D2E159472A6EA6EC00052F8B /* DailyBoxOffice.swift in Sources */, D20B9F5B2A8356A20035DEC4 /* TitleLabel.swift in Sources */, CB289C022A7F319F007EBADA /* DaumSearchMainText.swift in Sources */, + CB30DA842A8B0976002DE804 /* BoxOfficeColumnCell.swift in Sources */, CBEF1EE12A6F8909005459DD /* BoxOffice.swift in Sources */, CB289C0D2A81C897007EBADA /* Bundle+.swift in Sources */, D20B9F4E2A820C800035DEC4 /* AlertManager.swift in Sources */, diff --git a/BoxOffice/View/BoxOfficeColumnCell.swift b/BoxOffice/View/BoxOfficeColumnCell.swift new file mode 100644 index 00000000..604c498d --- /dev/null +++ b/BoxOffice/View/BoxOfficeColumnCell.swift @@ -0,0 +1,74 @@ +// +// BoxOfficeColumnCell.swift +// BoxOffice +// +// Created by Serena, BMO on 2023/08/15. +// + +import UIKit + +final class BoxOfficeColumnCell: UICollectionViewListCell { + static let identifier = "boxOfficeColumnCell" + + // MARK: - InformationStackView + private let contentStackView: UIStackView = { + let stackView = UIStackView() + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.axis = .vertical + stackView.alignment = .center + + return stackView + }() + + let rankLabel: UILabel = { + let label = UILabel() + label.font = UIFont.preferredFont(forTextStyle: .title1) + label.adjustsFontForContentSizeCategory = true + label.adjustsFontSizeToFitWidth = true + + return label + }() + + let movieNameLabel: UILabel = DetailLabel() + let rankInformationLabel: UILabel = DetailLabel() + let audienceCountLabel: UILabel = DetailLabel() + + override init(frame: CGRect) { + super.init(frame: frame) + configureColumnCell() + } + + required init?(coder: NSCoder) { + fatalError("not implemnted") + } +} + +// MARK: - Constraints +extension BoxOfficeColumnCell { + private func configureColumnCell() { + configureUI() + setupConstraints() + } + + private func configureUI() { + addSubview(contentStackView) + + contentStackView.addArrangedSubview(rankLabel) + contentStackView.addArrangedSubview(movieNameLabel) + contentStackView.addArrangedSubview(rankInformationLabel) + contentStackView.addArrangedSubview(audienceCountLabel) + } + + private func setupConstraints() { + setupContentStackViewConstraints() + } + + private func setupContentStackViewConstraints() { + NSLayoutConstraint.activate([ + contentStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + contentStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + contentStackView.topAnchor.constraint(equalTo: topAnchor, constant: 8), + contentStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8) + ]) + } +} From bc06c9e8755799559f03c4ac5714456b8a7e3195 Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Tue, 15 Aug 2023 10:37:46 +0900 Subject: [PATCH 09/24] =?UTF-8?q?refactor:=20CellRegistration=EC=97=90=20B?= =?UTF-8?q?oxOfficeListCell=20=ED=83=80=EC=9E=85=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BoxOfficeViewController.swift | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index d5285355..3ebe16f3 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -104,9 +104,29 @@ final class BoxOfficeViewController: UIViewController { // MARK: - Configure CollectionView UI extension BoxOfficeViewController { - private func createLayout() -> UICollectionViewLayout { - let config = UICollectionLayoutListConfiguration(appearance: .plain) - let layout = UICollectionViewCompositionalLayout.list(using: config) + // TODO: 리스트, 컬렉션 레이아웃 코드 분리 + // 리스트 레이아웃 +// private func createLayout() -> UICollectionViewLayout { +// let config = UICollectionLayoutListConfiguration(appearance: .plain) +// let layout = UICollectionViewCompositionalLayout.list(using: config) +// +// return layout +// } + + // 컬렉션 레이아웃 + func createLayout() -> UICollectionViewLayout { + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), + heightDimension: .fractionalHeight(1.0)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + item.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8) + + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), + heightDimension: .fractionalWidth(0.5)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, + subitems: [item]) + + let section = NSCollectionLayoutSection(group: group) + let layout = UICollectionViewCompositionalLayout(section: section) return layout } @@ -125,11 +145,23 @@ extension BoxOfficeViewController { private func configureDataSource() { guard let collectionView else { return } - let cellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in + // TODO: 리스트, 컬렉션 Registration 코드 분리 + // 리스트 Registration +// let cellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in +// cell.rankLabel.text = dailyBoxOffice.rank +// cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) +// cell.detailLabel.text = self.makeDetailLabelText(in: dailyBoxOffice) +// cell.accessories = [.disclosureIndicator()] +// } + + // 컬렉션 Registration + let cellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in cell.rankLabel.text = dailyBoxOffice.rank + cell.movieNameLabel.text = dailyBoxOffice.movieName cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) - cell.detailLabel.text = self.makeDetailLabelText(in: dailyBoxOffice) - cell.accessories = [.disclosureIndicator()] + cell.audienceCountLabel.text = "오늘: \(dailyBoxOffice.audienceCount) / 총: \(dailyBoxOffice.audienceAccumulation)" + cell.layer.borderColor = UIColor.black.cgColor + cell.layer.borderWidth = 1 } dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in From 3a83403cdf35c1236f19e42d7337eaf566a328db Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Tue, 15 Aug 2023 10:38:01 +0900 Subject: [PATCH 10/24] =?UTF-8?q?chore:=20=EC=A3=BC=EC=84=9D=20=EB=82=B4?= =?UTF-8?q?=EC=9A=A9=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/View/BoxOfficeListCell.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BoxOffice/View/BoxOfficeListCell.swift b/BoxOffice/View/BoxOfficeListCell.swift index 612985ac..eb904172 100644 --- a/BoxOffice/View/BoxOfficeListCell.swift +++ b/BoxOffice/View/BoxOfficeListCell.swift @@ -1,5 +1,5 @@ // -// BoxOfficeCell.swift +// BoxOfficeListCell.swift // BoxOffice // // Created by Serena, BMO on 2023/08/02. From 04cefb4c0004009f7b41aa723c6f07e94b53c7f1 Mon Sep 17 00:00:00 2001 From: Serena Date: Tue, 15 Aug 2023 10:52:29 +0900 Subject: [PATCH 11/24] =?UTF-8?q?refactor:=20DetailLabel=20=ED=8C=8C?= =?UTF-8?q?=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/View/BoxOfficeColumnCell.swift | 11 ++++++----- BoxOffice/View/Custom/DetailLabel.swift | 5 +++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/BoxOffice/View/BoxOfficeColumnCell.swift b/BoxOffice/View/BoxOfficeColumnCell.swift index 604c498d..6943f015 100644 --- a/BoxOffice/View/BoxOfficeColumnCell.swift +++ b/BoxOffice/View/BoxOfficeColumnCell.swift @@ -16,6 +16,7 @@ final class BoxOfficeColumnCell: UICollectionViewListCell { stackView.translatesAutoresizingMaskIntoConstraints = false stackView.axis = .vertical stackView.alignment = .center + stackView.distribution = .fillEqually return stackView }() @@ -29,9 +30,9 @@ final class BoxOfficeColumnCell: UICollectionViewListCell { return label }() - let movieNameLabel: UILabel = DetailLabel() - let rankInformationLabel: UILabel = DetailLabel() - let audienceCountLabel: UILabel = DetailLabel() + let movieNameLabel: UILabel = DetailLabel(fontStyle: .body) + let rankInformationLabel: UILabel = DetailLabel(fontStyle: .body) + let audienceCountLabel: UILabel = DetailLabel(fontStyle: .caption1) override init(frame: CGRect) { super.init(frame: frame) @@ -67,8 +68,8 @@ extension BoxOfficeColumnCell { NSLayoutConstraint.activate([ contentStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), contentStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - contentStackView.topAnchor.constraint(equalTo: topAnchor, constant: 8), - contentStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8) + contentStackView.topAnchor.constraint(equalTo: topAnchor, constant: 4), + contentStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4) ]) } } diff --git a/BoxOffice/View/Custom/DetailLabel.swift b/BoxOffice/View/Custom/DetailLabel.swift index 8931ff98..4afff5ce 100644 --- a/BoxOffice/View/Custom/DetailLabel.swift +++ b/BoxOffice/View/Custom/DetailLabel.swift @@ -8,14 +8,15 @@ import UIKit final class DetailLabel: UILabel { - convenience init() { + convenience init(fontStyle: UIFont.TextStyle) { self.init(frame: CGRectZero) numberOfLines = 0 minimumScaleFactor = 0.3 - font = UIFont.preferredFont(forTextStyle: .body) + font = UIFont.preferredFont(forTextStyle: fontStyle) adjustsFontForContentSizeCategory = true adjustsFontSizeToFitWidth = true + textAlignment = .center } override init(frame: CGRect) { From 66aec3a0359a580af59a0c6c213a2096ed2d6c0c Mon Sep 17 00:00:00 2001 From: Serena Date: Tue, 15 Aug 2023 13:47:41 +0900 Subject: [PATCH 12/24] =?UTF-8?q?refactor:=20movieNameLabel=EA=B3=BC=20aud?= =?UTF-8?q?ienceCountLabel=20=EB=B6=84=EB=A6=AC=ED=95=A8=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=9D=BC=20BoxOfficeListCell=20contstraint=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/View/BoxOfficeListCell.swift | 64 ++++++++++------- BoxOffice/View/Custom/DetailLabel.swift | 1 - .../BoxOfficeViewController.swift | 71 ++++++++++--------- 3 files changed, 76 insertions(+), 60 deletions(-) diff --git a/BoxOffice/View/BoxOfficeListCell.swift b/BoxOffice/View/BoxOfficeListCell.swift index eb904172..f1d1fb7b 100644 --- a/BoxOffice/View/BoxOfficeListCell.swift +++ b/BoxOffice/View/BoxOfficeListCell.swift @@ -21,22 +21,24 @@ final class BoxOfficeListCell: UICollectionViewListCell { separatorConstraint = constraint } - // MARK: - InformationStackView - private let informationStackView: UIStackView = { + // MARK: - RankStackView + private let rankStackView: UIStackView = { let stackView = UIStackView() + stackView.axis = .vertical + stackView.alignment = .center + stackView.distribution = .fillProportionally stackView.translatesAutoresizingMaskIntoConstraints = false - stackView.axis = .horizontal + stackView.spacing = 4 return stackView }() - // MARK: - RankStackView - private let rankStackView: UIStackView = { + // MARK: - ContentStackView + private let contentStackView: UIStackView = { let stackView = UIStackView() + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.setContentHuggingPriority(.init(1), for: .vertical) stackView.axis = .vertical - stackView.alignment = .center - stackView.distribution = .fillProportionally - stackView.spacing = 4 return stackView }() @@ -50,8 +52,21 @@ final class BoxOfficeListCell: UICollectionViewListCell { return label }() - let rankInformationLabel: UILabel = DetailLabel() - let detailLabel: UILabel = DetailLabel() + let rankInformationLabel: UILabel = DetailLabel(fontStyle: .body) + + let movieNameLabel: UILabel = { + let label = UILabel() + label.font = UIFont.preferredFont(forTextStyle: .body) + label.numberOfLines = 0 + label.setContentHuggingPriority(.init(1), for: .vertical) + label.adjustsFontForContentSizeCategory = true + label.adjustsFontSizeToFitWidth = true + label.minimumScaleFactor = 0.3 + + return label + }() + + let audienceCountLabel: UILabel = DetailLabel(fontStyle: .body) override init(frame: CGRect) { super.init(frame: frame) @@ -71,35 +86,36 @@ extension BoxOfficeListCell { } private func configureUI() { - addSubview(informationStackView) - - informationStackView.addArrangedSubview(rankStackView) - informationStackView.addArrangedSubview(detailLabel) + addSubview(rankStackView) + addSubview(contentStackView) rankStackView.addArrangedSubview(rankLabel) rankStackView.addArrangedSubview(rankInformationLabel) + + contentStackView.addArrangedSubview(movieNameLabel) + contentStackView.addArrangedSubview(audienceCountLabel) } private func setupConstraints() { updateSeparatorConstraint() - setupInformationStackViewConstraints() setupRankStackViewConstraints() + setupContentStackViewViewConstraints() } - private func setupInformationStackViewConstraints() { + private func setupRankStackViewConstraints() { NSLayoutConstraint.activate([ - informationStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - informationStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), - informationStackView.topAnchor.constraint(equalTo: topAnchor, constant: 8), - informationStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8) + rankStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), + rankStackView.widthAnchor.constraint(equalTo: contentStackView.widthAnchor, multiplier: 0.15), + rankStackView.topAnchor.constraint(equalTo: contentStackView.topAnchor) ]) } - private func setupRankStackViewConstraints() { + private func setupContentStackViewViewConstraints() { NSLayoutConstraint.activate([ - rankStackView.leadingAnchor.constraint(equalTo: informationStackView.leadingAnchor), - rankStackView.widthAnchor.constraint(equalTo: informationStackView.widthAnchor, multiplier: 0.15), - rankStackView.topAnchor.constraint(equalTo: informationStackView.topAnchor) + contentStackView.leadingAnchor.constraint(equalTo: rankStackView.trailingAnchor, constant: 16), + contentStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + contentStackView.topAnchor.constraint(equalTo: topAnchor, constant: 8), + contentStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8) ]) } } diff --git a/BoxOffice/View/Custom/DetailLabel.swift b/BoxOffice/View/Custom/DetailLabel.swift index 4afff5ce..40ea4d0d 100644 --- a/BoxOffice/View/Custom/DetailLabel.swift +++ b/BoxOffice/View/Custom/DetailLabel.swift @@ -16,7 +16,6 @@ final class DetailLabel: UILabel { font = UIFont.preferredFont(forTextStyle: fontStyle) adjustsFontForContentSizeCategory = true adjustsFontSizeToFitWidth = true - textAlignment = .center } override init(frame: CGRect) { diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 3ebe16f3..4a6bb5a3 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -106,31 +106,31 @@ final class BoxOfficeViewController: UIViewController { extension BoxOfficeViewController { // TODO: 리스트, 컬렉션 레이아웃 코드 분리 // 리스트 레이아웃 -// private func createLayout() -> UICollectionViewLayout { -// let config = UICollectionLayoutListConfiguration(appearance: .plain) -// let layout = UICollectionViewCompositionalLayout.list(using: config) -// -// return layout -// } - - // 컬렉션 레이아웃 - func createLayout() -> UICollectionViewLayout { - let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), - heightDimension: .fractionalHeight(1.0)) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - item.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8) - - let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), - heightDimension: .fractionalWidth(0.5)) - let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, - subitems: [item]) + private func createLayout() -> UICollectionViewLayout { + let config = UICollectionLayoutListConfiguration(appearance: .plain) + let layout = UICollectionViewCompositionalLayout.list(using: config) - let section = NSCollectionLayoutSection(group: group) - let layout = UICollectionViewCompositionalLayout(section: section) - return layout } + // 컬렉션 레이아웃 +// func createLayout() -> UICollectionViewLayout { +// let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), +// heightDimension: .fractionalHeight(1.0)) +// let item = NSCollectionLayoutItem(layoutSize: itemSize) +// item.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8) +// +// let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), +// heightDimension: .fractionalWidth(0.5)) +// let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, +// subitems: [item]) +// +// let section = NSCollectionLayoutSection(group: group) +// let layout = UICollectionViewCompositionalLayout(section: section) +// +// return layout +// } + private func configureHierarchy() { collectionView = UICollectionView(frame: view.safeAreaLayoutGuide.layoutFrame, collectionViewLayout: createLayout()) collectionView?.autoresizingMask = [.flexibleWidth, .flexibleHeight] @@ -147,23 +147,24 @@ extension BoxOfficeViewController { // TODO: 리스트, 컬렉션 Registration 코드 분리 // 리스트 Registration -// let cellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in -// cell.rankLabel.text = dailyBoxOffice.rank -// cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) -// cell.detailLabel.text = self.makeDetailLabelText(in: dailyBoxOffice) -// cell.accessories = [.disclosureIndicator()] -// } - - // 컬렉션 Registration - let cellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in + let cellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in cell.rankLabel.text = dailyBoxOffice.rank - cell.movieNameLabel.text = dailyBoxOffice.movieName cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) - cell.audienceCountLabel.text = "오늘: \(dailyBoxOffice.audienceCount) / 총: \(dailyBoxOffice.audienceAccumulation)" - cell.layer.borderColor = UIColor.black.cgColor - cell.layer.borderWidth = 1 + cell.movieNameLabel.text = dailyBoxOffice.movieName + cell.audienceCountLabel.text = self.makeDetailLabelText(in: dailyBoxOffice) + cell.accessories = [.disclosureIndicator()] } + // 컬렉션 Registration +// let cellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in +// cell.rankLabel.text = dailyBoxOffice.rank +// cell.movieNameLabel.text = dailyBoxOffice.movieName +// cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) +// cell.audienceCountLabel.text = "오늘: \(dailyBoxOffice.audienceCount) / 총: \(dailyBoxOffice.audienceAccumulation)" +// cell.layer.borderColor = UIColor.black.cgColor +// cell.layer.borderWidth = 1 +// } + dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier) } @@ -245,6 +246,6 @@ extension BoxOfficeViewController { let audienceCount = formatter.string(for: Int(dailyBoxOffice.audienceCount)) ?? "0" let audienceAccumulation = formatter.string(for: Int(dailyBoxOffice.audienceAccumulation)) ?? "0" - return "\(dailyBoxOffice.movieName)\n오늘: \(audienceCount) / 총: \(audienceAccumulation)" + return "오늘: \(audienceCount) / 총: \(audienceAccumulation)" } } From 8c6a7daf4ba775e463010034f71b6e09edcdfb72 Mon Sep 17 00:00:00 2001 From: Serena Date: Tue, 15 Aug 2023 13:59:01 +0900 Subject: [PATCH 13/24] =?UTF-8?q?refactor:=20BoxOfficeListCell=EC=9D=98=20?= =?UTF-8?q?contentStackView=20bottom=20constraint=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/View/BoxOfficeListCell.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/BoxOffice/View/BoxOfficeListCell.swift b/BoxOffice/View/BoxOfficeListCell.swift index f1d1fb7b..281743e5 100644 --- a/BoxOffice/View/BoxOfficeListCell.swift +++ b/BoxOffice/View/BoxOfficeListCell.swift @@ -108,6 +108,11 @@ extension BoxOfficeListCell { rankStackView.widthAnchor.constraint(equalTo: contentStackView.widthAnchor, multiplier: 0.15), rankStackView.topAnchor.constraint(equalTo: contentStackView.topAnchor) ]) + + let bottomAnchor = rankStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8) + + bottomAnchor.isActive = true + bottomAnchor.priority = .defaultLow } private func setupContentStackViewViewConstraints() { @@ -115,7 +120,7 @@ extension BoxOfficeListCell { contentStackView.leadingAnchor.constraint(equalTo: rankStackView.trailingAnchor, constant: 16), contentStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), contentStackView.topAnchor.constraint(equalTo: topAnchor, constant: 8), - contentStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8) + contentStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8) ]) } } From 5bc335409955327d3a45c378e724e97b5ce4dc1d Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Tue, 15 Aug 2023 17:25:37 +0900 Subject: [PATCH 14/24] =?UTF-8?q?refactor:=20BoxOfficeColumnCell=20?= =?UTF-8?q?=EC=A0=9C=EC=95=BD=EC=82=AC=ED=95=AD=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/View/BoxOfficeColumnCell.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BoxOffice/View/BoxOfficeColumnCell.swift b/BoxOffice/View/BoxOfficeColumnCell.swift index 6943f015..a35be2d0 100644 --- a/BoxOffice/View/BoxOfficeColumnCell.swift +++ b/BoxOffice/View/BoxOfficeColumnCell.swift @@ -66,8 +66,8 @@ extension BoxOfficeColumnCell { private func setupContentStackViewConstraints() { NSLayoutConstraint.activate([ - contentStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), - contentStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + contentStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 4), + contentStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -4), contentStackView.topAnchor.constraint(equalTo: topAnchor, constant: 4), contentStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4) ]) From c9efcd021bf2be159ea1db67494545a0a5e02cf4 Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Tue, 15 Aug 2023 17:27:28 +0900 Subject: [PATCH 15/24] =?UTF-8?q?feat:=20list=20<->=20icon=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=B3=80=EA=B2=BD=20=EB=A1=9C=EC=A7=81=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BoxOfficeViewController.swift | 115 ++++++++++++------ 1 file changed, 75 insertions(+), 40 deletions(-) diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 4a6bb5a3..dea42f96 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -15,6 +15,7 @@ final class BoxOfficeViewController: UIViewController { private var dataSource: UICollectionViewDiffableDataSource? = nil private var collectionView: UICollectionView? = nil + private var viewMode: String? private let refresher = UIRefreshControl() init(boxOfficeService: BoxOfficeService) { @@ -29,6 +30,13 @@ final class BoxOfficeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + if let viewMode = UserDefaults.standard.string(forKey: "viewMode") { + self.viewMode = viewMode + } else { + viewMode = "list" + UserDefaults.standard.set(viewMode, forKey: "viewMode") + } + setupRightBarButton() showLoadingView() setupTitle() @@ -104,35 +112,33 @@ final class BoxOfficeViewController: UIViewController { // MARK: - Configure CollectionView UI extension BoxOfficeViewController { - // TODO: 리스트, 컬렉션 레이아웃 코드 분리 - // 리스트 레이아웃 - private func createLayout() -> UICollectionViewLayout { + private func createListLayout() -> UICollectionViewLayout { let config = UICollectionLayoutListConfiguration(appearance: .plain) let layout = UICollectionViewCompositionalLayout.list(using: config) return layout } - // 컬렉션 레이아웃 -// func createLayout() -> UICollectionViewLayout { -// let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), -// heightDimension: .fractionalHeight(1.0)) -// let item = NSCollectionLayoutItem(layoutSize: itemSize) -// item.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8) -// -// let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), -// heightDimension: .fractionalWidth(0.5)) -// let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, -// subitems: [item]) -// -// let section = NSCollectionLayoutSection(group: group) -// let layout = UICollectionViewCompositionalLayout(section: section) -// -// return layout -// } + private func createIconLayout() -> UICollectionViewLayout { + let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), + heightDimension: .fractionalHeight(1.0)) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + item.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8) + + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), + heightDimension: .fractionalWidth(0.5)) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, + subitems: [item]) + + let section = NSCollectionLayoutSection(group: group) + let layout = UICollectionViewCompositionalLayout(section: section) + + return layout + } private func configureHierarchy() { - collectionView = UICollectionView(frame: view.safeAreaLayoutGuide.layoutFrame, collectionViewLayout: createLayout()) + let collectionviewlayout = viewMode == "list" ? createListLayout : createIconLayout + collectionView = UICollectionView(frame: view.safeAreaLayoutGuide.layoutFrame, collectionViewLayout: collectionviewlayout()) collectionView?.autoresizingMask = [.flexibleWidth, .flexibleHeight] collectionView?.delegate = self collectionView?.refreshControl = refresher @@ -143,11 +149,7 @@ extension BoxOfficeViewController { } private func configureDataSource() { - guard let collectionView else { return } - - // TODO: 리스트, 컬렉션 Registration 코드 분리 - // 리스트 Registration - let cellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in + let listCellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in cell.rankLabel.text = dailyBoxOffice.rank cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) cell.movieNameLabel.text = dailyBoxOffice.movieName @@ -155,24 +157,39 @@ extension BoxOfficeViewController { cell.accessories = [.disclosureIndicator()] } - // 컬렉션 Registration -// let cellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in -// cell.rankLabel.text = dailyBoxOffice.rank -// cell.movieNameLabel.text = dailyBoxOffice.movieName -// cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) -// cell.audienceCountLabel.text = "오늘: \(dailyBoxOffice.audienceCount) / 총: \(dailyBoxOffice.audienceAccumulation)" -// cell.layer.borderColor = UIColor.black.cgColor -// cell.layer.borderWidth = 1 -// } + let iconCellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in + cell.rankLabel.text = dailyBoxOffice.rank + cell.movieNameLabel.text = dailyBoxOffice.movieName + cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) + cell.audienceCountLabel.text = self.makeDetailLabelText(in: dailyBoxOffice) + cell.layer.borderColor = UIColor.black.cgColor + cell.layer.borderWidth = 1 + } + guard let collectionView else { return } dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in - return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier) + if self.viewMode == "list" { + return collectionView.dequeueConfiguredReusableCell(using: listCellRegistration, for: indexPath, item: itemIdentifier) + } else { + return collectionView.dequeueConfiguredReusableCell(using: iconCellRegistration, for: indexPath, item: itemIdentifier) + } } + dataSource?.apply(setupSnapshot(), animatingDifferences: false) + } + + func setupSnapshot() -> NSDiffableDataSourceSnapshot { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.dailyBoxOffice]) - snapshot.appendItems(boxOffice?.boxOfficeResult.dailyBoxOfficeList ?? []) - dataSource?.apply(snapshot, animatingDifferences: false) + snapshot.appendItems(boxOffice?.boxOfficeResult.dailyBoxOfficeList ?? [], toSection: .dailyBoxOffice) + + return snapshot + } + + func reloadData() { + guard var updatedSnapshot = dataSource?.snapshot() else { return } + updatedSnapshot.reloadSections([.dailyBoxOffice]) + self.dataSource?.apply(updatedSnapshot, animatingDifferences: true) } } @@ -207,9 +224,27 @@ extension BoxOfficeViewController { return action } - // TODO: 화면 모드 변경 구현하기 private func showSelector() -> UIAction { - let action = UIAction() { _ in + let action = UIAction { _ in + let sheet = UIAlertController(title: "화면모드변경", message: nil, preferredStyle: .actionSheet) + if self.viewMode == "list" { + sheet.addAction(UIAlertAction(title: "아이콘", style: .default, handler: { _ in + self.viewMode = "icon" + UserDefaults.standard.set("icon", forKey: "viewMode") + self.collectionView?.collectionViewLayout = self.createIconLayout() + self.reloadData() + })) + } else { + sheet.addAction(UIAlertAction(title: "리스트", style: .default, handler: { _ in + self.viewMode = "list" + UserDefaults.standard.set("list", forKey: "viewMode") + self.collectionView?.collectionViewLayout = self.createListLayout() + self.reloadData() + })) + + } + sheet.addAction(UIAlertAction(title: "취소", style: .cancel)) + self.present(sheet, animated: true) } return action From 5bcf5689db2d20ea506259b3e5908ad3152b89cc Mon Sep 17 00:00:00 2001 From: Serena Date: Tue, 15 Aug 2023 19:57:59 +0900 Subject: [PATCH 16/24] =?UTF-8?q?chore:=20=EC=BB=A8=EB=B2=A4=EC=85=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/ViewController/BoxOfficeViewController.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index dea42f96..7ce0c4fc 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -137,8 +137,8 @@ extension BoxOfficeViewController { } private func configureHierarchy() { - let collectionviewlayout = viewMode == "list" ? createListLayout : createIconLayout - collectionView = UICollectionView(frame: view.safeAreaLayoutGuide.layoutFrame, collectionViewLayout: collectionviewlayout()) + let collectionViewLayout = viewMode == "list" ? createListLayout : createIconLayout + collectionView = UICollectionView(frame: view.safeAreaLayoutGuide.layoutFrame, collectionViewLayout: collectionViewLayout()) collectionView?.autoresizingMask = [.flexibleWidth, .flexibleHeight] collectionView?.delegate = self collectionView?.refreshControl = refresher @@ -241,7 +241,6 @@ extension BoxOfficeViewController { self.collectionView?.collectionViewLayout = self.createListLayout() self.reloadData() })) - } sheet.addAction(UIAlertAction(title: "취소", style: .cancel)) self.present(sheet, animated: true) From 1c25e16bb1828ff589d6fb3be22db997910be3ec Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Wed, 16 Aug 2023 14:16:54 +0900 Subject: [PATCH 17/24] =?UTF-8?q?feat:=20ViewMode=20=EC=97=B4=EA=B1=B0?= =?UTF-8?q?=ED=98=95,=20UserDefaults=20=ED=99=95=EC=9E=A5=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice.xcodeproj/project.pbxproj | 8 +++ BoxOffice/Extension/UserDefaults+.swift | 19 +++++++ BoxOffice/Model/ViewMode.swift | 20 +++++++ .../BoxOfficeViewController.swift | 54 +++++++++---------- 4 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 BoxOffice/Extension/UserDefaults+.swift create mode 100644 BoxOffice/Model/ViewMode.swift diff --git a/BoxOffice.xcodeproj/project.pbxproj b/BoxOffice.xcodeproj/project.pbxproj index 39d1ed67..20fd78ad 100644 --- a/BoxOffice.xcodeproj/project.pbxproj +++ b/BoxOffice.xcodeproj/project.pbxproj @@ -25,6 +25,8 @@ CB30DA0F2A850C1B002DE804 /* CalendarViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA0E2A850C1B002DE804 /* CalendarViewControllerDelegate.swift */; }; CB30DA112A8517C6002DE804 /* DateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA102A8517C6002DE804 /* DateManager.swift */; }; CB30DA842A8B0976002DE804 /* BoxOfficeColumnCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA832A8B0976002DE804 /* BoxOfficeColumnCell.swift */; }; + CB30DA862A8C925F002DE804 /* ViewMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA852A8C925F002DE804 /* ViewMode.swift */; }; + CB30DA882A8C9294002DE804 /* UserDefaults+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA872A8C9294002DE804 /* UserDefaults+.swift */; }; CB31F7C62A738376001E9B21 /* CustomDateFormatStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB31F7C52A738376001E9B21 /* CustomDateFormatStyle.swift */; }; CB31F7C82A738397001E9B21 /* MimeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB31F7C72A738397001E9B21 /* MimeType.swift */; }; CB4CFDD32A6FA25200288EFA /* BoxOfficeResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4CFDD22A6FA25200288EFA /* BoxOfficeResultTests.swift */; }; @@ -90,6 +92,8 @@ CB30DA0E2A850C1B002DE804 /* CalendarViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarViewControllerDelegate.swift; sourceTree = ""; }; CB30DA102A8517C6002DE804 /* DateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateManager.swift; sourceTree = ""; }; CB30DA832A8B0976002DE804 /* BoxOfficeColumnCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeColumnCell.swift; sourceTree = ""; }; + CB30DA852A8C925F002DE804 /* ViewMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewMode.swift; sourceTree = ""; }; + CB30DA872A8C9294002DE804 /* UserDefaults+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+.swift"; sourceTree = ""; }; CB31F7C52A738376001E9B21 /* CustomDateFormatStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDateFormatStyle.swift; sourceTree = ""; }; CB31F7C72A738397001E9B21 /* MimeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MimeType.swift; sourceTree = ""; }; CB4CFDD12A6FA16900288EFA /* BoxOfficeTestPlan.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = BoxOfficeTestPlan.xctestplan; sourceTree = ""; }; @@ -331,6 +335,7 @@ children = ( CBC61F8D2A6EA42D00FDAD18 /* DTO */, D2D027C72A7BD54E00456444 /* Section.swift */, + CB30DA852A8C925F002DE804 /* ViewMode.swift */, ); path = Model; sourceTree = ""; @@ -352,6 +357,7 @@ D2D027C92A7BD57800456444 /* String+.swift */, D2D027D12A813B7A00456444 /* UIFont+.swift */, CB289C0C2A81C897007EBADA /* Bundle+.swift */, + CB30DA872A8C9294002DE804 /* UserDefaults+.swift */, ); path = Extension; sourceTree = ""; @@ -476,12 +482,14 @@ CB30DA092A8359FF002DE804 /* DetailLabel.swift in Sources */, D2D027CA2A7BD57800456444 /* String+.swift in Sources */, CB31F7C82A738397001E9B21 /* MimeType.swift in Sources */, + CB30DA882A8C9294002DE804 /* UserDefaults+.swift in Sources */, CB30DA0F2A850C1B002DE804 /* CalendarViewControllerDelegate.swift in Sources */, D2A6AE482A73AA0100CE5903 /* NetworkManagerError.swift in Sources */, D277169E2A6FFFAC005C0045 /* JSONDecoderError.swift in Sources */, CB289C042A7F31C2007EBADA /* DaumSearchMeta.swift in Sources */, D236E1B22A7356EA003D0F7A /* KobisNameSpace.swift in Sources */, D2D027D22A813B7A00456444 /* UIFont+.swift in Sources */, + CB30DA862A8C925F002DE804 /* ViewMode.swift in Sources */, CB30DA072A83555D002DE804 /* MovieDetailNameSpace.swift in Sources */, CB30DA0D2A8480B7002DE804 /* CalendarViewController.swift in Sources */, 63DF20F32970E1A0005DF7D1 /* BoxOfficeViewController.swift in Sources */, diff --git a/BoxOffice/Extension/UserDefaults+.swift b/BoxOffice/Extension/UserDefaults+.swift new file mode 100644 index 00000000..7a9e25de --- /dev/null +++ b/BoxOffice/Extension/UserDefaults+.swift @@ -0,0 +1,19 @@ +// +// UserDefaults+.swift +// BoxOffice +// +// Created by Serena, BMO on 2023/08/16. +// + +import Foundation + +extension UserDefaults { + private enum UserDefaultsKeys: String { + case viewMode + } + + var viewMode: String { + get { string(forKey: UserDefaultsKeys.viewMode.rawValue) ?? ViewMode.list.rawValue } + set { setValue(newValue, forKey: UserDefaultsKeys.viewMode.rawValue) } + } +} diff --git a/BoxOffice/Model/ViewMode.swift b/BoxOffice/Model/ViewMode.swift new file mode 100644 index 00000000..07c1a2c6 --- /dev/null +++ b/BoxOffice/Model/ViewMode.swift @@ -0,0 +1,20 @@ +// +// ViewMode.swift +// BoxOffice +// +// Created by Serena, BMO on 2023/08/16. +// + +enum ViewMode: String { + case list + case icon + + var description: String { + switch self { + case .list: + return "리스트" + case .icon: + return "아이콘" + } + } +} diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 7ce0c4fc..8f6a63ce 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -15,9 +15,12 @@ final class BoxOfficeViewController: UIViewController { private var dataSource: UICollectionViewDiffableDataSource? = nil private var collectionView: UICollectionView? = nil - private var viewMode: String? private let refresher = UIRefreshControl() + private var viewMode: ViewMode { + return ViewMode(rawValue: UserDefaults.standard.viewMode) ?? ViewMode.list + } + init(boxOfficeService: BoxOfficeService) { self.boxOfficeService = boxOfficeService super.init(nibName: nil, bundle: nil) @@ -30,13 +33,6 @@ final class BoxOfficeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - if let viewMode = UserDefaults.standard.string(forKey: "viewMode") { - self.viewMode = viewMode - } else { - viewMode = "list" - UserDefaults.standard.set(viewMode, forKey: "viewMode") - } - setupRightBarButton() showLoadingView() setupTitle() @@ -55,7 +51,7 @@ final class BoxOfficeViewController: UIViewController { let modeButton = UIBarButtonItem(title: "화면 모드 변경", primaryAction: showSelector()) let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil) - + let items = [flexibleSpace, modeButton, flexibleSpace] toolbarItems = items @@ -115,29 +111,29 @@ extension BoxOfficeViewController { private func createListLayout() -> UICollectionViewLayout { let config = UICollectionLayoutListConfiguration(appearance: .plain) let layout = UICollectionViewCompositionalLayout.list(using: config) - + return layout } private func createIconLayout() -> UICollectionViewLayout { let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), - heightDimension: .fractionalHeight(1.0)) + heightDimension: .fractionalHeight(1.0)) let item = NSCollectionLayoutItem(layoutSize: itemSize) item.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8) - + let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), - heightDimension: .fractionalWidth(0.5)) + heightDimension: .fractionalWidth(0.5)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, - subitems: [item]) - + subitems: [item]) + let section = NSCollectionLayoutSection(group: group) let layout = UICollectionViewCompositionalLayout(section: section) - + return layout } private func configureHierarchy() { - let collectionViewLayout = viewMode == "list" ? createListLayout : createIconLayout + let collectionViewLayout = viewMode == ViewMode.list ? createListLayout : createIconLayout collectionView = UICollectionView(frame: view.safeAreaLayoutGuide.layoutFrame, collectionViewLayout: collectionViewLayout()) collectionView?.autoresizingMask = [.flexibleWidth, .flexibleHeight] collectionView?.delegate = self @@ -168,9 +164,10 @@ extension BoxOfficeViewController { guard let collectionView else { return } dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in - if self.viewMode == "list" { + switch self.viewMode { + case .list: return collectionView.dequeueConfiguredReusableCell(using: listCellRegistration, for: indexPath, item: itemIdentifier) - } else { + case .icon: return collectionView.dequeueConfiguredReusableCell(using: iconCellRegistration, for: indexPath, item: itemIdentifier) } } @@ -227,25 +224,26 @@ extension BoxOfficeViewController { private func showSelector() -> UIAction { let action = UIAction { _ in let sheet = UIAlertController(title: "화면모드변경", message: nil, preferredStyle: .actionSheet) - if self.viewMode == "list" { - sheet.addAction(UIAlertAction(title: "아이콘", style: .default, handler: { _ in - self.viewMode = "icon" - UserDefaults.standard.set("icon", forKey: "viewMode") + + switch self.viewMode { + case .list: + sheet.addAction(UIAlertAction(title: ViewMode.icon.description, style: .default, handler: { _ in + UserDefaults.standard.viewMode = ViewMode.icon.rawValue self.collectionView?.collectionViewLayout = self.createIconLayout() self.reloadData() })) - } else { - sheet.addAction(UIAlertAction(title: "리스트", style: .default, handler: { _ in - self.viewMode = "list" - UserDefaults.standard.set("list", forKey: "viewMode") + case .icon: + sheet.addAction(UIAlertAction(title: ViewMode.list.description, style: .default, handler: { _ in + UserDefaults.standard.viewMode = ViewMode.list.rawValue self.collectionView?.collectionViewLayout = self.createListLayout() self.reloadData() })) } + sheet.addAction(UIAlertAction(title: "취소", style: .cancel)) self.present(sheet, animated: true) } - + return action } From 49503c9e23617bae7fd411318e1ded22f65830b2 Mon Sep 17 00:00:00 2001 From: Serena Date: Wed, 16 Aug 2023 14:45:49 +0900 Subject: [PATCH 18/24] =?UTF-8?q?refactor:=20listCellRegistration,=20iconC?= =?UTF-8?q?ellRegistration=20=EC=9D=BD=EA=B8=B0=20=EC=A0=84=EC=9A=A9=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BoxOfficeViewController.swift | 71 ++++++++++--------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 8f6a63ce..08bc7065 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -21,6 +21,23 @@ final class BoxOfficeViewController: UIViewController { return ViewMode(rawValue: UserDefaults.standard.viewMode) ?? ViewMode.list } + private lazy var listCellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in + cell.rankLabel.text = dailyBoxOffice.rank + cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) + cell.movieNameLabel.text = dailyBoxOffice.movieName + cell.audienceCountLabel.text = self.makeDetailLabelText(in: dailyBoxOffice) + cell.accessories = [.disclosureIndicator()] + } + + private lazy var iconCellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in + cell.rankLabel.text = dailyBoxOffice.rank + cell.movieNameLabel.text = dailyBoxOffice.movieName + cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) + cell.audienceCountLabel.text = self.makeDetailLabelText(in: dailyBoxOffice) + cell.layer.borderColor = UIColor.black.cgColor + cell.layer.borderWidth = 1 + } + init(boxOfficeService: BoxOfficeService) { self.boxOfficeService = boxOfficeService super.init(nibName: nil, bundle: nil) @@ -143,39 +160,29 @@ extension BoxOfficeViewController { view.addSubview(collectionView ?? UICollectionView()) } - +} + +// MARK: - Handling Data +extension BoxOfficeViewController { private func configureDataSource() { - let listCellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in - cell.rankLabel.text = dailyBoxOffice.rank - cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) - cell.movieNameLabel.text = dailyBoxOffice.movieName - cell.audienceCountLabel.text = self.makeDetailLabelText(in: dailyBoxOffice) - cell.accessories = [.disclosureIndicator()] - } + guard let collectionView else { return } - let iconCellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in - cell.rankLabel.text = dailyBoxOffice.rank - cell.movieNameLabel.text = dailyBoxOffice.movieName - cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) - cell.audienceCountLabel.text = self.makeDetailLabelText(in: dailyBoxOffice) - cell.layer.borderColor = UIColor.black.cgColor - cell.layer.borderWidth = 1 - } + let listCell = listCellRegistration + let iconCell = iconCellRegistration - guard let collectionView else { return } dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in switch self.viewMode { case .list: - return collectionView.dequeueConfiguredReusableCell(using: listCellRegistration, for: indexPath, item: itemIdentifier) + return collectionView.dequeueConfiguredReusableCell(using: listCell, for: indexPath, item: itemIdentifier) case .icon: - return collectionView.dequeueConfiguredReusableCell(using: iconCellRegistration, for: indexPath, item: itemIdentifier) + return collectionView.dequeueConfiguredReusableCell(using: iconCell, for: indexPath, item: itemIdentifier) } } - dataSource?.apply(setupSnapshot(), animatingDifferences: false) + dataSource?.apply(setupSnapshot(), animatingDifferences: true) } - func setupSnapshot() -> NSDiffableDataSourceSnapshot { + private func setupSnapshot() -> NSDiffableDataSourceSnapshot { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.dailyBoxOffice]) snapshot.appendItems(boxOffice?.boxOfficeResult.dailyBoxOfficeList ?? [], toSection: .dailyBoxOffice) @@ -183,11 +190,19 @@ extension BoxOfficeViewController { return snapshot } - func reloadData() { + private func reloadSnapshot() { guard var updatedSnapshot = dataSource?.snapshot() else { return } updatedSnapshot.reloadSections([.dailyBoxOffice]) self.dataSource?.apply(updatedSnapshot, animatingDifferences: true) } + + private func refreshData() -> UIAction { + let action = UIAction { _ in + self.loadDailyBoxOfficeData() + } + + return action + } } // MARK: - CollectionView Delegate @@ -230,13 +245,13 @@ extension BoxOfficeViewController { sheet.addAction(UIAlertAction(title: ViewMode.icon.description, style: .default, handler: { _ in UserDefaults.standard.viewMode = ViewMode.icon.rawValue self.collectionView?.collectionViewLayout = self.createIconLayout() - self.reloadData() + self.reloadSnapshot() })) case .icon: sheet.addAction(UIAlertAction(title: ViewMode.list.description, style: .default, handler: { _ in UserDefaults.standard.viewMode = ViewMode.list.rawValue self.collectionView?.collectionViewLayout = self.createListLayout() - self.reloadData() + self.reloadSnapshot() })) } @@ -247,14 +262,6 @@ extension BoxOfficeViewController { return action } - private func refreshData() -> UIAction { - let action = UIAction { _ in - self.loadDailyBoxOfficeData() - } - - return action - } - private func changeRankInformation(in dailyBoxOffice: DailyBoxOffice) -> NSMutableAttributedString { if dailyBoxOffice.rankOldAndNew == "NEW" { return "신작".addAttributeFontForKeyword(keyword: "신작", color: .red) From a20303784ab09a65d8923cf460f52589d4037c4b Mon Sep 17 00:00:00 2001 From: Serena Date: Wed, 16 Aug 2023 14:57:54 +0900 Subject: [PATCH 19/24] =?UTF-8?q?refactor:=20collectionView=20=EB=AA=A8?= =?UTF-8?q?=EB=93=9C=20=EB=B3=80=EA=B2=BD=20=EC=8B=9C=20=EC=95=A0=EB=8B=88?= =?UTF-8?q?=EB=A9=94=EC=9D=B4=EC=85=98=20=ED=9A=A8=EA=B3=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/ViewController/BoxOfficeViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 08bc7065..3a03f524 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -244,13 +244,13 @@ extension BoxOfficeViewController { case .list: sheet.addAction(UIAlertAction(title: ViewMode.icon.description, style: .default, handler: { _ in UserDefaults.standard.viewMode = ViewMode.icon.rawValue - self.collectionView?.collectionViewLayout = self.createIconLayout() + self.collectionView?.setCollectionViewLayout(self.createIconLayout(), animated: true) self.reloadSnapshot() })) case .icon: sheet.addAction(UIAlertAction(title: ViewMode.list.description, style: .default, handler: { _ in UserDefaults.standard.viewMode = ViewMode.list.rawValue - self.collectionView?.collectionViewLayout = self.createListLayout() + self.collectionView?.setCollectionViewLayout(self.createListLayout(), animated: true) self.reloadSnapshot() })) } From 6decb2d2436dca29cef3dff5178acd46cf3aeae4 Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Wed, 16 Aug 2023 15:30:10 +0900 Subject: [PATCH 20/24] =?UTF-8?q?refactor:=20=EC=A4=91=EB=B3=B5=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BoxOfficeViewController.swift | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 3a03f524..4888d98e 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -107,9 +107,7 @@ final class BoxOfficeViewController: UIViewController { } } else { DispatchQueue.main.async { - var snapshot = NSDiffableDataSourceSnapshot() - snapshot.appendSections([.dailyBoxOffice]) - snapshot.appendItems(boxOffice.boxOfficeResult.dailyBoxOfficeList) + let snapshot = self.setupSnapshot() self.dataSource?.apply(snapshot, animatingDifferences: false) self.refresher.endRefreshing() @@ -239,25 +237,27 @@ extension BoxOfficeViewController { private func showSelector() -> UIAction { let action = UIAction { _ in let sheet = UIAlertController(title: "화면모드변경", message: nil, preferredStyle: .actionSheet) - + sheet.addAction(self.createViewModeChangeAction()) + sheet.addAction(UIAlertAction(title: "취소", style: .cancel)) + self.present(sheet, animated: true) + } + + return action + } + + private func createViewModeChangeAction() -> UIAlertAction { + let action = UIAlertAction(title: viewMode.otherOption, style: .default, handler: { _ in switch self.viewMode { case .list: - sheet.addAction(UIAlertAction(title: ViewMode.icon.description, style: .default, handler: { _ in - UserDefaults.standard.viewMode = ViewMode.icon.rawValue - self.collectionView?.setCollectionViewLayout(self.createIconLayout(), animated: true) - self.reloadSnapshot() - })) + self.collectionView?.setCollectionViewLayout(self.createIconLayout(), animated: true) + UserDefaults.standard.viewMode = ViewMode.icon.rawValue case .icon: - sheet.addAction(UIAlertAction(title: ViewMode.list.description, style: .default, handler: { _ in - UserDefaults.standard.viewMode = ViewMode.list.rawValue - self.collectionView?.setCollectionViewLayout(self.createListLayout(), animated: true) - self.reloadSnapshot() - })) + self.collectionView?.setCollectionViewLayout(self.createListLayout(), animated: true) + UserDefaults.standard.viewMode = ViewMode.list.rawValue } - sheet.addAction(UIAlertAction(title: "취소", style: .cancel)) - self.present(sheet, animated: true) - } + self.reloadSnapshot() + }) return action } From 2476fff851ff69c3f4150116210632f9036642dd Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Wed, 16 Aug 2023 15:31:44 +0900 Subject: [PATCH 21/24] =?UTF-8?q?refactor:=20=EC=BB=A4=EB=B0=8B=20?= =?UTF-8?q?=EB=88=84=EB=9D=BD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/ViewMode.swift | 6 +++--- BoxOffice/ViewController/BoxOfficeViewController.swift | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BoxOffice/Model/ViewMode.swift b/BoxOffice/Model/ViewMode.swift index 07c1a2c6..25913b25 100644 --- a/BoxOffice/Model/ViewMode.swift +++ b/BoxOffice/Model/ViewMode.swift @@ -9,12 +9,12 @@ enum ViewMode: String { case list case icon - var description: String { + var anotherOption: String { switch self { case .list: - return "리스트" - case .icon: return "아이콘" + case .icon: + return "리스트" } } } diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 4888d98e..899f0fbf 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -246,7 +246,7 @@ extension BoxOfficeViewController { } private func createViewModeChangeAction() -> UIAlertAction { - let action = UIAlertAction(title: viewMode.otherOption, style: .default, handler: { _ in + let action = UIAlertAction(title: viewMode.anotherOption, style: .default, handler: { _ in switch self.viewMode { case .list: self.collectionView?.setCollectionViewLayout(self.createIconLayout(), animated: true) From 04f4e8ad9e479bb5070d5594ad78f9e6c9045c01 Mon Sep 17 00:00:00 2001 From: Serena Date: Wed, 16 Aug 2023 17:01:42 +0900 Subject: [PATCH 22/24] =?UTF-8?q?refactor:=20BoxOfficeIconCell=EB=A1=9C=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice.xcodeproj/project.pbxproj | 8 ++++---- ...fficeColumnCell.swift => BoxOfficeIconCell.swift} | 12 ++++++------ .../ViewController/BoxOfficeViewController.swift | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) rename BoxOffice/View/{BoxOfficeColumnCell.swift => BoxOfficeIconCell.swift} (89%) diff --git a/BoxOffice.xcodeproj/project.pbxproj b/BoxOffice.xcodeproj/project.pbxproj index 20fd78ad..ae2b9bcb 100644 --- a/BoxOffice.xcodeproj/project.pbxproj +++ b/BoxOffice.xcodeproj/project.pbxproj @@ -24,7 +24,7 @@ CB30DA0D2A8480B7002DE804 /* CalendarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA0C2A8480B7002DE804 /* CalendarViewController.swift */; }; CB30DA0F2A850C1B002DE804 /* CalendarViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA0E2A850C1B002DE804 /* CalendarViewControllerDelegate.swift */; }; CB30DA112A8517C6002DE804 /* DateManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA102A8517C6002DE804 /* DateManager.swift */; }; - CB30DA842A8B0976002DE804 /* BoxOfficeColumnCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA832A8B0976002DE804 /* BoxOfficeColumnCell.swift */; }; + CB30DA842A8B0976002DE804 /* BoxOfficeIconCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA832A8B0976002DE804 /* BoxOfficeIconCell.swift */; }; CB30DA862A8C925F002DE804 /* ViewMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA852A8C925F002DE804 /* ViewMode.swift */; }; CB30DA882A8C9294002DE804 /* UserDefaults+.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB30DA872A8C9294002DE804 /* UserDefaults+.swift */; }; CB31F7C62A738376001E9B21 /* CustomDateFormatStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB31F7C52A738376001E9B21 /* CustomDateFormatStyle.swift */; }; @@ -91,7 +91,7 @@ CB30DA0C2A8480B7002DE804 /* CalendarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarViewController.swift; sourceTree = ""; }; CB30DA0E2A850C1B002DE804 /* CalendarViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarViewControllerDelegate.swift; sourceTree = ""; }; CB30DA102A8517C6002DE804 /* DateManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateManager.swift; sourceTree = ""; }; - CB30DA832A8B0976002DE804 /* BoxOfficeColumnCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeColumnCell.swift; sourceTree = ""; }; + CB30DA832A8B0976002DE804 /* BoxOfficeIconCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeIconCell.swift; sourceTree = ""; }; CB30DA852A8C925F002DE804 /* ViewMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewMode.swift; sourceTree = ""; }; CB30DA872A8C9294002DE804 /* UserDefaults+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+.swift"; sourceTree = ""; }; CB31F7C52A738376001E9B21 /* CustomDateFormatStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDateFormatStyle.swift; sourceTree = ""; }; @@ -286,7 +286,7 @@ children = ( D20B9F592A8356810035DEC4 /* Custom */, D22F99672A79ECEE002EAE5E /* BoxOfficeListCell.swift */, - CB30DA832A8B0976002DE804 /* BoxOfficeColumnCell.swift */, + CB30DA832A8B0976002DE804 /* BoxOfficeIconCell.swift */, D2D027CF2A7F293A00456444 /* MovieDetailView.swift */, ); path = View; @@ -468,7 +468,7 @@ D2E159472A6EA6EC00052F8B /* DailyBoxOffice.swift in Sources */, D20B9F5B2A8356A20035DEC4 /* TitleLabel.swift in Sources */, CB289C022A7F319F007EBADA /* DaumSearchMainText.swift in Sources */, - CB30DA842A8B0976002DE804 /* BoxOfficeColumnCell.swift in Sources */, + CB30DA842A8B0976002DE804 /* BoxOfficeIconCell.swift in Sources */, CBEF1EE12A6F8909005459DD /* BoxOffice.swift in Sources */, CB289C0D2A81C897007EBADA /* Bundle+.swift in Sources */, D20B9F4E2A820C800035DEC4 /* AlertManager.swift in Sources */, diff --git a/BoxOffice/View/BoxOfficeColumnCell.swift b/BoxOffice/View/BoxOfficeIconCell.swift similarity index 89% rename from BoxOffice/View/BoxOfficeColumnCell.swift rename to BoxOffice/View/BoxOfficeIconCell.swift index a35be2d0..8e37c272 100644 --- a/BoxOffice/View/BoxOfficeColumnCell.swift +++ b/BoxOffice/View/BoxOfficeIconCell.swift @@ -1,5 +1,5 @@ // -// BoxOfficeColumnCell.swift +// BoxOfficeIconCell.swift // BoxOffice // // Created by Serena, BMO on 2023/08/15. @@ -7,8 +7,8 @@ import UIKit -final class BoxOfficeColumnCell: UICollectionViewListCell { - static let identifier = "boxOfficeColumnCell" +final class BoxOfficeIconCell: UICollectionViewListCell { + static let identifier = "boxOfficeIconCell" // MARK: - InformationStackView private let contentStackView: UIStackView = { @@ -36,7 +36,7 @@ final class BoxOfficeColumnCell: UICollectionViewListCell { override init(frame: CGRect) { super.init(frame: frame) - configureColumnCell() + configureIconCell() } required init?(coder: NSCoder) { @@ -45,8 +45,8 @@ final class BoxOfficeColumnCell: UICollectionViewListCell { } // MARK: - Constraints -extension BoxOfficeColumnCell { - private func configureColumnCell() { +extension BoxOfficeIconCell { + private func configureIconCell() { configureUI() setupConstraints() } diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 899f0fbf..8a53b94f 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -29,7 +29,7 @@ final class BoxOfficeViewController: UIViewController { cell.accessories = [.disclosureIndicator()] } - private lazy var iconCellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in + private lazy var iconCellRegistration = UICollectionView.CellRegistration { (cell, indexPath, dailyBoxOffice) in cell.rankLabel.text = dailyBoxOffice.rank cell.movieNameLabel.text = dailyBoxOffice.movieName cell.rankInformationLabel.attributedText = self.changeRankInformation(in: dailyBoxOffice) From 1f14cf0ff5dd3f64d99691f01a4922a0ed56c697 Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Fri, 18 Aug 2023 16:47:52 +0900 Subject: [PATCH 23/24] =?UTF-8?q?refactor:=20=ED=98=84=EC=9E=AC=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EC=9C=84=EC=B9=98=EB=A5=BC=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EB=AA=A8=EB=93=9C=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=8B=9C=20=EB=B0=98=EC=98=81=EB=90=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ViewController/BoxOfficeViewController.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index 8a53b94f..cd56e1c6 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -247,6 +247,17 @@ extension BoxOfficeViewController { private func createViewModeChangeAction() -> UIAlertAction { let action = UIAlertAction(title: viewMode.anotherOption, style: .default, handler: { _ in + var visibleIndexPath: [IndexPath] = [] + if let visibleCell = self.collectionView?.indexPathsForVisibleItems { + visibleIndexPath = visibleCell.sorted { + if $0[0] == $1[0] { + return $0[1] < $1[1] + } + + return $0[0] < $1[0] + } + } + switch self.viewMode { case .list: self.collectionView?.setCollectionViewLayout(self.createIconLayout(), animated: true) @@ -257,6 +268,10 @@ extension BoxOfficeViewController { } self.reloadSnapshot() + + if visibleIndexPath.isEmpty == false { + self.collectionView?.scrollToItem(at: visibleIndexPath[0], at: .top, animated: true) + } }) return action From 702392210b5b09181c3e7013ab53ba218f85af35 Mon Sep 17 00:00:00 2001 From: bubblecocoa Date: Fri, 18 Aug 2023 16:54:33 +0900 Subject: [PATCH 24/24] =?UTF-8?q?refactor:=20ViewMode=EC=9D=98=20case?= =?UTF-8?q?=EA=B0=80=20toggle=20=EA=B0=92=EC=9D=84=20=EA=B0=80=EC=A7=80?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EC=9B=90=EC=8B=9C=EA=B0=92=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/ViewMode.swift | 10 +++++----- BoxOffice/ViewController/BoxOfficeViewController.swift | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/BoxOffice/Model/ViewMode.swift b/BoxOffice/Model/ViewMode.swift index 25913b25..d2428ee3 100644 --- a/BoxOffice/Model/ViewMode.swift +++ b/BoxOffice/Model/ViewMode.swift @@ -6,15 +6,15 @@ // enum ViewMode: String { - case list - case icon + case list = "리스트" + case icon = "아이콘" - var anotherOption: String { + var toggle: ViewMode { switch self { case .list: - return "아이콘" + return .icon case .icon: - return "리스트" + return .list } } } diff --git a/BoxOffice/ViewController/BoxOfficeViewController.swift b/BoxOffice/ViewController/BoxOfficeViewController.swift index cd56e1c6..12c34276 100644 --- a/BoxOffice/ViewController/BoxOfficeViewController.swift +++ b/BoxOffice/ViewController/BoxOfficeViewController.swift @@ -246,7 +246,7 @@ extension BoxOfficeViewController { } private func createViewModeChangeAction() -> UIAlertAction { - let action = UIAlertAction(title: viewMode.anotherOption, style: .default, handler: { _ in + let action = UIAlertAction(title: viewMode.toggle.rawValue, style: .default, handler: { _ in var visibleIndexPath: [IndexPath] = [] if let visibleCell = self.collectionView?.indexPathsForVisibleItems { visibleIndexPath = visibleCell.sorted {