MPMoviePlayerController Doesn't Play when receiving a remote control event? - ios

So I've got a MPMoviePlayerController playing a video in the background.
If I tell it to load a different video using remote control notifications it works just fine.
However if I tell it to play the video it doesn't play?
Has anyone else had this problem or found a solution?
Code snippet:
override func canBecomeFirstResponder() -> Bool {
return true
}
override func remoteControlReceivedWithEvent(event: UIEvent!) {
if (event.type == UIEventType.RemoteControl){
if (event.subtype.toRaw() == 100 || event.subtype.toRaw() == 101){
didPressPausePlay(self)
}else if(event.subtype.toRaw() == 104){
didPressNext(self)
}else if(event.subtype.toRaw() == 105){
didPressPrevious(self)
}
}
}
#IBAction func didPressPrevious(sender: AnyObject) {
videoTitle.text = ""
if (currentIndex != 0){
currentIndex--
currentVideo = parsedVideoIds[currentIndex] as NSString
videoPlayerViewController = XCDYouTubeVideoPlayerViewController(videoIdentifier: currentVideo);
videoPlayerViewController.moviePlayer.backgroundPlaybackEnabled = true;
videoPlayerViewController.presentInView(self.view);
videoPlayerViewController.moviePlayer.controlStyle = MPMovieControlStyle.None
self.view.bringSubviewToFront(customControls);
videoPlayerViewController.moviePlayer.play()
currentImage = 0
pauseplayButton.setImage(pauseImage, forState: UIControlState.Normal)
}
}
I left out the rest because it all does the same thing.

Related

is that possible to trigger another gestureRecognizer in one gestureRecognizer (in swift)?

if i click image long then func actionLongPress get started and as you can see, if i click image long then i want to move that image along my touch... but this code doesn't work...
there is no error. even though i click image long, image doesn't follow my touch..
do you fix this trouble???
#objc func actionLongPress(Recog : UILongPressGestureRecognizer){
if Recog.state == .began{
AudioServicesPlaySystemSound(1519)
addPanGesture(view: addedImage)
origin = addedImage.frame.origin
} else if Recog.state == .ended{
AudioServicesPlaySystemSound(1519)
}
}
You are missing one state that you need to implement ... which is state change
#objc func actionLongPress(Recog : UILongPressGestureRecognizer){
if Recog.state == .began{
AudioServicesPlaySystemSound(1519)
addPanGesture(view: addedImage)
origin = addedImage.frame.origin
} else if Recog.state == .changed {
// assign view frame here
let location = Recog.location(in: self)
your_view?.center = targetPosition
} else if Recog.state == .ended{
AudioServicesPlaySystemSound(1519)
}
}

Cannot assign value of type 'Bool' to type 'UIButton?'

