How to disable the Haptic Feedback when playing a Live Photo - ios

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)

Related

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 stop 2 different playing audio at the same time?

How can I stop 2 different playing audio at the same time? When music is playing, another button pressed playing it's audio at the same time. Sorry for my english.
NoiseMaker:
import AVFoundation
class NoiseMaker {
let audioFileNames = ["guitar", "applause", "monster", "bubbles"]
let players: [AVAudioPlayer?]
init() {
players = audioFileNames.map { filename in
if let url = NSBundle.mainBundle().URLForResource(filename, withExtension: "wav") {
return try? AVAudioPlayer(contentsOfURL: url)
} else {
return nil
}
}
}
func play(index: Int) {
if !players.isEmpty && index >= 0 && index < players.count {
players[index]?.play()
}
}
}
ViewController:
import UIKit
class ViewController: UIViewController {
let noiseMaker = NoiseMaker()
#IBAction func playSound(sender: UIButton) {
noiseMaker.play(sender.tag)
}
}
Add an instance variable for the currently playing sound. When you tap a button, send a stop message to the currently playing sound, copy the new sound to the instance variable, and then start that sound playing.

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()
}
}

How to play and pause music in SpriteKit

I have background music in my game. I tried to make a method to play and pause the music.
My app crashes when I press on the play/pause music button.
I don't understand why its not working.
The method in the Main Scene: (Edited)
var SoundOnOff = SKSpriteNode(imageNamed: "MusicOn.png")
if (SoundOnOff.containsPoint(location)) {
if BackgroundMusic.sharedHelper.isMuted() {
//BackgroundMusic.sharedHelper.mute()
BackgroundMusic.sharedHelper.pause()
self.SoundOnOff.texture = SKTexture(imageNamed:"MusicOff.png")
print("Music Off!")
}
else {
//BackgroundMusic.sharedHelper.unmute()
BackgroundMusic.sharedHelper.resume()
self.SoundOnOff.texture = SKTexture(imageNamed:"MusicOn.png")
print("Music On!")
}
}
BackgroundMusic Class (Edited)
import AVFoundation
class BackgroundMusic: NSObject {
internal let localDefaults = NSUserDefaults.standardUserDefaults()
static let sharedHelper = BackgroundMusic()
var BgMusic: AVAudioPlayer?
/// Keys
internal struct Key {
static let muted = "MusicMuteState"
}
override init() {
super.init()
print("Music helper init")
playBackgroundMusic()
if isMuted() {
mute()
}
}
func playBackgroundMusic() {
let aSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Secrets of the Schoolyard", ofType: "mp3")!)
do {
BgMusic = try AVAudioPlayer(contentsOfURL:aSound)
BgMusic!.numberOfLoops = -1
BgMusic!.prepareToPlay()
BgMusic!.play()
} catch {
print("Cannot play the file")
}
}
func mute() {
BgMusic!.volume = 0
localDefaults.setBool(true, forKey: Key.muted)
}
/// Unmute
func unmute() {
BgMusic!.volume = 1
localDefaults.setBool(false, forKey: Key.muted)
}
// Check mute state
func isMuted() -> Bool {
if localDefaults.boolForKey(Key.muted) {
return true
} else {
return false
}
}
}
Your class is a subclass of AVAudioPlayer but you're not actually using your own instance to play the music. When you use BackgroundMusic.sharedHelper.playing, you're referencing an instance of your class, not the BgMusic player that is actually playing. Since your class hasn't been initialized with any sound file, I presume that it cannot properly process .playing
I recently answered a similar question. I am also using my own helper for music which is similar to yours. There is a few things that you should do different in your helper.
On and off music/sfx for sprite kit
GitHub: https://github.com/crashoverride777/Swift2-SpriteKit-Music-Helper

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

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.

Resources