Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remote Control event in iOS with Swift

Trying to figure out how to read the Apple headphone's volume buttons to use as a trigger for the camera shutter (as the Apple Camera app does).

From the documentation on Remote Control Events, Remote Control Received With Event, and this git repo, I've pieced together that I'll probably need an AVAudioPlayer object, .beginReceivingRemoteControlEvents(), and remoteControlReceivedWithEvent, along with making this view canBecomeFirstResponder() return true.

import UIKit
import AVFoundation

class ViewController: UIViewController, AVAudioPlayerDelegate {
    var player: AVAudioPlayer!
    override func viewDidLoad() {
        super.viewDidLoad()

        var session: AVAudioSession = AVAudioSession.sharedInstance()
        session.setActive(true, error: nil)
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        println("viewDidAppear worked...")
        self.becomeFirstResponder()
        UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
    }

    override func canBecomeFirstResponder() -> Bool {
        return true
    }

    override func remoteControlReceivedWithEvent(event: UIEvent) {
        let rc = event.subtype
        println("does this work? \(rc.rawValue)")
        //takePicture()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

I expected to get "does this work" when hitting the volume buttons on the headphones, instead I just see it adjust the headphone volume like normal. So I must be missing something, maybe with a delegate or AVSession?

like image 231
Cody Avatar asked May 25 '15 05:05

Cody


2 Answers

I cross-posted this on r/swift, where I was told it probably requires playing audio (quoted straight from the documentation).

So while this isn't the ideal solution, it works for my own private use.

import UIKit
import AVFoundation
import MediaPlayer

class ViewController: UIViewController, AVAudioPlayerDelegate {
    var testPlayer: AVAudioPlayer? = nil

    func loadSound(filename: NSString) -> AVAudioPlayer {
        let url = NSBundle.mainBundle().URLForResource(filename as String, withExtension: "caf")
        var error: NSError? = nil
        let player = AVAudioPlayer(contentsOfURL: url, error: &error)
        if error != nil {
            println("Error loading \(url): \(error?.localizedDescription)")
        } else {
            player.prepareToPlay()
        }
        return player
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.testPlayer = self.loadSound("silence")
        self.testPlayer?.numberOfLoops = -1
        self.testPlayer?.play()
    }

    override func canBecomeFirstResponder() -> Bool {
        return true
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        self.becomeFirstResponder()
        UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
    }

    override func remoteControlReceivedWithEvent(event: UIEvent) {
        let rc = event.subtype
        println("rc.rawValue: \(rc.rawValue)")
        // take photo
    }
}

I noticed that in Apple's camera app, the +/- volume buttons trigger the camera, and the microphone button pauses/plays any audio running in another app, but in this implementation the volume buttons still control the volume (and any audio has been paused when the app is launched).

An rc.rawValue: 103 corresponds to a single click of the microphone button, a double click returns 104, and a triple click returns 105, and then sometimes bumping a couple at a time returns a 108 or 109.

like image 74
Cody Avatar answered Oct 13 '22 01:10

Cody


Based on Cody's answer but updated for 2019 (Swift 5)

import UIKit
import AVFoundation
import MediaPlayer

class ViewController: UIViewController, AVAudioPlayerDelegate {
    var myPlayer: AVAudioPlayer? = nil

    func loadSound(filename: NSString) -> AVAudioPlayer? {
        let url = Bundle.main.url(forResource: filename as String, withExtension: "mp3")
        do {
            let player = try AVAudioPlayer(contentsOf: url ?? URL(fileURLWithPath: ""))
            player.prepareToPlay()
            return player
        }
        catch {
            print("Error : \(error)")
            return nil
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        guard let testPlayer = loadSound(filename: "silence") else {
            print("Not able to load the sound")
            return
        }
        testPlayer.delegate = self
        testPlayer.volume = 0.8
        testPlayer.numberOfLoops = -1
        myPlayer = testPlayer
        myPlayer?.play()
    }


    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.becomeFirstResponder()
        UIApplication.shared.beginReceivingRemoteControlEvents()
    }

    override func remoteControlReceived(with event: UIEvent?) {
        let rc = event?.subtype
        print("rc.rawValue: \(rc?.rawValue)")
        // Do your thing
    }
}
like image 22
Uriel Barbosa Avatar answered Oct 13 '22 02:10

Uriel Barbosa