Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I implement a volume shutter in iOS 11?

I want to implement a volume shutter in my camera app. When the user presses the volume button, I should get an event to take a photo.

I'm looking for an implementation that meets the following requirements:

  • It should work even if the volume is currently at the maximum, and the user presses the volume up button.
  • There should be no on-screen UI showing that the volume changed.
  • There should be no known cases of Apple rejecting an app that used this technique.

Other questions and answers exist on this topic, but for older versions of iOS, so I wanted to find one that works on iOS 11.

Camera apps like ProCamera, ProCam and Camera+ have the volume shutter that satisfies all these conditions, so it's clearly possible.

like image 974
Kartick Vaddadi Avatar asked Jan 12 '17 16:01

Kartick Vaddadi


People also ask

How do I turn on the shutter sound on my iPhone 11?

You can adjust the sound of the shutter in Camera , or mute it using the Ring/Silent switch on the side of your iPhone.

How do volume buttons work on iPhone 11?

Adjust the volumePress the Volume buttons on the left side of the device to adjust media or call volume. You can also adjust the volume from the Sounds & Haptics screen. Select and drag the Volume slider as desired. To enable or disable changing the volume with buttons, select the Change with Buttons switch.


2 Answers

So here is the code that will meet all your requirements – I'm not sure whether or not Apple will approve this though.
I've pulled all this code from questions/answers here on StackOverflow.

Tested with iOS 10.2 in Xcode 8.3.1

You need to use the AVFoundation and MediaPlayer frameworks for this to work.

import UIKit
import AVFoundation
import MediaPlayer

class ViewController: UIViewController {

    //keeps track of the initial volume the user had set when entering the app
    //used to reset the volume when he exits the app
    var volume: Float = 0

    override func viewDidLoad() {
        super.viewDidLoad()

        let audioSession = AVAudioSession.sharedInstance()
        volume = audioSession.outputVolume-0.1 //if the user is at 1 (full volume)
        try! audioSession.setActive(true)
        audioSession.addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil)
        //prevents the volume hud from showing up
        let volView = MPVolumeView(frame: .zero)
        view.addSubview(volView)
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        //when the user changes the volume,
        //prevent the output volume from changing by setting it to the default volume we specified,
        //so that we can continue pressing the buttons for ever
        (MPVolumeView().subviews.filter{NSStringFromClass($0.classForCoder) == "MPVolumeSlider"}.first as? UISlider)?.setValue(volume, animated: false)

        //implement your photo-capturing function here
        print("volume changed")
    }
}

Update

If you want to make sure your code is still working after the user exits the app, use the AppDelegate to install the observer when the app becomes active, like this:

AppDelegate

import UIKit
import AVFoundation
import MediaPlayer

var volume: Float = 0.5

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    let audioSession = AVAudioSession.sharedInstance()

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        return true
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        (MPVolumeView().subviews.filter{NSStringFromClass($0.classForCoder) == "MPVolumeSlider"}.first as? UISlider)?.setValue(volume, animated: false)

        NotificationCenter.default.removeObserver(self)
        NotificationCenter.default.post(Notification(name: Notification.Name(rawValue: "volumeChanged")))
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        volume = audioSession.outputVolume
        if volume == 0 { volume += 0.1 } else if volume == 1 { volume -= 0.1 }
        try! audioSession.setActive(true)
        audioSession.addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil)
    }

    func applicationWillResignActive(_ application: UIApplication) {
        audioSession.removeObserver(self, forKeyPath: "outputVolume")
    }
}

ViewController

import UIKit
import AVFoundation
import MediaPlayer

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        NotificationCenter.default.addObserver(self, selector: #selector(self.volumeChanged), name: Notification.Name(rawValue: "volumeChanged"), object: nil)
        //prevents the volume hud from showing up
        let volView = MPVolumeView(frame: .zero)
        view.addSubview(volView)
    }

    func volumeChanged() {
        print("volume changed")
    }
}
like image 110
LinusGeffarth Avatar answered Oct 16 '22 10:10

LinusGeffarth


As per Apple's App Store Review Guidelines, this would be explicit grounds for rejection.

2.5.9 Apps that alter the functions of standard switches, such as the Volume Up/Down and Ring/Silent switches, or other native user interface elements or behaviors will be rejected.

Source: Is it possible to disable volume buttons in iOS apps?

like image 23
metc500 Avatar answered Oct 16 '22 09:10

metc500