일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- War Game
- 재귀
- c언어
- windosws wbcs
- 큐
- 파일 시스템
- 암호수학
- HTML
- 시간복잡도
- 자료구조
- OSI
- 백준
- LoB
- ftz
- ftz level13
- 파이썬
- 정렬 알고리즘
- 스택
- C
- windosw 문자열
- System
- SWiFT
- pwnable.kr
- web
- 미로 탐색 알고리즘
- Stack
- Java
- PHP
- level13
- 두근두근 자료구조
- Today
- Total
나의 기록, 현진록
[Swift/iOS] AVPlayer에 Observer 추가하여 재생시간 탐지하기, UISlider을 이용하여 AVPlayer 영상 시점 이동하기, Control View 숨기기/보이기 본문
[Swift/iOS] AVPlayer에 Observer 추가하여 재생시간 탐지하기, UISlider을 이용하여 AVPlayer 영상 시점 이동하기, Control View 숨기기/보이기
guswlsdk 2022. 7. 19. 17:48
작성자가 복습겸 코딩한 내용을 기록하려고 글을 작성하는 것이지만, 혹시라도 이 글을 참고하는 사람이 있다면 넷플릭스를 클론코딩하였기 때문에 기본 동작은 유사하다는 전제하에 글을 읽어나가면 될 것이다.
용량 이슈로 배속 적용, 타이머 적용하여 ControlView 숨기기 기능 때문에 잘 안 보이지만!
1초마다 영상 재생 시간을 탐지하여 남은 시간을 나타내는 기능을 구현해보자.
영상이 재생되는 뷰가 실행됨과 동시에 영상이 재생될 수 있도록 할 것이다.
viewDidLoad()에는 play() 메소드가 포함되어 있고 그 안에는 addPeriodicTimeObserver()가 있다. 이 함수가 영상의 시간(길이)를 탐지할 수 있도록 동작할 것이다.
func play(){
player.play()
self.timerNum = 0
addPeriodicTimeObserver()
playButton.isSelected = true
}
func pause(){
player.pause()
self.timerNum = 0
removeTimeObserver()
playButton.isSelected = false
}
func reset(){
player.pause()
removeTimeObserver()
player.replaceCurrentItem(with: nil)
}
1초마다 영상 길이를 탐지하는 Observer가 동작하도록 코드를 작성하고 전역변수인 timeObserverToken에 넣어준다.
func addPeriodicTimeObserver(){
let timeScale = CMTime(seconds: 1, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
timeObserverToken = player.addPeriodicTimeObserver(forInterval: timeScale, queue: .main) { [weak self] currentTime in
let totalPlayTime = Float(CMTimeGetSeconds(self?.player.currentItem?.duration ?? CMTimeMake(value: 1, timescale: 1)))
self?.timeSlider.maximumValue = totalPlayTime
self?.updateTime(time: currentTime)
let currentPlayTime = Float(CMTimeGetSeconds(currentTime))
if currentPlayTime == totalPlayTime{
self?.reset()
self?.dismiss(animated: false)
}
}
}
func removeTimeObserver(){
if let token = timeObserverToken{
player.removeTimeObserver(token)
timeObserverToken = nil
}
}
영상이 일시정지 되거나 영상 재생 화면을 닫을 때는 Observer가 필요 없기 때문에 removeTimeObserver() 메소드가 동작하도록 한다.
가장 먼저 UISlider를
timeSlider는 UISlider 속성을 가진 프로퍼티이다. 값이 변경될 때마다 그 이벤트를 탐지할 수 있도록 연결해준다.
func initTimeSlider(){
timeSlider.tintColor = .red
timeSlider.isContinuous = true
timeSlider.minimumValue = 0
timeSlider.value = .zero
updateTime(time: .zero)
timeSlider.addTarget(self, action: #selector(playbackSliderChagnedValue), for: .valueChanged)
}
updateTime은 UISlider의 값(바)를 변경, 이동시키며 UISlider 우측에 남은 시간을 나타내는 UILabel의 내용을 수정하는 기능을 한다. 아래에 더보기에 코드가 있다.
UISlider가 움직일 때 크게 3가지로 구분하여 동작을 정리해야한다.
이 때 값은 UISlider 내에 있는 Value를 말한다. 예를 들어 슬라이더 바를 움직였을 때 Value가 바뀐다.
이 동작은 참고로 슬라이더를 움직인 순간부터 손을 뗀 순간까지의 과정을 말한다.
1. 값이 변경되기 시작할 때 (최초로 슬라이더를 터치했을 때)
2. 값이 변경 중일 때
3. 값의 변경 작업이 종료될 때 (슬라이더에서 손을 떼어냈을 때)
1. 값이 변경되기 시작할 때
이 시점에서는 재생 중인 영상이 일시정지 된 상태여야 한다.
아래의 코드를 보면 timer?.invalidate()는 화면을 재생, 일시정지, 화면 닫기, 슬라이더 등의 Control View를 숨기기/보이기 하는 데에 사용되는 코드이다.
2. 값이 변경 중일 때
슬라이더 우측에 있는 남은 시간을 나타내는 Label을 변경한다. convertTimeToString() 메소드는 아래 더보기에 있다.
3. 값의 변경이 끝났을 때
현재의 값에 해당하는 영상 시점으로 영상이 재생되어야 한다.
startTime()은 슬라이더에서 손을 떼어냈을 시점 이후이기 때문에 타이머를 다시 시작하여 일정 시간 이후 Control View를 숨기는 동작을 한다.
@objc func playbackSliderChagnedValue(timeSlider: UISlider, event: UIEvent){
if let touchEvent = event.allTouches?.first {
switch touchEvent.phase {
case .began:
// handle drag began
pause()
timer?.invalidate()
case .moved:
// handle drag moved
//totaltime - slider.value
let seconds : Int64 = Int64(timeSlider.value * Float(CMTimeScale(NSEC_PER_SEC)))
let targetTime:CMTime = CMTimeMake(value: seconds, timescale: CMTimeScale(NSEC_PER_SEC))
self.remainingTimeLabel.text = convertTimeToString(time: targetTime)
case .ended:
// handle drag ended
let seconds = Int64(timeSlider.value * Float(CMTimeScale(NSEC_PER_SEC)))
let seekTime = CMTimeMake(value: seconds, timescale: CMTimeScale(NSEC_PER_SEC))
self.player.seek(to: seekTime)
if player.rate == 0{
play()
}
startTimer()
default:
break
}
}
}
func updateTime(time: CMTime){
self.remainingTimeLabel.text = convertTimeToString(time: time)
timeSlider.value = Float(CMTimeGetSeconds(time))
}
func convertTimeToString(time: CMTime) -> String{
let totalTime = Float(CMTimeGetSeconds(self.player.currentItem?.duration ?? CMTimeMake(value: 1, timescale: 1)))
let currentTime = Float(CMTimeGetSeconds(time))
print(currentTime, totalTime)
guard !totalTime.isInfinite, !totalTime.isNaN, !currentTime.isInfinite, !currentTime.isNaN else{
print("time is infinite or NaN")
return String(format: "%02d:%02d", 0, 0)
}
//totalTime - currentTime
let remainingTime = Int(totalTime-currentTime)
let min = remainingTime / 60
let seconds = remainingTime % 60
return String(format: "%02d:%02d", min, seconds)
}
다음은 Control View 숨기기 / 보이기 기능이다.
화면에 일정 시간 동안 입력이 없을 경우 자동으로 뷰가 사라지고, 터치를 하면 뷰가 보여지도록 동작한다.
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if controlView.alpha == 0{
showControlView()
}else if controlView.alpha == 1{
hideControlView()
}
}
화면이 보여지고 난 이후에는 일정 시간이 지나면 다시 숨기는 동작이 가능하도록 하기 위해 타이머를 구현해야한다.
func hideControlView(){
UIView.animate(withDuration: 0.4){
self.controlView.alpha = 0
}
}
func showControlView(){
UIView.animate(withDuration: 0.4){
self.controlView.alpha = 1
}
startTimer()
}
다음은 타이머와 관련된 코드이다. 영상이 재생되는 뷰가 실행됨과 동시에 시간을 계산할 수 있도록 viewDidLoad()에 추가하도록 하자.
Timer.scheduledTimer에 있는 파라미터 timeInterval에 1을 주어 1초마다 연결한 메소드가 실행될 수 있도록 한다.
func startTimer(){
timerNum = 0
if timer != nil{
timer?.invalidate()
}
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(autoHideControlView), userInfo: nil, repeats: true)
}
다음은 1초마다 실행될 메소드이다.
timerNum이 1초마다 1씩 증가할테고, 3이 될 경우 hideControlView()가 실행된다. 슬라이더, 재생, 일시정지 버튼, 화면 닫기 버튼 등 UI를 숨기는 동작을 한다.
@objc func autoHideControlView(){
if timerNum == 3{
hideControlView()
timer?.invalidate()
timer = nil
}
timerNum += 1
}
이외에도 이전에서 설명한 슬라이더를 움직이는 과정에서는 타이머가 종료되어야 한다. 화면이 다시 숨겨지는 3초 이상으로 슬라이더를 동작하고 있으면 동작하고 있는 도중 ControlView가 사라지기 때문이다.
코드를 일부만 가져왔기 때문에 전체적인 코드를 확인하려면 글 상단에 게시된 작성자의 깃허브에서 동작시키며 확인하면 될 것 같다.
'iOS' 카테고리의 다른 글
iOS에서의 유닛 테스트(Unit Test)에 대해 알아보자 (0) | 2023.03.15 |
---|---|
[Swift/iOS] UITextView 세로 가운데 정렬 vertical center (0) | 2022.07.26 |
[iOS/Swift] iOS에서 URL로 이미지에 넣어보기 (0) | 2022.07.02 |
[iOS/Swift] Apple itunes search API 활용하기 (0) | 2022.07.02 |
[Swift/iOS] searchBar cancel button custom (change title) (0) | 2022.06.28 |