在 AVPlayer 上添加 ActivityIndicator 并删除 addObserver
Add ActivityIndicator and remove addObserver on AVPlayer
我正在尝试显示 ActivityIndicator
播放器(缓冲或加载)和开始播放时停止播放 ActivityIndicator
。此外,当我停止播放器时,它是 AVPlayer 的(removeObserver 或 deallocObservers)。
当我播放音乐时,它会显示 ActivityIndicator
直到它准备好播放,但它会在播放前 4.5 秒停止动画 ActivityIndicator`。问题是当我停止 AVPlayer 或点击另一个播放按钮时,它在删除 AVPlayer 观察器时给我错误。
谁能告诉我我的代码哪里有错误,我该如何解决,谢谢。
var selectIndex:Int = -1
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! RadioCollectionViewCell
cell.backgroundColor = UIColor.yellowColor()
let object = objects[indexPath.row]
cell.img.image = UIImage(named: object["image"]!)
cell.btnPlay.addTarget(self, action: Selector("audioControlButtonAction:"), forControlEvents: UIControlEvents.TouchUpInside)
cell.btnPlay.tag = indexPath.row+1
return cell
}
func audioControlButtonAction(sender: UIButton){
if player != nil && player?.currentItem != nil {
deallocObservers(player!)
}
var btn:NSInteger
btn = sender.tag as NSInteger
let object = objects[btn-1]
let nurl = NSURL(string: "\(object["url"]!)")!
playerItem = AVPlayerItem(URL: nurl)
player=AVPlayer(playerItem: playerItem!)
print(selectIndex)
if selectIndex != -1 && selectIndex != sender.tag
{
let bt:UIButton = self.view.viewWithTag(selectIndex) as! UIButton
if bt.selected == true
{
bt.selected = false
deallocObservers(player!)
}
}
if sender.selected == false
{
player!.addObserver(self, forKeyPath: "status", options:NSKeyValueObservingOptions(), context: nil)
player!.addObserver(self, forKeyPath: "playbackBufferEmpty", options:NSKeyValueObservingOptions(), context: nil)
// player!.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options:NSKeyValueObservingOptions(), context: nil)
player!.addObserver(self, forKeyPath: "loadedTimeRanges", options: NSKeyValueObservingOptions(), context: nil)
player!.play()
sender.selected = true
selectIndex = sender.tag
activityView.startAnimating()
// self.view.userInteractionEnabled = false
}
else
{
deallocObservers(player!)
player?.pause()
sender.selected = false
selectIndex = -1
}
print(selectIndex)
}
func deallocObservers(player: AVPlayer) {
player.removeObserver(self, forKeyPath: "status")
player.removeObserver(self, forKeyPath: "playbackBufferEmpty")
// player.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
player.removeObserver(self, forKeyPath: "loadedTimeRanges")
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>){
if object?.isEqual(player) == true && keyPath == "status" {
print("status")
if player?.status == AVPlayerStatus.ReadyToPlay{
print("AVPlayerStatus.ReadyToPlay")
activityView.stopAnimating()
// self.view.userInteractionEnabled = true
}else{
print("AVPlayerStatus.NotReadyToPlay")
activityView.startAnimating()
// self.view.userInteractionEnabled = false
}
//if keyPath == "playbackLikelyToKeepUp" {
//activityView.stopAnimating()
////self.view.userInteractionEnabled = true
//print("playbackLikelyToKeepUp")
//}
if keyPath == "playbackBufferEmpty" {
activityView.startAnimating()
self.view.userInteractionEnabled = false
let createAccountErrorAlert: UIAlertView = UIAlertView()
createAccountErrorAlert.delegate = self
createAccountErrorAlert.title = "No Internet Connection"
createAccountErrorAlert.message = "Make sure your device is connected to the internet."
createAccountErrorAlert.addButtonWithTitle("Dismiss")
createAccountErrorAlert.show()
print("playbackBufferEmpty")
}
if player?.status == AVPlayerStatus.Failed{
print("Something went wrong . player.error should contain some information")
}
}
}
Output
-1
1
status
AVPlayerStatus.ReadyToPlay
1
2016-07-24 21:51:00.585 Radio[777:23560] *** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <Radio.RadioCollectionViewController 0x7fbe39d47f70> for the key path "status" from <AVPlayer 0x7fbe39e67410> because it is not registered as an observer.'
*** First throw call stack:
(
0 CoreFoundation 0x00000001019b2e65 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x0000000103b51deb objc_exception_throw + 48
2 CoreFoundation 0x00000001019b2d9d +[NSException raise:format:] + 205
3 Foundation 0x0000000101fc4d51 -[NSObject(NSKeyValueObserverRegistration) _removeObserver:forProperty:] + 504
4 Foundation 0x0000000101fc4abd -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:] + 84
5 Radio 0x00000001014a935d _TFC5Radio29RadioCollectionViewController16deallocObserversfS0_FCSo8AVPlayerT_ + 141
6 Radio 0x00000001014a8fbe _TFC5Radio29RadioCollectionViewController24audioControlButtonActionfS0_FCSo8UIButtonT_ + 5326
7 Radio 0x00000001014a92ba _TToFC5Radio29RadioCollectionViewController24audioControlButtonActionfS0_FCSo8UIButtonT_ + 58
8 UIKit 0x0000000102630194 -[UIApplication sendAction:to:from:forEvent:] + 92
9 UIKit 0x000000010279f6fc -[UIControl sendAction:to:forEvent:] + 67
10 UIKit 0x000000010279f9c8 -[UIControl _sendActionsForEvents:withEvent:] + 311
11 UIKit 0x000000010279eaf8 -[UIControl touchesEnded:withEvent:] + 601
12 UIKit 0x000000010269f49b -[UIWindow _sendTouchesForEvent:] + 835
13 UIKit 0x00000001026a01d0 -[UIWindow sendEvent:] + 865
14 UIKit 0x000000010264eb66 -[UIApplication sendEvent:] + 263
15 UIKit 0x0000000102628d97 _UIApplicationHandleEventQueue + 6844
16 CoreFoundation 0x00000001018dea31 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
17 CoreFoundation 0x00000001018d495c __CFRunLoopDoSources0 + 556
18 CoreFoundation 0x00000001018d3e13 __CFRunLoopRun + 867
19 CoreFoundation 0x00000001018d3828 CFRunLoopRunSpecific + 488
20 GraphicsServices 0x0000000106247ad2 GSEventRunModal + 161
21 UIKit 0x000000010262e610 UIApplicationMain + 171
22 Radio 0x00000001014afd5d main + 109
23 libdyld.dylib 0x000000010468892d start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
我找到了从 link 中删除 status observer
的解决方案,但不知道如何在 swift 中转换它。
Solution
@try { [player removeObserver:self forKeyPath:@"status"]; } @catch (NSException *exception) { } @finally { } –
问题是 "deallocObservers" 方法被调用了两次,但在这两次调用之间没有添加任何观察者。我想曾经在这里:
if player != nil && player?.currentItem != nil {
deallocObservers(player!)
}
第二次调用该方法时,玩家变量和当前物品不会为null。第二次调用 deallocObservers 可能在这里:
if bt.selected == true
{
bt.selected = false
deallocObservers(player!)
}
我会在 deallocObservers 方法中放置一个断点,以查看此方法被调用了多少次以及调用它的人(查看堆栈跟踪)。
swift 版本
Objective C
@try { [player removeObserver:self forKeyPath:@"status"]; } @catch (NSException *exception) { } @finally { }
Swift
do {
try player.removeObserver(self, forKeyPath: "status")
}
catch {
print("error")
}
defer{
print("finally statement here")
}
我添加了对这些条件的检查。
var check = true
func audioControlButtonAction(sender: UIButton){
if check == false {
deallocObservers(player!)
}
if sender.selected == false{
check = false
}
else{
check = true
}
}
我正在尝试显示 ActivityIndicator
播放器(缓冲或加载)和开始播放时停止播放 ActivityIndicator
。此外,当我停止播放器时,它是 AVPlayer 的(removeObserver 或 deallocObservers)。
当我播放音乐时,它会显示 ActivityIndicator
直到它准备好播放,但它会在播放前 4.5 秒停止动画 ActivityIndicator`。问题是当我停止 AVPlayer 或点击另一个播放按钮时,它在删除 AVPlayer 观察器时给我错误。
谁能告诉我我的代码哪里有错误,我该如何解决,谢谢。
var selectIndex:Int = -1
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! RadioCollectionViewCell
cell.backgroundColor = UIColor.yellowColor()
let object = objects[indexPath.row]
cell.img.image = UIImage(named: object["image"]!)
cell.btnPlay.addTarget(self, action: Selector("audioControlButtonAction:"), forControlEvents: UIControlEvents.TouchUpInside)
cell.btnPlay.tag = indexPath.row+1
return cell
}
func audioControlButtonAction(sender: UIButton){
if player != nil && player?.currentItem != nil {
deallocObservers(player!)
}
var btn:NSInteger
btn = sender.tag as NSInteger
let object = objects[btn-1]
let nurl = NSURL(string: "\(object["url"]!)")!
playerItem = AVPlayerItem(URL: nurl)
player=AVPlayer(playerItem: playerItem!)
print(selectIndex)
if selectIndex != -1 && selectIndex != sender.tag
{
let bt:UIButton = self.view.viewWithTag(selectIndex) as! UIButton
if bt.selected == true
{
bt.selected = false
deallocObservers(player!)
}
}
if sender.selected == false
{
player!.addObserver(self, forKeyPath: "status", options:NSKeyValueObservingOptions(), context: nil)
player!.addObserver(self, forKeyPath: "playbackBufferEmpty", options:NSKeyValueObservingOptions(), context: nil)
// player!.addObserver(self, forKeyPath: "playbackLikelyToKeepUp", options:NSKeyValueObservingOptions(), context: nil)
player!.addObserver(self, forKeyPath: "loadedTimeRanges", options: NSKeyValueObservingOptions(), context: nil)
player!.play()
sender.selected = true
selectIndex = sender.tag
activityView.startAnimating()
// self.view.userInteractionEnabled = false
}
else
{
deallocObservers(player!)
player?.pause()
sender.selected = false
selectIndex = -1
}
print(selectIndex)
}
func deallocObservers(player: AVPlayer) {
player.removeObserver(self, forKeyPath: "status")
player.removeObserver(self, forKeyPath: "playbackBufferEmpty")
// player.removeObserver(self, forKeyPath: "playbackLikelyToKeepUp")
player.removeObserver(self, forKeyPath: "loadedTimeRanges")
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>){
if object?.isEqual(player) == true && keyPath == "status" {
print("status")
if player?.status == AVPlayerStatus.ReadyToPlay{
print("AVPlayerStatus.ReadyToPlay")
activityView.stopAnimating()
// self.view.userInteractionEnabled = true
}else{
print("AVPlayerStatus.NotReadyToPlay")
activityView.startAnimating()
// self.view.userInteractionEnabled = false
}
//if keyPath == "playbackLikelyToKeepUp" {
//activityView.stopAnimating()
////self.view.userInteractionEnabled = true
//print("playbackLikelyToKeepUp")
//}
if keyPath == "playbackBufferEmpty" {
activityView.startAnimating()
self.view.userInteractionEnabled = false
let createAccountErrorAlert: UIAlertView = UIAlertView()
createAccountErrorAlert.delegate = self
createAccountErrorAlert.title = "No Internet Connection"
createAccountErrorAlert.message = "Make sure your device is connected to the internet."
createAccountErrorAlert.addButtonWithTitle("Dismiss")
createAccountErrorAlert.show()
print("playbackBufferEmpty")
}
if player?.status == AVPlayerStatus.Failed{
print("Something went wrong . player.error should contain some information")
}
}
}
Output
-1
1
status
AVPlayerStatus.ReadyToPlay
1
2016-07-24 21:51:00.585 Radio[777:23560] *** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <Radio.RadioCollectionViewController 0x7fbe39d47f70> for the key path "status" from <AVPlayer 0x7fbe39e67410> because it is not registered as an observer.'
*** First throw call stack:
(
0 CoreFoundation 0x00000001019b2e65 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x0000000103b51deb objc_exception_throw + 48
2 CoreFoundation 0x00000001019b2d9d +[NSException raise:format:] + 205
3 Foundation 0x0000000101fc4d51 -[NSObject(NSKeyValueObserverRegistration) _removeObserver:forProperty:] + 504
4 Foundation 0x0000000101fc4abd -[NSObject(NSKeyValueObserverRegistration) removeObserver:forKeyPath:] + 84
5 Radio 0x00000001014a935d _TFC5Radio29RadioCollectionViewController16deallocObserversfS0_FCSo8AVPlayerT_ + 141
6 Radio 0x00000001014a8fbe _TFC5Radio29RadioCollectionViewController24audioControlButtonActionfS0_FCSo8UIButtonT_ + 5326
7 Radio 0x00000001014a92ba _TToFC5Radio29RadioCollectionViewController24audioControlButtonActionfS0_FCSo8UIButtonT_ + 58
8 UIKit 0x0000000102630194 -[UIApplication sendAction:to:from:forEvent:] + 92
9 UIKit 0x000000010279f6fc -[UIControl sendAction:to:forEvent:] + 67
10 UIKit 0x000000010279f9c8 -[UIControl _sendActionsForEvents:withEvent:] + 311
11 UIKit 0x000000010279eaf8 -[UIControl touchesEnded:withEvent:] + 601
12 UIKit 0x000000010269f49b -[UIWindow _sendTouchesForEvent:] + 835
13 UIKit 0x00000001026a01d0 -[UIWindow sendEvent:] + 865
14 UIKit 0x000000010264eb66 -[UIApplication sendEvent:] + 263
15 UIKit 0x0000000102628d97 _UIApplicationHandleEventQueue + 6844
16 CoreFoundation 0x00000001018dea31 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
17 CoreFoundation 0x00000001018d495c __CFRunLoopDoSources0 + 556
18 CoreFoundation 0x00000001018d3e13 __CFRunLoopRun + 867
19 CoreFoundation 0x00000001018d3828 CFRunLoopRunSpecific + 488
20 GraphicsServices 0x0000000106247ad2 GSEventRunModal + 161
21 UIKit 0x000000010262e610 UIApplicationMain + 171
22 Radio 0x00000001014afd5d main + 109
23 libdyld.dylib 0x000000010468892d start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
我找到了从 link 中删除 status observer
的解决方案,但不知道如何在 swift 中转换它。
Solution
@try { [player removeObserver:self forKeyPath:@"status"]; } @catch (NSException *exception) { } @finally { } –
问题是 "deallocObservers" 方法被调用了两次,但在这两次调用之间没有添加任何观察者。我想曾经在这里:
if player != nil && player?.currentItem != nil {
deallocObservers(player!)
}
第二次调用该方法时,玩家变量和当前物品不会为null。第二次调用 deallocObservers 可能在这里:
if bt.selected == true
{
bt.selected = false
deallocObservers(player!)
}
我会在 deallocObservers 方法中放置一个断点,以查看此方法被调用了多少次以及调用它的人(查看堆栈跟踪)。
swift 版本
Objective C
@try { [player removeObserver:self forKeyPath:@"status"]; } @catch (NSException *exception) { } @finally { }
Swift
do {
try player.removeObserver(self, forKeyPath: "status")
}
catch {
print("error")
}
defer{
print("finally statement here")
}
我添加了对这些条件的检查。
var check = true
func audioControlButtonAction(sender: UIButton){
if check == false {
deallocObservers(player!)
}
if sender.selected == false{
check = false
}
else{
check = true
}
}