Today’s Goal

  • Learn LBTA Youtube ep 18
    • UILabel
    • UISlider - duration
    • UISlider - addTarget

LBTA Youtube ep 18

How to Seek and Scrub AVPlayer with UISlider

컨트롤뷰의 핵심 요소인 영상 시간과 슬라이더를 만들자

####UILabel

오늘 코드들은 VideoLauncher.swift - VideoPlayerView class에서 모두 작성되었다.

controlsContainerView

video의 총 길이를 나타낼 UILabel을 선언

VideoLauncher.swift

let videoLengthLabel: UILabel = {
        let label = UILabel()
        label.text = "00:00"
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = UIFont.boldSystemFont(ofSize: 14)
        label.textAlignment = .right
        label.textColor = .white
        return label
    }()

closure를 사용, constraint를 줄것이기 때문에 translatesAutoresizingMaskIntoConstraints 잊지말자

font, textAlignment 사용법 알자

init() - for constraints

controlsContainerView.addSubview(videoLengthLabel)
        videoLengthLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -8).isActive = true
        videoLengthLabel.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        videoLengthLabel.widthAnchor.constraint(equalToConstant: 60).isActive = true
        videoLengthLabel.heightAnchor.constraint(equalToConstant: 24).isActive = true

UILabel constraints

observeValue()

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        
        // this is when the player is ready and rendering frames
        if keyPath == "currentItem.loadedTimeRanges" {
            activityIndicatorView.stopAnimating()
            controlsContainerView.backgroundColor = .clear
            pausePlayButton.isHidden = false
            isPlaying = true
            
            if let duration = player?.currentItem?.duration {
                let seconds = Int(CMTimeGetSeconds(duration))
                
                let secondsText = seconds % 60
                let minutesText = String(format: "%02d", seconds / 60)
                videoLengthLabel.text = "\(minutesText):\(secondsText)"
            }
        }
    }

이전 episode에서 다뤘듯이 observer에서 던지는 keyPath를 observeValue함수에서 체크하여 video를 렌더링 하기 때문에 여기에서 duration을 세팅해준다.

AVPlayer의 객체에서 currentItem?.duration 을 가져온다.

optional binding을 해주고 CMTimeGetSeconds를 이용하여 초 단위로 변환한다.

모듈러와 나누기 연산을 이용, 그리고 minuteText는 String() 함수를 이용하여 반영한다.

####UISlider

let videoSlider: UISlider = {
        let slider = UISlider()
        slider.translatesAutoresizingMaskIntoConstraints = false
        slider.minimumTrackTintColor = .red
    	slider.maximumTrackTintColor = .white
    	slider.setThumbImage(UIImage(named: ""), for: .normal)
        return slider
    }()

UISlider를 선언해주고 마찬가지로 constraints 줄것.

막대 색깔 정해주는 것은 TrackTintColor

또한 동그라미는 Thumb이라고 부르며 색깔은 따로 지정할 수 없으므로 ThumbImage를 변경해준다.

init() - for constraints

controlsContainerView.addSubview(videoSlider)
        videoSlider.rightAnchor.constraint(equalTo: videoLengthLabel.leftAnchor).isActive = true
        videoSlider.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
        videoSlider.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
        videoSlider.heightAnchor.constraint(equalToConstant: 30).isActive = true

UISlider constraints

let videoSlider: UISlider = {
        ...
        slider.addTarget(self, action: #selector(handleSliderChange), for: .valueChanged)
        ...
    }()

Slider에서 addTarget을 사용해야 Slider를 움직일 때마다 selector를 호출할 수 있다.

			let seekTime = CMTime(value: Int64(value), timescale: 1)
            
            player?.seek(to: seekTime, completionHandler: { (completedSeek) in
                // perhaps do something later here

AVPlayer의 객체에서 seek 함수의 to: 파라미터인 seekTime에 value를 10을 주면 10초로 이동하게 된다.

complete 부분은 엔터를 쳐서 스킵한다. (추후에 추가)

 @objc func handleSliderChange() {
        print(videoSlider.value)
        
        if let duration = player?.currentItem?.duration {
            let totalSeconds = CMTimeGetSeconds(duration)
            
            let value = Float64(videoSlider.value) * totalSeconds
            
            let seekTime = CMTime(value: Int64(value), timescale: 1)
            
            player?.seek(to: seekTime, completionHandler: { (completedSeek) in
                // perhaps do something later here
            })
        }
        
    }

이전 videoLengthLabel 데이터로 사용했던 duration을 가져와서 videoSlider.value를 곱해준다.

videoSlider.value 는 출력했을 때 슬라이더의 시작은 0, 끝은 1을 반환한다.

그래서 value를 두 값의 곱으로 잡아주고 let seekTime의 value 인자로 넣어준다.

여기서 중요한 점은 Definition을 확인하며 타입을 맞춰줘야한다.

totalSeconds는 float point이므로 videoSlider.value를 Float64로 형변환을 해준 후에 연산을 해줘야한다.

value는 Int64이므로 마지막에 형변환을 해준다.

태그:

카테고리:

업데이트:

댓글남기기