이번에는 기획자가 간단한 기능을 하나 추가해 달라고 합니다. 기존에는 이미지만 볼 수 있다면, 이번에는 영상도 볼 수 있게 해달라고 하네요. 일단은 영상을 재생시킬수 있는 뷰를 하나 만들어주겠습니다.
final class VideoView: UIView {
private var url: String?
private var player = AVPlayer()
private var playerLayer: AVPlayerLayer?
private lazy var videoBackgroundView: UIView = {
let view = UIView()
view.backgroundColor = .systemGray
view.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(view)
return view
}()
init(){
super.init(frame: .zero)
NSLayoutConstraint.activate([
self.videoBackgroundView.leftAnchor.constraint(equalTo: self.leftAnchor),
self.videoBackgroundView.rightAnchor.constraint(equalTo: self.rightAnchor),
self.videoBackgroundView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
self.videoBackgroundView.topAnchor.constraint(equalTo: self.topAnchor),
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
self.playerLayer?.frame = self.videoBackgroundView.bounds
}
}
extension VideoView {
func load(url: String) {
self.url = url
guard let url = URL(string: self.url ?? "") else { return }
let item = AVPlayerItem(url: url)
self.player.replaceCurrentItem(with: item)
let playerLayer = AVPlayerLayer(player: self.player)
playerLayer.frame = self.videoBackgroundView.bounds
playerLayer.videoGravity = .resizeAspectFill
self.playerLayer = playerLayer
self.videoBackgroundView.layer.addSublayer(playerLayer)
self.player.play()
}
}
그리고 데이터 모델에 비디오/이미지 타입을 넣어줍시다.
struct Content {
enum MediaType {
case image
case video
}
var type: Content.MediaType
var url: String
var description: String
}
이제 각 뷰의 셀이 모델 타입에 따라 비디오/이미지 뷰를 넣어줘야 합니다. 저희는 코드의 중복을 피하기 위해 MediaContainer이라는 프로토콜로 셀에 들어갈 UI들은 만들어둔 상태입니다. 여기서 수정을 하면 해당 프로토콜을 채택하고 객체들에게도 적용이 되죠. 이미지뷰와 비디오 뷰는 둘다 UIView를 상속받았기에 그 둘을 둘 다 사용할 수 있게 media를 UIView타입으로 두고, extension으로 content의 타입에 따라 이미지/비디오 뷰가 리턴되도록 하였습니다.
protocol MediaContainer {
var content: Content? { get set }
var media: UIView { get }
var note: UILabel { get }
var videoView: VideoView { get }
var mediaImageView: UIImageView { get }
func contentUpdate()
}
extension MediaContainer {
var media: UIView {
guard let content = content else { return UIImageView() }
switch content.type {
case .video:
return videoView
case .image:
return mediaImageView
}
}
func contentUpdate() {
if let content = content {
switch content.type {
case .video:
videoView.load(url: content.url)
case .image:
mediaImageView.load(url: content.url)
}
self.note.text = content.description
}
}
}
이제 MediaContainer를 채택하고 있는 뷰들은 모두다 동일하게 적용이 된 것을 볼 수 있습니다.
전체 소스코드: https://github.com/Kim-Junhwan/POP_Gallry
'iOS > swift' 카테고리의 다른 글
참조 타입과 Call by Value, Call by Reference (0) | 2023.04.27 |
---|---|
CAShapeLayer 특정 뷰의 중심에 맞추기 (0) | 2023.03.11 |
프로토콜 지향 프로그래밍을 이용한 프로젝트를 진행해보자 (1) (0) | 2022.08.13 |
POP에서는 모든 코드가 프로토콜로 시작을 해야 할까? (0) | 2022.08.10 |
AnyObject와 Protocol (0) | 2022.07.24 |