How to implement a volume key shutter for iPhone?

huxia picture huxia · Dec 6, 2011 · Viewed 12.7k times · Source

I want to implement the same behavior with the native camera of iOS5:

  • press the volume + button to take a photo

What's the ideal way to archive it? Are there any ways to capture the volume key pressed event?

After googling & searching around for hours, I found 1 solution: using NSNotificationCenter:

...
    [[NSNotificationCenter defaultCenter]
         addObserver:self
         selector:@selector(volumeChanged:)
         name:@"AVSystemController_SystemVolumeDidChangeNotification"
         object:nil];
...
- (void)volumeChanged:(NSNotification *)notification{
    [self takePhoto];   
}

However, it has 2 issues:

  • There is an semi-transparent overlay of "current system volume" show up every time when pressing the volume key, this is not what I wanted.
  • For the native camera, when you press the volume key as shutter, the system volume won't change, however, by using the above method, the system volume will change.

Answer

huxia picture huxia · Dec 6, 2011

I've found another way to hide the "system volume overlay" and "bypass the system volume change when the volume key pressed" by myself.

The bad part: this is an super UGLY hack.

However, the good part is: this ugly hack uses NO private APIs.

Another note is: it only works for ios5+ (anyway, for my issue, since the AVSystemController_SystemVolumeDidChangeNotification only works for ios5, so this UGLY hack just fits my issue.)

The way it work: "act as a music/movie player app and let the volume key to adjust the application-volume".

Code:

// these 4 lines of code tell the system that "this app needs to play sound/music"
AVAudioPlayer* p = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"photo-shutter.wav"]] error:NULL];
[p prepareToPlay];
[p stop];
[p release];

// these 5 lines of code tell the system that "this window has an volume view inside it, so there is no need to show a system overlay"
[[self.view viewWithTag:54870149] removeFromSuperview];
MPVolumeView* vv = [[MPVolumeView alloc] initWithFrame:CGRectMake(-100, -100, 100, 100)];
[self.view addSubview:vv];
vv.tag = 54870149;
[vv release];

(5 hours spending on discovering this super ugly method... shit... 草尼马啊!)

Another thing: if you take the above hack, you need to run the code EVERY-TIME when your app become active. So, you might need to put some code into your app delegate.

- (void)applicationDidBecomeActive:(UIApplication *)application