- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我正在创建一个允许用户观看视频的功能。
使用下面的代码将视频播放器动画化到屏幕上,并将 AVPlayer 包含到视频 View 中并开始播放视频剪辑。
但是,当我按下 dismissButton 时,它不起作用。它只隐藏 dismissButton。
我感谢帮助我解决这个问题的任何帮助。
import UIKit
import AVFoundation
class VideoPlayerView: UIView {
let activityIndicatorView: UIActivityIndicatorView = {
let aiv = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
aiv.translatesAutoresizingMaskIntoConstraints = false
aiv.startAnimating()
return aiv
}()
lazy var pausePlayButton: UIButton = {
let button = UIButton(type: .system)
let image = UIImage(named: "pauseButton")
button.setImage(image, for: UIControlState())
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = .white
button.isHidden = true
button.addTarget(self, action: #selector(handlePause), for: .touchUpInside)
return button
}()
var isPlaying = false
func handlePause() {
if isPlaying {
player?.pause()
pausePlayButton.setImage(UIImage(named: "playVideoButton"), for: UIControlState())
} else {
player?.play()
pausePlayButton.setImage(UIImage(named: "pauseButton"), for: UIControlState())
}
isPlaying = !isPlaying
}
let controlsContainerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0, alpha: 1)
return view
}()
let dismissButton: UIButton = {
let button = UIButton(type: .system)
let image = UIImage(named: "closeVideoButton")
button.setImage(image, for: UIControlState())
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = .white
button.addTarget(self, action:#selector(pressButton(button:)), for: .touchUpInside)
return button
}()
func pressButton(button: UIButton) {
controlsContainerView.removeFromSuperview()
self.removeFromSuperview()
}
let currentTimeLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "0:00"
label.textColor = .white
label.font = UIFont.boldSystemFont(ofSize: 17)
return label
}()
lazy var videoSlider: UISlider = {
let slider = UISlider()
slider.translatesAutoresizingMaskIntoConstraints = false
slider.minimumTrackTintColor = .white
slider.maximumTrackTintColor = .lightGray
slider.setThumbImage(UIImage(named: "thumb"), for: UIControlState())
slider.addTarget(self, action: #selector(handleSliderChange), for: .valueChanged)
return slider
}()
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
})
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupPlayerView()
setupGradientLayer()
controlsContainerView.frame = frame
addSubview(controlsContainerView)
controlsContainerView.addSubview(activityIndicatorView)
activityIndicatorView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
activityIndicatorView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
controlsContainerView.addSubview(pausePlayButton)
pausePlayButton.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
pausePlayButton.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
pausePlayButton.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
pausePlayButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
controlsContainerView.addSubview(currentTimeLabel)
currentTimeLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -8).isActive = true
currentTimeLabel.topAnchor.constraint(equalTo: topAnchor, constant: -2).isActive = true
currentTimeLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true
currentTimeLabel.heightAnchor.constraint(equalToConstant: 44).isActive = true
controlsContainerView.addSubview(dismissButton)
dismissButton.leftAnchor.constraint(equalTo: leftAnchor, constant: 8).isActive = true
dismissButton.topAnchor.constraint(equalTo: topAnchor, constant: -2).isActive = true
dismissButton.widthAnchor.constraint(equalToConstant: 50).isActive = true
dismissButton.heightAnchor.constraint(equalToConstant: 44).isActive = true
controlsContainerView.addSubview(videoSlider)
videoSlider.rightAnchor.constraint(equalTo: currentTimeLabel.leftAnchor).isActive = true
videoSlider.topAnchor.constraint(equalTo: topAnchor).isActive = true
videoSlider.leftAnchor.constraint(equalTo: dismissButton.rightAnchor).isActive = true
videoSlider.heightAnchor.constraint(equalToConstant: 40).isActive = true
backgroundColor = .black
}
var player: AVPlayer?
fileprivate func setupPlayerView() {
let urlString = "https://firebasestorage.googleapis.com/v0/b/bunpou-d20ae.appspot.com/o/katoomouto.mp4?alt=media&token=861a0bad-2979-428c-94cf-f86dc9cb63ca"
if let url = URL(string: urlString) {
player = AVPlayer(url: url)
let playerLayer = AVPlayerLayer(player: player)
self.layer.addSublayer(playerLayer)
playerLayer.frame = self.frame
player?.play()
player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)
//track player progress
let interval = CMTime(value: 1, timescale: 2)
player?.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main, using: { (progressTime) in
let seconds = CMTimeGetSeconds(progressTime)
let secondsString = String(format: "%02d", Int(seconds.truncatingRemainder(dividingBy: 60)))
let minutesString = String(format: "%02d", Int(seconds / 60))
self.currentTimeLabel.text = "\(minutesString):\(secondsString)"
if let duration = self.player?.currentItem?.duration {
let durationSeconds = CMTimeGetSeconds(duration)
self.videoSlider.value = Float(seconds / durationSeconds)
}
})
}
}
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
}
}
fileprivate func setupGradientLayer() {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [UIColor.black.cgColor, UIColor.clear.cgColor]
gradientLayer.locations = [-1.4, 0.2]
controlsContainerView.layer.addSublayer(gradientLayer)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class VideoLauncher: NSObject {
func showVideoPlayer() {
print("Showing video player animation....")
if let keyWindow = UIApplication.shared.keyWindow {
let view = UIView(frame: keyWindow.frame)
view.backgroundColor = UIColor.white
view.frame = CGRect(x: keyWindow.frame.width - 10, y: keyWindow.frame.height - 10, width: 10, height: 10)
let videoPlayerFrame = CGRect(x: 0, y: 0, width: keyWindow.frame.width, height: keyWindow.frame.height)
let videoPlayerView = VideoPlayerView(frame: videoPlayerFrame)
view.addSubview(videoPlayerView)
keyWindow.addSubview(view)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
view.frame = keyWindow.frame
}, completion: { (completedAnimation) in
})
}
}
}
最佳答案
当点击关闭按钮时,您只会从父 View 中删除 controlsContainerView
。
func pressButton(button: UIButton) { controlsContainerView.removeFromSuperview() }
鉴于您的设置方式,我将执行以下操作:
showVideoPlayer
中使用 let view = ...
,只需使用 videoPlayerView
并像 keyWindow 一样添加它。 addSubView(视频播放器 View )
pressButton
中,您必须首先从 player
中删除您添加的观察者,例如 player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options : .new, 上下文: nil)
pressButton
中停止播放器,如 player?.stop()
然后从 superview 中删除 self
就像你对 self.removeFromSuperview()
注意:我不喜欢那个 View 本身应该负责将自己从 super View 中删除。所以我建议你有一个 Controller 来处理 VideoPlayerView
的显示/隐藏,只需向 VideoPlayerView
添加一个公共(public)方法,比如 stop
移除 player
的所有观察者,停止 player
,这样当 Controller 决定移除 VideoPlayerView
时,你会做一个很好的清理
关于ios - AVPlayer 无法关闭 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44406298/
我通过 spring ioc 编写了一些 Rest 应用程序。但我无法解决这个问题。这是我的异常(exception): org.springframework.beans.factory.BeanC
我对 TestNG、Spring 框架等完全陌生,我正在尝试使用注释 @Value通过 @Configuration 访问配置文件注释。 我在这里想要实现的目标是让控制台从配置文件中写出“hi”,通过
为此工作了几个小时。我完全被难住了。 这是 CS113 的实验室。 如果用户在程序(二进制计算器)结束时选择继续,我们需要使用 goto 语句来到达程序的顶部。 但是,我们还需要释放所有分配的内存。
我正在尝试使用 ffmpeg 库构建一个小的 C 程序。但是我什至无法使用 avformat_open_input() 打开音频文件设置检查错误代码的函数后,我得到以下输出: Error code:
使用 Spring Initializer 创建一个简单的 Spring boot。我只在可用选项下选择 DevTools。 创建项目后,无需对其进行任何更改,即可正常运行程序。 现在,当我尝试在项目
所以我只是在 Mac OS X 中通过 brew 安装了 qt。但是它无法链接它。当我尝试运行 brew link qt 或 brew link --overwrite qt 我得到以下信息: ton
我在提交和 pull 时遇到了问题:在提交的 IDE 中,我看到: warning not all local changes may be shown due to an error: unable
我跑 man gcc | grep "-L" 我明白了 Usage: grep [OPTION]... PATTERN [FILE]... Try `grep --help' for more inf
我有一段代码,旨在接收任何 URL 并将其从网络上撕下来。到目前为止,它运行良好,直到有人给了它这个 URL: http://www.aspensurgical.com/static/images/a
在过去的 5 个小时里,我一直在尝试在我的服务器上设置 WireGuard,但在完成所有设置后,我无法 ping IP 或解析域。 下面是服务器配置 [Interface] Address = 10.
我正在尝试在 GitLab 中 fork 我的一个私有(private)项目,但是当我按下 fork 按钮时,我会收到以下信息: No available namespaces to fork the
我这里遇到了一些问题。我是 node.js 和 Rest API 的新手,但我正在尝试自学。我制作了 REST API,使用 MongoDB 与我的数据库进行通信,我使用 Postman 来测试我的路
下面的代码在控制台中给出以下消息: Uncaught DOMException: Failed to execute 'appendChild' on 'Node': The new child el
我正在尝试调用一个新端点来显示数据,我意识到在上一组有效的数据中,它在数据周围用一对额外的“[]”括号进行控制台,我认为这就是问题是,而新端点不会以我使用数据的方式产生它! 这是 NgFor 失败的原
我正在尝试将我的 Symfony2 应用程序部署到我的 Azure Web 应用程序,但遇到了一些麻烦。 推送到远程时,我在终端中收到以下消息 remote: Updating branch 'mas
Minikube已启动并正在运行,没有任何错误,但是我无法 curl IP。我在这里遵循:https://docs.traefik.io/user-guide/kubernetes/,似乎没有提到关闭
每当我尝试docker组成任何项目时,都会出现以下错误。 我尝试过有和没有sudo 我在这台机器上只有这个问题。我可以在Mac和Amazon WorkSpace上运行相同的容器。 (myslabs)
我正在尝试 pip install stanza 并收到此消息: ERROR: No matching distribution found for torch>=1.3.0 (from stanza
DNS 解析看起来不错,但我无法 ping 我的服务。可能是什么原因? 来自集群中的另一个 Pod: $ ping backend PING backend.default.svc.cluster.l
我正在使用Hibernate 4 + Spring MVC 4当我开始 Apache Tomcat Server 8我收到此错误: Error creating bean with name 'wel
我是一名优秀的程序员,十分优秀!