mastodon-ios/Mastodon/Scene/Share/View/Container/PlayerContainerView.swift

180 lines
8.2 KiB
Swift
Raw Normal View History

2021-03-10 07:36:28 +01:00
//
2021-03-11 12:06:15 +01:00
// PlayerContainerView.swift
2021-03-10 07:36:28 +01:00
// Mastodon
//
// Created by xiaojian sun on 2021/3/10.
//
import os.log
2021-03-10 07:36:28 +01:00
import AVKit
import UIKit
import Combine
2021-03-10 07:36:28 +01:00
protocol PlayerContainerViewDelegate: AnyObject {
2021-03-11 12:06:15 +01:00
func playerContainerView(_ playerContainerView: PlayerContainerView, contentWarningOverlayViewDidPressed contentWarningOverlayView: ContentWarningOverlayView)
}
final class PlayerContainerView: UIView {
static let cornerRadius: CGFloat = ContentWarningOverlayView.cornerRadius
2021-03-10 07:36:28 +01:00
private let container = UIView()
private let touchBlockingView = TouchBlockingView()
private var containerHeightLayoutConstraint: NSLayoutConstraint!
2021-03-11 12:06:15 +01:00
let contentWarningOverlayView: ContentWarningOverlayView = {
let contentWarningOverlayView = ContentWarningOverlayView()
contentWarningOverlayView.update(cornerRadius: PlayerContainerView.cornerRadius)
2021-03-11 12:06:15 +01:00
return contentWarningOverlayView
2021-03-11 08:10:41 +01:00
}()
2021-03-10 07:36:28 +01:00
let playerViewController = AVPlayerViewController()
let blurhashOverlayImageView = UIImageView()
2021-06-17 12:43:06 +02:00
let mediaTypeIndicatorView = MediaTypeIndicatorView()
2021-03-11 12:06:15 +01:00
weak var delegate: PlayerContainerViewDelegate?
private var isReadyForDisplayObservation: NSKeyValueObservation?
let isReadyForDisplay = CurrentValueSubject<Bool, Never>(false)
2021-03-10 07:36:28 +01:00
override init(frame: CGRect) {
super.init(frame: frame)
_init()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
_init()
}
}
2021-03-11 12:06:15 +01:00
extension PlayerContainerView {
2021-03-10 07:36:28 +01:00
private func _init() {
2021-05-10 10:06:00 +02:00
// accessibility
accessibilityIgnoresInvertColors = true
2021-03-10 07:36:28 +01:00
container.translatesAutoresizingMaskIntoConstraints = false
addSubview(container)
containerHeightLayoutConstraint = container.heightAnchor.constraint(equalToConstant: 162).priority(.required - 1)
NSLayoutConstraint.activate([
container.topAnchor.constraint(equalTo: topAnchor),
container.leadingAnchor.constraint(equalTo: leadingAnchor),
trailingAnchor.constraint(equalTo: container.trailingAnchor),
bottomAnchor.constraint(equalTo: container.bottomAnchor),
containerHeightLayoutConstraint,
])
// will not influence full-screen playback
playerViewController.view.layer.masksToBounds = true
2021-03-11 12:06:15 +01:00
playerViewController.view.layer.cornerRadius = PlayerContainerView.cornerRadius
2021-03-10 07:36:28 +01:00
playerViewController.view.layer.cornerCurve = .continuous
blurhashOverlayImageView.translatesAutoresizingMaskIntoConstraints = false
playerViewController.contentOverlayView!.addSubview(blurhashOverlayImageView)
NSLayoutConstraint.activate([
blurhashOverlayImageView.topAnchor.constraint(equalTo: playerViewController.contentOverlayView!.topAnchor),
blurhashOverlayImageView.leadingAnchor.constraint(equalTo: playerViewController.contentOverlayView!.leadingAnchor),
blurhashOverlayImageView.trailingAnchor.constraint(equalTo: playerViewController.contentOverlayView!.trailingAnchor),
blurhashOverlayImageView.bottomAnchor.constraint(equalTo: playerViewController.contentOverlayView!.bottomAnchor),
])
// mediaType
2021-06-17 12:43:06 +02:00
mediaTypeIndicatorView.translatesAutoresizingMaskIntoConstraints = false
playerViewController.contentOverlayView!.addSubview(mediaTypeIndicatorView)
NSLayoutConstraint.activate([
2021-06-17 12:43:06 +02:00
mediaTypeIndicatorView.bottomAnchor.constraint(equalTo: playerViewController.contentOverlayView!.bottomAnchor),
mediaTypeIndicatorView.rightAnchor.constraint(equalTo: playerViewController.contentOverlayView!.rightAnchor),
mediaTypeIndicatorView.heightAnchor.constraint(equalToConstant: MediaTypeIndicatorView.indicatorViewSize.height).priority(.required - 1),
mediaTypeIndicatorView.widthAnchor.constraint(equalToConstant: MediaTypeIndicatorView.indicatorViewSize.width).priority(.required - 1),
])
isReadyForDisplayObservation = playerViewController.observe(\.isReadyForDisplay, options: [.initial, .new]) { [weak self] playerViewController, _ in
guard let self = self else { return }
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: isReadyForDisplay: %s", (#file as NSString).lastPathComponent, #line, #function, playerViewController.isReadyForDisplay.description)
self.isReadyForDisplay.value = playerViewController.isReadyForDisplay
}
contentWarningOverlayView.delegate = self
2021-03-11 12:06:15 +01:00
}
}
// MARK: - ContentWarningOverlayViewDelegate
extension PlayerContainerView: ContentWarningOverlayViewDelegate {
func contentWarningOverlayViewDidPressed(_ contentWarningOverlayView: ContentWarningOverlayView) {
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
2021-03-11 12:06:15 +01:00
delegate?.playerContainerView(self, contentWarningOverlayViewDidPressed: contentWarningOverlayView)
2021-03-10 07:36:28 +01:00
}
}
2021-03-11 12:06:15 +01:00
extension PlayerContainerView {
2021-03-10 07:36:28 +01:00
func reset() {
// note: set playerViewController.player pause() and nil in data source configuration process make reloadData not break playing
playerViewController.willMove(toParent: nil)
playerViewController.view.removeFromSuperview()
playerViewController.removeFromParent()
blurhashOverlayImageView.image = nil
2021-03-10 07:36:28 +01:00
container.subviews.forEach { subview in
subview.removeFromSuperview()
}
}
func setupPlayer(aspectRatio: CGSize, maxSize: CGSize, parent: UIViewController?) -> AVPlayerViewController {
reset()
touchBlockingView.translatesAutoresizingMaskIntoConstraints = false
container.addSubview(touchBlockingView)
NSLayoutConstraint.activate([
touchBlockingView.topAnchor.constraint(equalTo: container.topAnchor),
touchBlockingView.leadingAnchor.constraint(equalTo: container.leadingAnchor),
touchBlockingView.bottomAnchor.constraint(equalTo: container.bottomAnchor),
])
let rect = AVMakeRect(
aspectRatio: aspectRatio,
insideRect: CGRect(origin: .zero, size: maxSize)
).integral
2021-03-10 07:36:28 +01:00
parent?.addChild(playerViewController)
playerViewController.view.translatesAutoresizingMaskIntoConstraints = false
touchBlockingView.addSubview(playerViewController.view)
parent.flatMap { playerViewController.didMove(toParent: $0) }
NSLayoutConstraint.activate([
playerViewController.view.topAnchor.constraint(equalTo: touchBlockingView.topAnchor),
playerViewController.view.leadingAnchor.constraint(equalTo: touchBlockingView.leadingAnchor),
playerViewController.view.trailingAnchor.constraint(equalTo: touchBlockingView.trailingAnchor),
playerViewController.view.bottomAnchor.constraint(equalTo: touchBlockingView.bottomAnchor),
touchBlockingView.widthAnchor.constraint(equalToConstant: rect.width).priority(.required - 1),
2021-03-10 07:36:28 +01:00
])
containerHeightLayoutConstraint.constant = rect.height
2021-03-10 07:36:28 +01:00
containerHeightLayoutConstraint.isActive = true
playerViewController.view.frame.size = rect.size
contentWarningOverlayView.removeFromSuperview()
contentWarningOverlayView.translatesAutoresizingMaskIntoConstraints = false
addSubview(contentWarningOverlayView)
NSLayoutConstraint.activate([
contentWarningOverlayView.topAnchor.constraint(equalTo: touchBlockingView.topAnchor),
contentWarningOverlayView.leadingAnchor.constraint(equalTo: touchBlockingView.leadingAnchor),
contentWarningOverlayView.trailingAnchor.constraint(equalTo: touchBlockingView.trailingAnchor),
contentWarningOverlayView.bottomAnchor.constraint(equalTo: touchBlockingView.bottomAnchor)
])
2021-06-17 12:43:06 +02:00
bringSubviewToFront(mediaTypeIndicatorView)
2021-03-10 07:36:28 +01:00
return playerViewController
}
func setMediaKind(kind: VideoPlayerViewModel.Kind) {
2021-06-17 12:43:06 +02:00
mediaTypeIndicatorView.setMediaKind(kind: kind)
}
func setMediaIndicator(isHidden: Bool) {
2021-06-17 12:43:06 +02:00
mediaTypeIndicatorView.alpha = isHidden ? 0 : 1
}
2021-03-10 07:36:28 +01:00
}