Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: 재생화면 버그 수정 #215

Merged
merged 10 commits into from
Dec 6, 2023
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?

override init(frame: CGRect) {
super.init(frame: frame)
Expand All @@ -21,6 +23,10 @@ final class PlaybackCell: UICollectionViewCell {
configure()
}

override func prepareForReuse() {
resetObserver()
}

func setPlaybackContents(info: PlaybackModels.PlaybackInfo) {
playbackView.descriptionView.titleLabel.text = info.title
playbackView.descriptionView.setText(info.content)
Expand All @@ -34,6 +40,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 @@ -84,7 +84,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 @@ -93,11 +94,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
6 changes: 3 additions & 3 deletions iOS/Layover/Layover/Scenes/Playback/PlaybackRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import UIKit

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

protocol PlaybackDataPassing {
Expand All @@ -25,7 +25,7 @@ final class PlaybackRouter: NSObject, PlaybackRoutingLogic, PlaybackDataPassing

// MARK: - Routing

func routeToNext() {

func routeToBack() {
viewController?.navigationController?.popViewController(animated: true)
}
}
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이것도 그냥 궁금한건데 무슨 의미인가여 이거 없으면 어케 되는지 궁금...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그 친구를 window에서 한단계 우선순위를 높여야 탭바에 안가려지더라구요. 탭 바위에 동그라미 보이게 하고 싶어서 우선순위 올린겁니다.

}

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()
}
}
72 changes: 22 additions & 50 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 @@ -42,7 +43,6 @@ final class PlaybackViewController: BaseViewController {
}()

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

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

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isMovingFromParent .. 뷰컨에 이런 프로퍼티가 있군요..?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

근데 얘 역할이 몬가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

viewWillDisappear되는 경우가 두가지가 있습니다. 하나는 backbutton눌러서 이동할 때고 다른 하나는 Tabbar로 이동하는 경우입니다.
backbutton눌러서 이동할 때는 pop을 하기 때문에 화면이 아예 사라지는 경우(isMovingFromParent)이기 때문에 player나 observer를 아예 초기화 시키구요.
Tabbar로 이동할 때는 아예 나가는게 아닌 탭만 이동하는 동작으로 처리합니다.
예를 들어 홈탭에서 영상을 보다 지도탭으로 들어간 후, 다시 홈탭으로 돌아오면 홈탭에서 보던 영상이 다시 재생됩니다. 그 구분을 위한 역할 입니다.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이해했습니다 !! 새로운 걸 알아가요..

interactor?.moveToBack()
}
}

override func viewDidLayoutSubviews() {
Expand All @@ -114,49 +117,16 @@ final class PlaybackViewController: BaseViewController {

override func setUI() {
super.setUI()
addWindowPlayerSlider()
}

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)
}

private func moveToBackViewController() {
interactor?.moveToBack()
}
}

extension PlaybackViewController: PlaybackDisplayLogic {
Expand All @@ -172,20 +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가 원점으로 돌아가는 시간 필요
// DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
// self.playerSlider.isHidden = false
// }
Task {
await slowShowPlayerSlider()
}
}
}

Expand All @@ -198,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 @@ -213,7 +179,6 @@ extension PlaybackViewController: PlaybackDisplayLogic {
}

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

Expand All @@ -228,7 +193,6 @@ extension PlaybackViewController: PlaybackDisplayLogic {
}
}
cell.addAVPlayer(url: playbackVideo.playbackInfo.videoURL)
self.setPlayerSlider(at: cell.playbackView)
return cell
}
}
Expand All @@ -238,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 @@ -269,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