Skip to content

Commit

Permalink
Merge pull request #215 from boostcampwm2023/iOS/feat#189
Browse files Browse the repository at this point in the history
bug: 재생화면 버그 수정
  • Loading branch information
chopmozzi authored Dec 6, 2023
2 parents 1607a0b + 963074e commit 1eec512
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 72 deletions.
17 changes: 17 additions & 0 deletions iOS/Layover/Layover/Scenes/Playback/Cell/PlaybackCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
//

import UIKit
import AVFoundation

final class PlaybackCell: UICollectionViewCell {
let playbackView: PlaybackView = PlaybackView()
var timeObserverToken: Any?
var boardID: Int = 0

override init(frame: CGRect) {
Expand All @@ -22,6 +24,10 @@ final class PlaybackCell: UICollectionViewCell {
configure()
}

override func prepareForReuse() {
resetObserver()
}

func setPlaybackContents(info: PlaybackModels.PlaybackInfo) {
boardID = info.boardID
playbackView.descriptionView.titleLabel.text = info.title
Expand All @@ -36,6 +42,17 @@ final class PlaybackCell: UICollectionViewCell {
func addAVPlayer(url: URL) {
playbackView.resetPlayer()
playbackView.addAVPlayer(url: url)
playbackView.setPlayerSlider()
}

func addPlayerSlider(tabBarHeight: CGFloat) {
playbackView.addWindowPlayerSlider(tabBarHeight)

}

func resetObserver() {
playbackView.removeTimeObserver()
playbackView.removePlayerSlider()
}

private func configure() {
Expand Down
12 changes: 12 additions & 0 deletions iOS/Layover/Layover/Scenes/Playback/PlaybackInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ protocol PlaybackBusinessLogic {
func playInitialPlaybackCell(with request: PlaybackModels.DisplayPlaybackVideo.Request)
func playVideo(with request: PlaybackModels.DisplayPlaybackVideo.Request)
func playTeleportVideo(with request: PlaybackModels.DisplayPlaybackVideo.Request)
func moveToBack()
func configurePlaybackCell()
func controlPlaybackMovie(with request: PlaybackModels.SeekVideo.Request)
func hidePlayerSlider()
}

protocol PlaybackDataStore: AnyObject {
Expand Down Expand Up @@ -141,6 +143,11 @@ final class PlaybackInteractor: PlaybackBusinessLogic, PlaybackDataStore {
presenter?.presentLeavePlaybackView(with: response)
}

func moveToBack() {
let response: Models.DisplayPlaybackVideo.Response = Models.DisplayPlaybackVideo.Response(prevCell: nil, curCell: prevCell)
presenter?.presentResetPlaybackCell(with: response)
}

func configurePlaybackCell() {
guard let posts,
let parentView else { return }
Expand All @@ -160,4 +167,9 @@ final class PlaybackInteractor: PlaybackBusinessLogic, PlaybackDataStore {
let response: Models.SeekVideo.Response = Models.SeekVideo.Response(willMoveLocation: willMoveLocation, curCell: prevCell)
presenter?.presentSeekVideo(with: response)
}

func hidePlayerSlider() {
guard let prevCell else { return }
prevCell.playbackView.playerSlider?.isHidden = true
}
}
2 changes: 1 addition & 1 deletion iOS/Layover/Layover/Scenes/Playback/PlaybackModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ enum PlaybackModels {

struct ViewModel {
let indexPathRow: Int?
let prevCell: PlaybackCell?
var prevCell: PlaybackCell?
let curCell: PlaybackCell?

init(indexPathRow: Int? = nil, prevCell: PlaybackCell?, curCell: PlaybackCell?) {
Expand Down
10 changes: 10 additions & 0 deletions iOS/Layover/Layover/Scenes/Playback/PlaybackPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ protocol PlaybackPresentationLogic {
func presentShowPlayerSlider(with response: PlaybackModels.DisplayPlaybackVideo.Response)
func presentTeleportCell(with response: PlaybackModels.DisplayPlaybackVideo.Response)
func presentLeavePlaybackView(with response: PlaybackModels.DisplayPlaybackVideo.Response)
func presentResetPlaybackCell(with response: PlaybackModels.DisplayPlaybackVideo.Response)
func presentConfigureCell(with response: PlaybackModels.ConfigurePlaybackCell.Response)
func presentSeekVideo(with response: PlaybackModels.SeekVideo.Response)
}
Expand Down Expand Up @@ -94,11 +95,20 @@ final class PlaybackPresenter: PlaybackPresentationLogic {
viewController?.leavePlaybackView(viewModel: viewModel)
}

func presentResetPlaybackCell(with response: PlaybackModels.DisplayPlaybackVideo.Response) {
let viewModel: Models.DisplayPlaybackVideo.ViewModel = Models.DisplayPlaybackVideo.ViewModel(prevCell: nil, curCell: response.curCell)
viewController?.routeToBack(viewModel: viewModel)
}

// MARK: - UseCase Configure Playback Cell

func presentConfigureCell(with response: PlaybackModels.ConfigurePlaybackCell.Response) {
let viewModel: Models.ConfigurePlaybackCell.ViewModel = Models.ConfigurePlaybackCell.ViewModel(teleportIndex: response.teleportIndex)
viewController?.configureDataSource(viewModel: viewModel)
}

// MARK: - UseCase Seek Video

func presentSeekVideo(with response: PlaybackModels.SeekVideo.Response) {
let viewModel: Models.SeekVideo.ViewModel = Models.SeekVideo.ViewModel(willMoveLocation: response.willMoveLocation, curCell: response.curCell)
viewController?.seekVideo(viewModel: viewModel)
Expand Down
5 changes: 5 additions & 0 deletions iOS/Layover/Layover/Scenes/Playback/PlaybackRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import UIKit

protocol PlaybackRoutingLogic {
func routeToBack()
func routeToReport()
}

Expand All @@ -25,6 +26,10 @@ final class PlaybackRouter: NSObject, PlaybackRoutingLogic, PlaybackDataPassing

// MARK: - Routing

func routeToBack() {
viewController?.navigationController?.popViewController(animated: true)
}

func routeToReport() {
let reportViewController: ReportViewController = ReportViewController()
guard let source = dataStore,
Expand Down
55 changes: 55 additions & 0 deletions iOS/Layover/Layover/Scenes/Playback/PlaybackView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import UIKit
import AVFoundation

final class PlaybackView: UIView {

// MARK: - Properties

private var timeObserverToken: Any?

// MARK: - UI Components
// TODO: private 다시 붙이고 Method 처리
let descriptionView: LODescriptionView = {
Expand Down Expand Up @@ -79,6 +84,8 @@ final class PlaybackView: UIView {

let playerView: PlayerView = PlayerView()

var playerSlider: LOSlider?

// MARK: - View Life Cycle

override init(frame: CGRect) {
Expand Down Expand Up @@ -132,12 +139,52 @@ final class PlaybackView: UIView {
func getDuration() -> Float64 {
CMTimeGetSeconds(playerView.player?.currentItem?.duration ?? CMTime(value: 0, timescale: 1))
}

func removePlayerSlider() {
guard let playerSlider = playerSlider else { return }
playerSlider.removeTarget(self, action: #selector(didChangedSliderValue(_:)), for: .valueChanged)
playerSlider.removeFromSuperview()
}

func addWindowPlayerSlider(_ tabBarHeight: CGFloat) {
playerSlider = LOSlider()
guard let playerSlider else { return }
let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
let window = windowScene?.windows.first
guard let playerSliderWidth: CGFloat = windowScene?.screen.bounds.width else { return }
guard let windowHeight: CGFloat = windowScene?.screen.bounds.height else { return }
playerSlider.frame = CGRect(x: 0, y: (windowHeight - tabBarHeight - LOSlider.loSliderHeight / 2), width: playerSliderWidth, height: LOSlider.loSliderHeight)
window?.addSubview(playerSlider)
playerSlider.window?.windowLevel = UIWindow.Level.normal + 1
}

func setPlayerSlider() {
let interval: CMTime = CMTimeMakeWithSeconds(1, preferredTimescale: Int32(NSEC_PER_SEC))
timeObserverToken = playerView.player?.addPeriodicTimeObserver(forInterval: interval, queue: .main, using: { [weak self] currentTime in
self?.updateSlider(currentTime: currentTime)
})
}

func addTargetPlayerSlider() {
playerSlider?.addTarget(self, action: #selector(didChangedSliderValue(_:)), for: .valueChanged)
}

func removeTimeObserver() {
if let timeObserverToken = timeObserverToken {
playerView.player?.removeTimeObserver(timeObserverToken)
}
}
}

// MARK: PlaybackView 내부에서만 쓰이는 Method

private extension PlaybackView {
func updateSlider(currentTime: CMTime) {
guard let currentItem: AVPlayerItem = playerView.player?.currentItem else { return }
let duration: CMTime = currentItem.duration
if CMTIME_IS_INVALID(duration) { return }
playerSlider?.value = Float(CMTimeGetSeconds(currentTime) / CMTimeGetSeconds(duration))
}

// MARK: - Gesture Method
Expand Down Expand Up @@ -268,4 +315,12 @@ private extension PlaybackView {
playerView.seek(to: CMTime.zero)
playerView.play()
}

@objc private func didChangedSliderValue(_ sender: LOSlider) {
guard let duration: CMTime = playerView.player?.currentItem?.duration else { return }
let value: Float64 = Float64(sender.value) * CMTimeGetSeconds(duration)
let seekTime: CMTime = CMTime(value: CMTimeValue(value), timescale: 1)
playerView.seek(to: seekTime)
playerView.play()
}
}
91 changes: 20 additions & 71 deletions iOS/Layover/Layover/Scenes/Playback/PlaybackViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ protocol PlaybackDisplayLogic: AnyObject {
func showPlayerSlider(viewModel: PlaybackModels.DisplayPlaybackVideo.ViewModel)
func teleportPlaybackCell(viewModel: PlaybackModels.DisplayPlaybackVideo.ViewModel)
func leavePlaybackView(viewModel: PlaybackModels.DisplayPlaybackVideo.ViewModel)
func routeToBack(viewModel: PlaybackModels.DisplayPlaybackVideo.ViewModel)
func configureDataSource(viewModel: PlaybackModels.ConfigurePlaybackCell.ViewModel)
func seekVideo(viewModel: PlaybackModels.SeekVideo.ViewModel)
}
Expand All @@ -41,16 +42,7 @@ final class PlaybackViewController: BaseViewController {
return collectionView
}()

private let reportButton: UIBarButtonItem = {
let button: UIButton = UIButton()
button.setImage(UIImage(systemName: "ellipsis"), for: .normal)
let barButtonItem: UIBarButtonItem = UIBarButtonItem(customView: button)
barButtonItem.customView?.transform = CGAffineTransform(rotationAngle: .pi / 2)
return barButtonItem
}()

// MARK: - Properties
private var playerSlider: LOSlider = LOSlider()

private var dataSource: UICollectionViewDiffableDataSource<Section, Models.PlaybackVideo>?

Expand Down Expand Up @@ -99,6 +91,9 @@ final class PlaybackViewController: BaseViewController {
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
interactor?.leavePlaybackView()
if isMovingFromParent {
interactor?.moveToBack()
}
}

override func viewDidLayoutSubviews() {
Expand All @@ -122,66 +117,15 @@ final class PlaybackViewController: BaseViewController {

override func setUI() {
super.setUI()
addWindowPlayerSlider()
guard let button = reportButton.customView as? UIButton else { return }
button.addTarget(self, action: #selector(reportButtonDidTap), for: .touchUpInside)
self.navigationItem.rightBarButtonItem = reportButton
self.navigationController?.navigationBar.tintColor = .layoverWhite
}

private func addWindowPlayerSlider() {
let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
let window = windowScene?.windows.first
guard let playerSliderWidth: CGFloat = windowScene?.screen.bounds.width else { return }
guard let windowHeight: CGFloat = windowScene?.screen.bounds.height else { return }
guard let tabBarHeight: CGFloat = self.tabBarController?.tabBar.frame.height else { return }
playerSlider.frame = CGRect(x: 0, y: (windowHeight - tabBarHeight - LOSlider.loSliderHeight / 2), width: playerSliderWidth, height: LOSlider.loSliderHeight)
window?.addSubview(playerSlider)
playerSlider.window?.windowLevel = UIWindow.Level.normal + 1
}

private func setPlayerSlider(at playbackView: PlaybackView) {
let interval: CMTime = CMTimeMakeWithSeconds(1, preferredTimescale: Int32(NSEC_PER_SEC))
playbackView.playerView.player?.addPeriodicTimeObserver(forInterval: interval, queue: .main, using: { [weak self] currentTime in
self?.updateSlider(currentTime: currentTime, playerView: playbackView.playerView)
})
playerSlider.addTarget(self, action: #selector(didChangedSliderValue(_:)), for: .valueChanged)
}

private func updateSlider(currentTime: CMTime, playerView: PlayerView) {
guard let currentItem: AVPlayerItem = playerView.player?.currentItem else { return }
let duration: CMTime = currentItem.duration
if CMTIME_IS_INVALID(duration) { return }
playerSlider.value = Float(CMTimeGetSeconds(currentTime) / CMTimeGetSeconds(duration))
}

private func slowShowPlayerSlider() async {
do {
try await Task.sleep(nanoseconds: 1_000_000_00)
playerSlider.isHidden = false
} catch {
os_log("Fail Waiting show Player Slider")
}
}

@objc private func didChangedSliderValue(_ sender: LOSlider) {
let request: Models.SeekVideo.Request = Models.SeekVideo.Request(currentLocation: Float64(sender.value))
interactor?.controlPlaybackMovie(with: request)
}

@objc private func reportButtonDidTap() {
let alert: UIAlertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let reportAction: UIAlertAction = UIAlertAction(title: "신고", style: .destructive, handler: {
[weak self] _ in
self?.router?.routeToReport()
})
let cancelAction: UIAlertAction = UIAlertAction(title: "취소", style: .cancel)
alert.addAction(reportAction)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: {
self.interactor?.leavePlaybackView()
})
private func moveToBackViewController() {
interactor?.moveToBack()
}
}

Expand All @@ -198,17 +142,16 @@ extension PlaybackViewController: PlaybackDisplayLogic {
}

func stopPrevPlayerAndPlayCurPlayer(viewModel: PlaybackModels.DisplayPlaybackVideo.ViewModel) {
guard let tabBarHeight: CGFloat = self.tabBarController?.tabBar.frame.height else { return }
if let prevCell = viewModel.prevCell {
prevCell.playbackView.removePlayerSlider()
prevCell.playbackView.stopPlayer()
prevCell.playbackView.replayPlayer()
}
if let curCell = viewModel.curCell {
curCell.addPlayerSlider(tabBarHeight: tabBarHeight)
curCell.playbackView.addTargetPlayerSlider()
curCell.playbackView.playPlayer()
setPlayerSlider(at: curCell.playbackView)
// Slider가 원점으로 돌아가는 시간 필요
Task {
await slowShowPlayerSlider()
}
}
}

Expand All @@ -221,7 +164,7 @@ extension PlaybackViewController: PlaybackDisplayLogic {
}

func showPlayerSlider(viewModel: PlaybackModels.DisplayPlaybackVideo.ViewModel) {
playerSlider.isHidden = false
viewModel.curCell?.playbackView.playerSlider?.isHidden = false
}

func moveInitialPlaybackCell(viewModel: PlaybackModels.SetInitialPlaybackCell.ViewModel) {
Expand All @@ -236,7 +179,6 @@ extension PlaybackViewController: PlaybackDisplayLogic {
}

func leavePlaybackView(viewModel: PlaybackModels.DisplayPlaybackVideo.ViewModel) {
playerSlider.isHidden = true
viewModel.prevCell?.playbackView.stopPlayer()
}

Expand All @@ -251,7 +193,6 @@ extension PlaybackViewController: PlaybackDisplayLogic {
}
}
cell.addAVPlayer(url: playbackVideo.playbackInfo.videoURL)
self.setPlayerSlider(at: cell.playbackView)
return cell
}
}
Expand All @@ -261,6 +202,14 @@ extension PlaybackViewController: PlaybackDisplayLogic {
viewModel.curCell.playbackView.seekPlayer(seekTime: seekTime)
viewModel.curCell.playbackView.playPlayer()
}

func routeToBack(viewModel: PlaybackModels.DisplayPlaybackVideo.ViewModel) {
var curCell = viewModel.curCell
curCell?.resetObserver()
curCell?.playbackView.resetPlayer()
curCell = nil
}

}

extension PlaybackViewController: UICollectionViewDelegateFlowLayout {
Expand Down Expand Up @@ -292,7 +241,7 @@ extension PlaybackViewController: UICollectionViewDelegate {
}

func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
playerSlider.isHidden = true
interactor?.hidePlayerSlider()
}

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
Expand Down

0 comments on commit 1eec512

Please sign in to comment.