I am trying to hide a button, until another condition is met. When i load the app i am using the slider and when it reaches the max, it should make the button visible, but for some weird reason it won't. I have just done it in another app with no issues and the code is almost identical.?
Can't figure out what to do tbh.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
profileVisible.isHidden = true
}
#IBAction func btnClicked(_ sender: Any) {
motionManager.startDeviceMotionUpdates(to: queue) { (motion, error) in DispatchQueue.main.async {
self.slider.value = Float((motion?.attitude.roll ?? 0 ) * 1.4)
print(self.slider.value)
if self.slider.value == 1.0 {
self.profileVisible = false
self.motionManager.stopDeviceMotionUpdates()
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate); // vibrates when payment has succeded
}
if self.slider.value == -1.0 {
profileVisible = true
self.motionManager.stopDeviceMotionUpdates()
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
}
}
}
You miss setting here
if self.slider.value == 1.0 {
self.profileVisible = false
And here
if self.slider.value == -1.0 {
profileVisible = true
Should be
profileVisible.isHidden = true/false // set it's value according to your logic
profileVisible is of type UIButton you need to set it's isHidden property not it directly

How to disable the Haptic Feedback when playing a Live Photo

In my app, I allow the user to play Live Photos from their photo library.
To do this, I use the following:
#IBOutlet weak var livePhotoView: PHLivePhotoView!
#objc func handleTapGesture(_ recognizer: UITapGestureRecognizer) {
if let currentType = currentMediaType {
if currentType == .livePhoto && livePhotoIsPlaying == false {
playImageView.alpha = 0
backButtonView.alpha = 0
ivePhotoView.startPlayback(with: .full)
livePhotoIsPlaying = true
} else if currentType == .livePhoto && livePhotoIsPlaying == true {
livePhotoView.stopPlayback()
livePhotoIsPlaying = false
}
}
}
By using this method, I get Haptic Feedback whenever the Live Photo is played which I don't want. Is this the normal behavior of PHLivePhotoView, and is there a way to disable it?
PHLivePhotoViews have an enum property called PHLivePhotoViewPlaybackStyle that determines if the playback should include haptic feedback.
To disable haptic feedback on playback use:
livePhotoView.startPlayback(with: .hint)
instead of:
livePhotoView.startPlayback(with: .full)

AVPlayer state listener in swift

I want to know how to get the state of my player (AVPlayer) (buffering, playing, stopped, error) and update the ui according to those states (including the player on the lock screen). How exactly should I do it?
I have a label that may contain:
"Buffering...", "Playing", "Stopped" or "Error".
Basically, I have the following:
MediaPlayer:
import Foundation
import AVFoundation
class MediaPlayer {
static let sharedInstance = MediaPlayer()
fileprivate var player = AVPlayer(url: URL(string: "my_hls_stream_url_here")!)
fileprivate var isPlaying = false
func play() {
player.play()
isPlaying = true
}
func pause() {
player.pause()
isPlaying = false
}
func toggle() {
if isPlaying == true {
pause()
} else {
play()
}
}
func currentlyPlaying() -> Bool {
return isPlaying
}
}
PlayerViewController:
class PlayerViewController: UIViewController {
#IBOutlet weak var label: UILabel!
#IBAction func playStopButtonAction(_ sender: UIButton) {
MediaPlayer.sharedInstance.toggle()
}
override func viewDidLoad() {
super.viewDidLoad()
label.text = "Disconnected"
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
print("Audio session ok\n")
} catch {
print("Error: Audio session.\n")
}
// Show only play/pause button on the lock screen
if #available(iOS 9.1, *) {
let center = MPRemoteCommandCenter.shared()
[center.previousTrackCommand, center.nextTrackCommand, center.seekForwardCommand, center.seekBackwardCommand, center.skipForwardCommand, center.skipBackwardCommand, center.ratingCommand, center.changePlaybackRateCommand, center.likeCommand, center.dislikeCommand, center.bookmarkCommand, center.changePlaybackPositionCommand].forEach {
$0.isEnabled = false
}
center.togglePlayPauseCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
MediaPlayer.sharedInstance.toggle()
return MPRemoteCommandHandlerStatus.success
}
center.playCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
MediaPlayer.sharedInstance.play()
return MPRemoteCommandHandlerStatus.success
}
center.pauseCommand.addTarget { (commandEvent) -> MPRemoteCommandHandlerStatus in
MediaPlayer.sharedInstance.pause()
return MPRemoteCommandHandlerStatus.success
}
} else {
// Fallback on earlier versions
print("Error (MPRemoteCommandCenter)")
}
}
override func remoteControlReceived(with event: UIEvent?) {
guard let event = event else {
print("No event\n")
return
}
guard event.type == UIEventType.remoteControl else {
print("Another event received\n")
return
}
switch event.subtype {
case UIEventSubtype.remoteControlPlay:
print("'Play' event received\n")
case UIEventSubtype.remoteControlPause:
print("'Pause' event received\n")
case UIEventSubtype.remoteControlTogglePlayPause:
print("'Toggle' event received\n")
default:
print("\(event.subtype)\n")
}
}
}
I think you could use the timeControlStatus property of AVPlayer. According to the doc it can be paused, waitingToPlayAtSpecifiedRate which is basically what you call buffering or playing.
If you really need the error state, you could observe the error property or whether the status property is set to failed.
A simple KVO observer on these properties would do the trick.
A place to start could be through using the AVPlayer's "status" property. It is an enumeration that contains the following values (this is taken directly from the documentation):
'unknown': Indicates that the status of the player is not yet known because it has not tried to load new media resources for playback.
'readyToPlay': Indicates that the player is ready to play AVPlayerItem instances.
'failed': Indicates that the player can no longer play AVPlayerItem instances because of an error.
As to how you could tell if the content is actually playing, you could just use boolean checks as it seems you have partially implemented. For pausing and stopping, you could just keep the file loaded for pause, and delete the file for stop that way you could differentiate the two.
For buffering, if the enum is not unknown or readyToPlay then that theoretically should mean that there is a file being attached but is not quite ready to play (i.e. buffering).

How can I know if AVAudioPlayer is paused?

To discover whether an AVAudioPlayer object is playing, I can simply use the isPlaying property:
if audioPlayer.isPlaying {
// ...
}
If the audio player is not playing however, how can I distinguish between it being paused and it being stopped?
I can't use the currentTime property for this purpose. This is because if I call stop(), then the current time stays at whatever time it was at the time of the call, which is the same behaviour as when pause() is called.
I think it's as simple as keep a BOOL property in view controller class which set to YES while play/resume buttons clicked and set to NO while stop/pause button pressed.
Its made from this this link:
if ((player.rate != 0) && (player.error == nil)) {
// player is playing
}else {
//notplaying
}
Simply add Bool variable and based on that you check is audio paused or not.
Ex:
var isAudioPaused = false//Add this on top
//Pause audio
func pauseAudio() {
if isAudioPaused == false {
if audioPlayer?.isPlaying == true {//Check is audio playing
audioPlayer?.pause()//Pause audio
isAudioPaused = true
btnPauseAudio.setTitle("Play Audio", for: .normal)
}
} else {
audioPlayer?.play()//Play audio
isAudioPaused = false
btnPauseAudio.setTitle("Pause Audio", for: .normal)
}
}
You can check player that is paused or not though code that is player.isPlaying && player.duration != 0
func buttonOneClicked(button: UIButton) {
if player.isPlaying && player.duration != 0 {
button.setImage(UIImage(systemName: "stop"), for: .normal)
player.pause()
}
else{
button.setImage(UIImage(systemName: "play"), for: .normal)
player.play()
}
}

Resources