From 526535f760a89c3a8c72b103035dead80272ab35 Mon Sep 17 00:00:00 2001 From: Tedd Date: Wed, 18 Jun 2025 11:16:45 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=ED=94=8C=EB=A0=88=EC=9D=B4=EC=96=B4?= =?UTF-8?q?=EB=B7=B0=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FullscreenPlayerViewController.swift | 4 - .../PlayerViewController+Gestures.swift | 9 -- .../Player/PlayerViewController+UI.swift | 2 +- ...layerViewController+collectionViewUI.swift | 123 ++++++++++++------ .../Views/Player/PlayerViewController.swift | 32 ++++- 5 files changed, 115 insertions(+), 55 deletions(-) diff --git a/PickaView/Views/Player/FullscreenPlayerViewController.swift b/PickaView/Views/Player/FullscreenPlayerViewController.swift index e1550f4..2d510f5 100644 --- a/PickaView/Views/Player/FullscreenPlayerViewController.swift +++ b/PickaView/Views/Player/FullscreenPlayerViewController.swift @@ -149,10 +149,6 @@ class FullscreenPlayerViewController: UIViewController { /// 시스템 제스처 연기 (모든 엣지) override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge { .all } - override var supportedInterfaceOrientations: UIInterfaceOrientationMask { - return .landscape - } - override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { return .landscapeRight } diff --git a/PickaView/Views/Player/PlayerViewController+Gestures.swift b/PickaView/Views/Player/PlayerViewController+Gestures.swift index 75f4dfe..55e32ac 100644 --- a/PickaView/Views/Player/PlayerViewController+Gestures.swift +++ b/PickaView/Views/Player/PlayerViewController+Gestures.swift @@ -141,16 +141,7 @@ extension PlayerViewController: UIGestureRecognizerDelegate { } } - /// 위로 스와이프: 전체화면 진입(세로모드일 때만) @objc func handleSwipeToFullscreen(_ gesture: UISwipeGestureRecognizer) { - guard !isFullscreenMode else { return } - let isPortrait: Bool - if let orientation = view.window?.windowScene?.interfaceOrientation { - isPortrait = orientation.isPortrait - } else { - isPortrait = UIDevice.current.orientation.isPortrait - } - guard isPortrait else { return } presentFullscreen() } diff --git a/PickaView/Views/Player/PlayerViewController+UI.swift b/PickaView/Views/Player/PlayerViewController+UI.swift index aab0b15..076120c 100644 --- a/PickaView/Views/Player/PlayerViewController+UI.swift +++ b/PickaView/Views/Player/PlayerViewController+UI.swift @@ -95,7 +95,7 @@ extension PlayerViewController { /// 가로/세로 모드에 따라 제약조건 전환 func updateConstraintsForOrientation() { let isLandscape = UIDevice.current.orientation.isLandscape - if isFullscreenMode || isLandscape { + if isFullscreenMode { NSLayoutConstraint.deactivate(portraitConstraints) NSLayoutConstraint.activate(landscapeConstraints) collectionView.isHidden = true diff --git a/PickaView/Views/Player/PlayerViewController+collectionViewUI.swift b/PickaView/Views/Player/PlayerViewController+collectionViewUI.swift index c955ee7..92ea1e0 100644 --- a/PickaView/Views/Player/PlayerViewController+collectionViewUI.swift +++ b/PickaView/Views/Player/PlayerViewController+collectionViewUI.swift @@ -9,12 +9,19 @@ import UIKit /// PlayerViewController의 UICollectionView에 대한 UI 구성 및 레이아웃 설정을 담당 extension PlayerViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + + func collectionView( + _ collectionView: UICollectionView, + numberOfItemsInSection section: Int + ) -> Int { return 11 } // 각 인덱스에 맞는 셀 구성: 0번은 가로 스크롤 태그 셀, 나머지는 비디오 셀 - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + func collectionView( + _ collectionView: UICollectionView, + cellForItemAt indexPath: IndexPath + ) -> UICollectionViewCell { guard let viewModel else { fatalError("viewModel nil") } if indexPath.item == 0 { let cell = collectionView.dequeueReusableCell( @@ -38,9 +45,11 @@ extension PlayerViewController: UICollectionViewDataSource, UICollectionViewDele } // 섹션 헤더 뷰의 크기 설정 (사용자 정보 + 좋아요 등) - func collectionView(_ collectionView: UICollectionView, - layout collectionViewLayout: UICollectionViewLayout, - referenceSizeForHeaderInSection section: Int) -> CGSize { + func collectionView( + _ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + referenceSizeForHeaderInSection section: Int + ) -> CGSize { let width = collectionView.bounds.width let font = UIFont.preferredFont(forTextStyle: .footnote) @@ -61,9 +70,11 @@ extension PlayerViewController: UICollectionViewDataSource, UICollectionViewDele } // 섹션 헤더 뷰를 생성 및 설정 - func collectionView(_ collectionView: UICollectionView, - viewForSupplementaryElementOfKind kind: String, - at indexPath: IndexPath) -> UICollectionReusableView { + func collectionView( + _ collectionView: UICollectionView, + viewForSupplementaryElementOfKind kind: String, + at indexPath: IndexPath + ) -> UICollectionReusableView { if kind == UICollectionView.elementKindSectionHeader { guard let viewModel else { fatalError("viewModel nil") } let header = collectionView.dequeueReusableSupplementaryView( @@ -87,57 +98,59 @@ extension PlayerViewController: UICollectionViewDataSource, UICollectionViewDele } // 각 셀의 크기 계산 - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + func collectionView( + _ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + sizeForItemAt indexPath: IndexPath + ) -> CGSize { + // 0번 인덱스는 태그 셀이므로 기존 로직을 유지합니다. if indexPath.item == 0 { let font = UIFont.preferredFont(forTextStyle: .body) let text = "Sample" as NSString let labelHeight = text.size(withAttributes: [.font: font]).height let insets: CGFloat = 8 - let height = ceil(labelHeight) + (insets * 2) return CGSize(width: collectionView.bounds.width, height: height) } else { - let width = collectionView.bounds.width - - var itemsPerRow: CGFloat = 1 - var insets: CGFloat = 10 - - let isPad = traitCollection.userInterfaceIdiom == .pad - - if isPad { - itemsPerRow = 2 - } else { - itemsPerRow = 1 - insets = 0 - } - - let spacing: CGFloat = 10 - let totalSpacing = spacing * (itemsPerRow - 1) + insets * 2 - let itemWidth = (width - totalSpacing) / itemsPerRow - - return CGSize(width: itemWidth, height: (itemWidth * 9 / 16) + (itemWidth * 1 / 6) + 8) + return calculateVideoCellSize( + for: collectionView, + layout: collectionViewLayout, + at: indexPath + ) } } // 셀 간 세로 간격 설정 - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + func collectionView( + _ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + minimumLineSpacingForSectionAt section: Int + ) -> CGFloat { return 30 } // 셀 간 가로 간격 설정 - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + func collectionView( + _ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + minimumInteritemSpacingForSectionAt section: Int + ) -> CGFloat { return 10 } // 디바이스 방향과 크기에 따라 섹션 여백 설정 - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + func collectionView( + _ collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + insetForSectionAt section: Int + ) -> UIEdgeInsets { let width = collectionView.bounds.width let height = collectionView.bounds.height let isPhonePortrait = traitCollection.userInterfaceIdiom == .phone && width < height - return isPhonePortrait ? .zero : UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10) + return isPhonePortrait ? .zero : .init(horizontal: 10) } // 셀 선택 시 동작 처리 @@ -160,15 +173,51 @@ extension PlayerViewController: UICollectionViewDataSource, UICollectionViewDele guard let self, let viewModel = self.viewModel else { return } let storyboard = UIStoryboard(name: "Player", bundle: nil) - guard let newPlayerVC = storyboard.instantiateViewController(withIdentifier: String(describing: PlayerViewController.self)) as? PlayerViewController else { - return - } + guard let newPlayerVC = storyboard.instantiateViewController( + withIdentifier: String( + describing: PlayerViewController.self + ) + ) as? PlayerViewController else { return } newPlayerVC.modalPresentationStyle = .fullScreen - let newPlayerVM = PlayerViewModel(video: video, coreDataManager: viewModel.getCoreDataManager()) + let newPlayerVM = PlayerViewModel( + video: video, + coreDataManager: viewModel.getCoreDataManager() + ) newPlayerVC.viewModel = newPlayerVM presentingVC.present(newPlayerVC, animated: false) } } + + private func calculateVideoCellSize( + for collectionView: UICollectionView, + layout collectionViewLayout: UICollectionViewLayout, + at indexPath: IndexPath + ) -> CGSize { + let insets = self.collectionView( + collectionView, + layout: collectionViewLayout, + insetForSectionAt: indexPath.section + ) + let interitemSpacing = self.collectionView( + collectionView, + layout: collectionViewLayout, + minimumInteritemSpacingForSectionAt: indexPath.section + ) + + let isPad = traitCollection.userInterfaceIdiom == .pad + + let itemsPerRow: CGFloat = isPad ? 2 : 1 + + let totalHorizontalSpacing = insets.left + insets.right + (interitemSpacing * (itemsPerRow - 1)) + + let itemWidth = (collectionView.bounds.width - totalHorizontalSpacing) / itemsPerRow + + let thumbnailHeight = itemWidth * 9 / 16 + let userInfoHeight = itemWidth * 1 / 6 + let itemHeight = thumbnailHeight + userInfoHeight + 8 + + return CGSize(width: itemWidth, height: itemHeight) + } } diff --git a/PickaView/Views/Player/PlayerViewController.swift b/PickaView/Views/Player/PlayerViewController.swift index 9e0591f..aafb0e0 100644 --- a/PickaView/Views/Player/PlayerViewController.swift +++ b/PickaView/Views/Player/PlayerViewController.swift @@ -25,6 +25,8 @@ class PlayerViewController: UIViewController, PlayerViewControllerDelegate { @IBOutlet weak var rateTwoView: UIView! var viewModel: PlayerViewModel! + + private var videoPlayerHeightConstraint: NSLayoutConstraint? // MARK: - Player Properties @@ -163,6 +165,7 @@ class PlayerViewController: UIViewController, PlayerViewControllerDelegate { override func viewDidLoad() { super.viewDidLoad() + setupPlayerHieght(for: view.bounds.size) // 백그라운드 실행 금지 try? AVAudioSession.sharedInstance().setCategory(.ambient, mode: .moviePlayback, options: []) @@ -235,8 +238,20 @@ class PlayerViewController: UIViewController, PlayerViewControllerDelegate { override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() playerLayer?.frame = videoContainerView.bounds + collectionView.collectionViewLayout.invalidateLayout() } + override func viewWillTransition( + to size: CGSize, + with coordinator: any UIViewControllerTransitionCoordinator + ) { + guard isViewLoaded else { return } + coordinator.animate(alongsideTransition: { [weak self] _ in + self?.collectionView.collectionViewLayout.invalidateLayout() + }) + setupPlayerHieght(for: size) + } + override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) @@ -477,10 +492,19 @@ class PlayerViewController: UIViewController, PlayerViewControllerDelegate { UIViewController.attemptRotationToDeviceOrientation() } } - - /// 현재 지원되는 화면 방향 - override var supportedInterfaceOrientations: UIInterfaceOrientationMask { - return isFullscreenMode ? .landscape : .portrait + + private func setupPlayerHieght(for size: CGSize) { + videoPlayerHeightConstraint?.isActive = false + + if traitCollection.userInterfaceIdiom == .pad { + let isPortrait = size.width < size.height + let heightMultiplier = isPortrait ? 0.4 : 0.3 + videoPlayerHeightConstraint = videoPlayerView.heightAnchor.constraint( + equalTo: view.widthAnchor, + multiplier: heightMultiplier + ) + } + videoPlayerHeightConstraint?.isActive = true } /// 프리젠테이션시 기본 방향