How to detect volume button press on iOS 9? - ios

I searched the solution for hours and it seems that AVSystemController_SystemVolumeDidChangeNotification should work. But after I finished the code I didn't see any callback when pressing the volume button. Here is my code:
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(volumeChanged:)
name:#"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
NSLog(#"init finished");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)volumeChanged:(NSNotification *)notification
{
float volume =
[[[notification userInfo]
objectForKey:#"AVSystemController_AudioVolumeNotificationParameter"]
floatValue];
NSLog(#"current volume = %f", volume);
}

Related

iPad UIKeyboard Hide / Resign / Dismiss Button Event Handler?

Can you see the button in the bottom right of the iPad keyboard?
Well I want access to the event handler for that button, Because, I have some logic that I need to administer when specifically THAT button is pressed.
My attempted solution included using the UIKeyboardWillHideNotification notification like so:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(hideKeyboardTapped:)
name:UIKeyboardWillHideNotification
object:nil];
However this is called everytime the keyboard is dismissed, including when search cancel button is pressed, with a gesture tap on the background, etc; all of which have slightly different logic.
So I just need to have one set of logic for when that specific "hide keyboard" button is pressed.
The UIKeyboard is activated by a UISearchBar (for those of you who need extra info).
Any help would be greatly appreciated; and I'll answer any questions you may have.
There is not an official, Apple-sanctioned way to do this.
If you don't mind mucking around with private classes, which could get you rejected from the App Store, there is an unsupported method. Basically, you listen for changes to the keyboard's activeKey property, and if the new active key has a name of "Dismiss-Key", then the dismiss key was tapped:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"activeKey"])
{
id activeKey = [object valueForKey:keyPath];
NSString *keyName = [activeKey valueForKey:#"name"];
if ([keyName isEqualToString:#"Dismiss-Key"])
{
[self didTapDismissKey];
}
}
else
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)keyboardDidShow:(NSNotification *)notification
{
// Search for the UIKeyboardLayoutStar class, which seems to manage layout for the keyboard, and observe its activeKey property
UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
[self traverseViewHierarchyWithView:topWindow block:^(UIView *view, BOOL *stop) {
if ([view isKindOfClass:NSClassFromString(#"UIKeyboardLayoutStar")])
{
[view addObserver:self forKeyPath:#"activeKey" options:0 context:NULL];
}
}];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
// Don't forget to remove the controller as an observer!
UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
[self traverseViewHierarchyWithView:topWindow block:^(UIView *view, BOOL *stop) {
if ([view isKindOfClass:NSClassFromString(#"UIKeyboardLayoutStar")])
{
[view removeObserver:self forKeyPath:#"activeKey"];
}
}];
}
- (BOOL)traverseViewHierarchyWithView:(UIView *)view block:(void (^)(UIView *, BOOL *))block
{
BOOL stop = NO;
block(view, &stop);
if (stop)
{
return YES;
}
for (UIView *subview in view.subviews)
{
if ([self traverseViewHierarchyWithView:subview block:block])
{
return YES;
}
}
return NO;
}
- (void)didTapDismissKey
{
NSLog(#"Dismiss key tapped");
}
Tested on the iOS 7 iPad simulator and it works.
I don't know if you can put a listener to a single button of the keyboard, but you can try to set some Booleans on the views, that can turn on/off the events that you want or don't want to handle (I would approach it like that), for example:
Add a custom notification observer/listener at the class where you
want to execute the method of the event.
Add the Apple default Keyboard Hide listener on the controller where the notification will be triggered.
Then, on the views that can trigger the event... Set the booleans to activate/deactivate the event action
#Some Other Class
-(void)viewWillAppear:(BOOL)animated{
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(doSomething)
name:#"doSomethingNotification"
object:nil];
}
-(void)viewWillDisappear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"doSomethingNotification" object:nil];
}
#Trigger Class
-(void)viewWillAppear:(BOOL)animated{
self.isNotificationNeeded = NO;
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(onKeyboardHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
-(void)viewWillDisappear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
-(void)onKeyboardHide:(id)sender{
//When your event is Happening
if (self.isNotificationNeeded == YES) {
NSNotification *n = [NSNotification notificationWithName:#"doSomethingNotification" object:nil];
[[NSNotificationCenter defaultCenter] postNotification:n];
self.isNotificationNeeded = NO;
}
}
-(void)userClickedCancelButton{
//If this is a keyboard hide event that you don't want to trigger
self.isNotificationNeeded = NO;
}
//SearchBarDelegate
-(BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar{
self.isNotificationNeeded = YES; //If your event can happen from here
return YES;
}

Change orientation with NSNotificationCenter and UIDeviceOrientation

Why don´t work when I change the orientation of the simulator in xCode? I see equals the label on the ViewController all time and the NSLog doesn´t appear.
ViewController.m:
- (void)viewDidLoad
{
[self orientacionActual];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(orientacionActual)
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(UIDeviceOrientation) orientacionActual
{
UIDeviceOrientation orientacionActual = [UIDevice currentDevice].orientation;
return orientacionActual;
}
- (void) cambiaOrientacion:(NSNotification *) notificación
{
if(UIDeviceOrientationIsLandscape([self orientacionActual]))
{
self.LabelEstoy.text = #"Landscape";
NSLog(#"Landscape");
} else if (UIDeviceOrientationIsPortrait([self orientacionActual]))
{
self.LabelEstoy.text = #"Portrait";
NSLog(#"Portrait");
}
}
Call UIApplicationDidChangeStatusBarOrientationNotification instead of device orientation changes. Also, I'm not sure what is your goal, but try to use this snippet:
Inside viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(cambiaOrientacion:)
name:UIApplicationDidChangeStatusBarOrientationNotification
object:nil];
and below:
- (void)cambiaOrientacion:(NSNotification *)notificacion
{
UIInterfaceOrientation orientation = [[[notification userInfo]
objectForKey:UIApplicationStatusBarOrientationUserInfoKey] integerValue];
if (UIInterfaceOrientationIsLandscape(orientation))
{
self.LabelEstoy.text = #"Landscape";
NSLog(#"Landscape");
} else
{
self.LabelEstoy.text = #"Portrait";
NSLog(#"Portrait");
}
}
and orientacionActual method redudant in your code, and doesn't makes any sense.
Don't forget to add [[NSNotificationCenter defaultCenter] removeObserver:self] inside dealloc.

Can't dismiss MPMoviePlayerViewController when Home Button or Power Button pressed

Basically, I have a MainViewController that presents a MPMoviePlayerViewControllerExtended and I want to dismiss MPMoviePlayerViewControllerExtended when user taps the Home Button or Power Button.
I tried this in MPMoviePlayerControllerExtended.m:
-(void)viewWillDisappear {
[self dismissMoviePlayerViewControllerAnimated];
}
-(void)viewDidDisappear {
[self dismissMoviePlayerViewControllerAnimated];
}
But it doesn't work, apparently they aren't called when Home or Power button is pressed.
Any help is appreciated.
try adding :(BOOL)animated
-(void)viewWillDisappear:(BOOL)animated {
[self dismissMoviePlayerViewControllerAnimated];
}
-(void)viewDidDisappear:(BOOL)animated {
[self dismissMoviePlayerViewControllerAnimated];
}
Also probably want to include the
[super viewWillDisappear:animated]
and
[super viewDidDisappear:animated]
in those somewhere too
-(void)viewWillDisappear:(BOOL)animated {
[self dismissMoviePlayerViewControllerAnimated];
[super viewWillDisappear:animated]
}
-(void)viewDidDisappear:(BOOL)animated {
[self dismissMoviePlayerViewControllerAnimated];
[super viewDidDisappear:animated]
}
Since both methods aren't called, I had to use Notifications.
In MPMoviePlayerViewControllerExtended.m
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(dismissModalViewControllerAnimated:)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
}

Present ViewController modally shows weird animation

I'm trying to create a simple modal view controller that lets you edit text using a text view. However, when I present the view controller modally, it slides in from the bottom left direction instead of just sliding in from the bottom.
Here's a video of the weird effect: http://youtu.be/9M_MHA5mt1M
My controller simply watches for the keyboard to show and then resizes the text view using auto layout appropriately. Here's the code:
#import "TextPicker.h"
#interface TextPicker ()
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *keyboardHeight;
#end
#implementation TextPicker
- (id)initWithText:(NSString *)text
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
self = [storyboard instantiateViewControllerWithIdentifier:#"textPicker"];
if (self) {
self.text = text;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self observeKeyboard];
//self.textView.text = self.text;
[self.textView becomeFirstResponder];
}
- (void) viewWillDisappear:(BOOL)animated {
[self.textView resignFirstResponder];
}
- (IBAction)savePressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)cancelPressed:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void) dealloc {
[self stopObervingKeyboard];
}
- (void)observeKeyboard {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow:) name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)stopObervingKeyboard {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSValue *kbFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardFrame = [kbFrame CGRectValue];
self.keyboardHeight.constant = -keyboardFrame.size.height;
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
- (void)keyboardWillHide:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
NSTimeInterval animationDuration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
self.keyboardHeight.constant = 0;
[UIView animateWithDuration:animationDuration animations:^{
[self.view layoutIfNeeded];
}];
}
- (IBAction)dismissKeyboard:(id)sender {
[self.textView resignFirstResponder];
}
#end
Your view is animating as you have asked it to by wrapping the [self.view layoutIfNeeded] call inside an animation block.
In viewDidLoad you begin observing keyboard changes, and when you detect them you animate the adjustments, this is normally correct. But then, before the view does its first layout, you show the keyboard; this results in an animation for all the views from CGRectZero to their proper sizes. And this is the effect you are seeing.
So basically you need to give the view a chance to layout before your animated layoutIfNeeded call. Probably the easiest way to do this is simply to move [self.textView becomeFirstResponder]; to either viewWillAppear: or viewDidAppear:.
*As a side note, remember to call super in appearance calls. I noticed you did not call [super viewWillDisappear];.

After calling Reachability startNotifier, the Network status changes to 0

in my iOS application, I need to use Reachability to complete the following tasks:
When a view controller is rendered, the application needs to check the connection status, and display the connection icon image accordingly (online or offline).
When the user stays on the view controller, and if the internet connection status changes, the application will be notified with the change, and then do some tasks accordingly.
I used the Reachability class which is produced by Tony Million in my application, but a strange thing happened:
After calling the startNotified method, the reachability status changes to 0(NotReachable).
The following is my code:
BTLandingViewController.h
#import <UIKit/UIKit.h>
#import "MBProgressHUD.h"
#import "Reachability.h"
#interface BTLandingViewController : UIViewController <UIAlertViewDelegate>
{
__weak IBOutlet UIImageView *internetIndicator;
MBProgressHUD *hud;
Reachability *reach;
UIAlertView *emptyRecordsAlert;
UIAlertView *syncReminderAlert;
}
#end
Part of the code in
BTLandingViewController.m
#import "BTLandingViewController.h"
#interface BTLandingViewController ()
#end
#implementation BTLandingViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Initialize reachability
reach = [Reachability reachabilityWithHostname:BTReachTestURL];
}
return self;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Register with reachability changed notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
NetworkStatus internetStatus = [reach currentReachabilityStatus]; // value = 1 or 2
***// PS: If there is connection, before [reach startNotifier], the internetStatus's value reflects the connection status (1 or 2)***
// Start notify reachability change
[reach startNotifier];
internetStatus = [reach currentReachabilityStatus]; // Value = 0;
***// PS: Even if there is connection, after [reach startNotifier], the internetStatus's value becomes 0***
}
Thank you for your help.
I have finally solved this problem by working around the startNotifier trap I mentioned above. Calling startNotifier does change the network status to 0, but I declare a variable to store the initial network status when the view controller is rendered. And then use the value of the variable in a if statement in the method which handles the action triggered by reachabilityChanged notification. The following is my code:
BTLandingViewController.h
#import <UIKit/UIKit.h>
#import "MBProgressHUD.h"
#import "Reachability.h"
#interface BTLandingViewController : UIViewController <UIAlertViewDelegate>
{
__weak IBOutlet UIImageView *internetIndicator;
MBProgressHUD *hud;
NSInteger initialNetworkStatus;
UIAlertView *emptyRecordsAlert;
UIAlertView *syncReminderAlert;
}
#end
BTLandingViewController.m
#import "BTLandingViewController.h"
#import "BTSyncEngine.h"
#import "BTInstructorLoginViewController.h"
#import "BTGlobalParams.h"
#interface BTLandingViewController ()
#end
#implementation BTLandingViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidUnload {
internetIndicator = nil;
[super viewDidUnload];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Register with sync complete notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(syncCompleted:) name:#"BTSyncEngineSyncCompleted" object:nil];
// Register with reachability changed notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
Reachability *reach = [Reachability reachabilityWithHostname:BTReachTestURL];
reach.reachableBlock = ^(Reachability * reachability){
dispatch_async(dispatch_get_main_queue(), ^{
initialNetworkStatus = 1;
[internetIndicator setImage:[UIImage imageNamed:#"online_indicator.png"]];
// Start syncing local records with the remote server
[[BTSyncEngine sharedEngine] startSync];
hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Sync in progress";
});
};
reach.unreachableBlock = ^(Reachability * reachability)
{
dispatch_async(dispatch_get_main_queue(), ^{
initialNetworkStatus = 0;
[internetIndicator setImage:[UIImage imageNamed:#"offline_indicator.png"]];
// If the initial sync has been executed
// then use the cached records on the device
if ([[BTSyncEngine sharedEngine] initialSyncComplete]) {
// Go to instructor login screen
BTInstructorLoginViewController *instructorLoginViewController = [[BTInstructorLoginViewController alloc] init];
[instructorLoginViewController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentViewController:instructorLoginViewController animated:YES completion:nil];
}
// If the initial sync has not been executed
// show an alert view
else {
emptyRecordsAlert = [[UIAlertView alloc] initWithTitle:#"Alert" message:#"Please connect to the internet to load data for the first time use." delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[emptyRecordsAlert show];
}
});
};
[reach startNotifier];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
// Unregister with reachability changed notification
[[NSNotificationCenter defaultCenter] removeObserver:self name:kReachabilityChangedNotification object:nil];
// Unregister with sync complete notification
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"BTSyncEngineSyncCompleted" object:nil];
}
- (void)syncCompleted:(NSNotification *)note
{
// Update the global sync completed flag
[[BTGlobalParams sharedParams] setSyncCompleted:YES];
// Hide syncing indicator
[MBProgressHUD hideHUDForView:self.view animated:YES];
// Go to instructor login screen
BTInstructorLoginViewController *instructorLoginViewController = [[BTInstructorLoginViewController alloc] init];
[instructorLoginViewController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentViewController:instructorLoginViewController animated:YES completion:nil];
}
- (void)reachabilityChanged:(NSNotification *)note
{
Reachability *reach = [note object];
if ([reach isReachable] && !initialNetworkStatus) {
[internetIndicator setImage:[UIImage imageNamed:#"online_indicator.png"]];
// Start syncing local records with the remote server
[[BTSyncEngine sharedEngine] startSync];
hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Sync in progress";
}
}
#end

Resources