UIDeviceOrientationDidChangeNotification only fires once - objective-c

I am trying to handle device orientation changes on a viewcontroller for one of my views.
Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"viewDidLoad");
// Tell the UIDevice to send notifications when the orientation changes
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
// tell the director that the orientation has changed
- (void) orientationChanged:(NSNotification *)notification
{
NSLog(#"orientationChanged");
}
When I first launch the App, the orientationChanged selector gets called, but then after that it does not get called again no matter how much I rotate the iPad. Does anyone have any idea what I might be doing wrong? When I put similar code in the app delegate, it works fine, but in this particular view controller, it is not behaiving properly.

I know this question is a bit old, but just thought I'd add a link to a tutorial I found when I was in the same situation you were (wanting to get notifications for device rotation, regardless of what the interface orientation was).
This tutorial gives a nice and simple overview of how to handle device rotation using UIDeviceOrientationDidChangeNotification.
I also had issues with UIDeviceOrientationDidChangeNotification not firing (it worked fine in the simulator, but not on my device). After a bit of debugging I realised that I had Portrait Orientation Locked on the device, and this was causing the UIDeviceOrientationDidChangeNotification not to fire. Not sure if that's your issue, but it's something that's easy to overlook and worth checking.

the problem is that the orientation will not change unless you specify that it can do so. when you rotate the device/simulator, the framework calls
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
where interface orientation is the orientation the system wants to use, however you have to return YES or it will not change.
if you are not returning YES then your orientation is not changing and therefore you cannot get notified for an orientation change (because it did not happen)

Related

Is There A Lost Focus Window Event in Objective C?

Coming from a .NET background I'm used to events getting fired so trapping a lost focus event is easy but I'm not sure how to do this in Obj-C. Basically I want my app to know when another application has gotten focus and it no longer has it so it can perform some actions.
Can you please tell me how I can implement this kind of functionality in Obj-C for an OSX app?
Have a look at the NSWindow notifications. Specifically, you're interested in NSWindowDidBecomeKeyNotification and NSWindowDidResignKeyNotification. You can also create a delegate for the window and implement its windowDidBecomeKey: and windowDidResignKey: methods, as noted in the NSWindowDelegate protocol documentation.
Or, if you just wanted to know when the application (not a window) has gained focus, you can subscribe to the NSApplicationDidBecomeActiveNotification. Likewise, NSApplicationDidResignActiveNotification will notify you when your app loses focus. These notifications are discussed more here. You can also implement applicationWillBecomeActive: and applicationWillResignActive: in the application delegate.
It's unclear if you want notification of a single window losing focus or notification of your entire app losing focus. My answer here provides notification for the entire application losing focus. (See mipadi's answer if you just want to know when one of your app's window loses focus.)
Observe the appropriate notification:
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(appDeactivated:)
name:NSWorkspaceDidDeactivateApplicationNotification
object:nil];
Then add the handler method:
-(void) appDeactivated:(NSNotification *)notification
{
NSRunningApplication* app = [notification.userInfo objectForKey:#"NSWorkspaceApplicationKey"];
if (app == [NSRunningApplication currentApplication]) {
// your cleanup code here
}
}

GCController framework (OSX) not generating notifications

I'm trying to integrate the GCController framework into my project. I'm not so familiar with Objective-C, so please excuse my ignorance around how this is supposed to work.
I have a class that extends NSOpenGLView, which registers observer methods for the various controller notifications, like so:
-(void)awakeFromNib
{
// Do some stuff
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(controllerStateChanged) name:GCControllerDidConnectNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(controllerStateChanged) name:GCControllerDidDisconnectNotification object:nil];
}
and I've defined a super simple handler as such:
- (void)controllerStateChanged {
NSLog (#"something happened, let's check it out\n");
}
Problem is, these events/notifications never seem to fire - and when I inspect [GCController controllers], it's entirely empty.
Of course, it stands to reason that if there are no controllers, there will be no events - so maybe I'm doing something wrong?
Or perhaps, for whatever reason - my controller simply fails to generate the required events (I'm using a PS4 controller which is registered with the OS, be it wirelessly or via USB, so I'm not sure what I'm missing here).
Is there some other place I need to enable these notifications? Do I need to somehow initialise the GCController framework?
So it turns out that the GCController framework only supports MFi (Made For iPod, Made For iPhone, Made For iPad) controller products.
For anyone looking to support non-MFi controllers, you'll need to interface with the HID via IOKit. Or use the rather excellent DDHIDLib instead

Get EAAccessoryDidConnectNotification when app is in background (iOS)

In my 'viewDidLoad'in ViewController.m, I am registering to the NSNotificationCenter defaultCenter with 'EAAccessoryDidConnectNotification' and 'EAAccessoryDidDisconnectNotification'
When my app is active in foreground, I get the notification, and respond in accessoryDidConnect. all works OK.
But, when app is in background, how can I get such notification?
('EAAccessoryDidConnectNotification' and 'EAAccessoryDidDisconnectNotification')
[Code below]
Thanks a lot.
Dan
(void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(accessoryDidConnect:)
name:EAAccessoryDidConnectNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(accessoryDidDisconnect:)
name:EAAccessoryDidDisconnectNotification
object:nil];
[[EAAccessoryManager sharedAccessoryManager] registerForLocalNotifications];
}
According to the documentation, it's not possible:
If your app is suspended in the background when an accessory
notification arrives, that notification is put in a queue. When your
app begins running again (either in the foreground or background),
notifications in the queue are delivered to your app. Notifications
are also coalesced and filtered wherever possible to eliminate any
irrelevant events. For example, if an accessory was connected and
subsequently disconnected while your app was suspended, your app would
ultimately not receive any indication that such events took place.
But it would be interesting if someone proved me wrong.
in iOS 12.1.4, plug in the relay cable with iPhone and DSLR Camera when app is in foreground, then switch app into background mode, unplug the relay cable and switch app back into foreground, you will receive the accessoryDidDisconnect notify via EAAccessory's delegate methods.

UIViewController visible callback

I am developing an iOS application where need to do some stuff when I have Internet connection and other, when I haven't. If I haven't at some point I will show a message to the user to give me internet and come back. The question it is how to detect the following situation:
the user press the Home button twice, goes to multitasking , Settings and will connect to internet
the user comes back with multitasking to my app, but doesn't press anything
I know I will get callbacks to the AppDelegate:
- (void)applicationDidEnterBackground:(UIApplication *)application
- (void) applicationDidBecomeActive:(UIApplication *)application
but the code ( it is not started by me) it is very big, and I don't want to handle there the UIViewController needs, if there is any alternative.
My UIViewController's - (void)viewDidAppear:(BOOL)animated it isn't called when the user came back.
The breakpoint it is not hited for sure!
Any usable ideas, except in AppDelegate?
You can use the notification center to listen to applicationDidEnterBackground within the view controller:
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(handleEnteredBackground:)
name: UIApplicationDidEnterBackgroundNotification
object: nil];
Do this in viewDidLoad. Similarily for applicationDidBecomeActive.
Don't forget to remove yourself as an observer in viewDidUnload.
The application delegate is the correct place to be handling application state changes, but just because that is the case, it doesn't mean you must put all the logic that is triggered by the application state change in there.
Put the logic where it belongs. If it's networking code, that's not in the application delegate and it's not in the view controller, it's in a separate class. Then look into ways of tying the different parts of your application together. In most cases, notifications, KVO and the shared instance pattern are good approaches to take.

Can I disable UIPickerView scroll sound?

I want to disable the annoying clicks that the UIPickerView generates upon scrolling up and down. Is there a way to do this? I want to play short sounds for each item that the picker view lands upon. It gets ruined by the built in sound.
I understand that the picker sounds can be turned off globally by switching off the keyboard sounds in iPhone/iPod settings. But is there a way to programatically do this?
Any help will be much appreciated!
Thanks
I've been struggling with a UIPickerView sound issue, and even though it's only partially relevant to the original question, I'm posting the problem/solution here because this topic keeps coming up in my search results so I think anyone else in the same boat may end up here too…
I needed to initialize a UIPickerView to restore the currently selected row from saved data. Simple, right? In viewDidLoad, just call the selectRow:inComponent:animated method of UIPickerView:
[myPicker selectRow:currentRowIndex inComponent:0 animated:NO];
This works as expected, but has a side effect that it generates a single "click" sound as if the user had scrolled the control. The click sound only occurs when running on a device (not the simulator), and only if the device has iOS 3.x installed (I tested with 3.1.3 and 3.2). This was apparently a bug in iOS that was fixed starting with iOS 4.0. But if you need to target Gen1 iPhone, you're stuck with iOS 3.1.3 where this problem is present.
I discussed the issue with Apple DTS, but they were unable to suggest any workaround other than upgrading to 4.0. I asked if they would make an exception and permit the use of the undocumented setSoundsEnabled mentioned above (which does actually solve the problem). The answer was, "There are no exceptions."
After some additional detective work, I discovered that you can prevent the sound from occurring by temporarily removing the UIPickerView from the superview, call selectRow, then re-add it to the superview. For example, in viewDidLoad:
UIView *superview = [myPicker superview];
[myPicker removeFromSuperview];
[myPicker reloadAllComponents];
[myPicker selectRow:currentRowIndex inComponent:0 animated:NO];
[superview addSubview:myPicker];
This gets rid of the extraneous click sound without using undocumented/private APIs so should pass Apple's approval process.
After using this specific undocumented api for over a year on the App Store Apple finally asked me to remove it from my App. It is very frustrating for audio apps to have that damn click sound. The best advice is to share with users that the picker sound can be disabled globally in the settings application under "Sounds" and setting "Keyboard Clicks" to "Off". I also strongly recommend visiting https://bugreport.apple.com/ and filing a bug for UIPickerView, as it can cause distortion in audio applications when the picker click is played.
they have just rejected an app of mine because the use of undocumented api's...thats one of them.
Someone I know says he got this past the App Store review just last week:
// Hide private API call from Apple static analyzer
SEL sse = NSSelectorFromString([NSString stringWithFormat:#"%#%#%#", #"set",#"Sounds",#"Enabled:"]);
if ([UIPickerView instancesRespondToSelector:sse]) {
IMP sseimp = [UIPickerView instanceMethodForSelector:sse];
sseimp(self.thePicker, sse, NO);
}
There is an undocumented way (I'm actually not sure if it is still available in iphone 3.0) but here it is any way
#import <UIKit/UIKit.h>
#interface SilintUIPickerView: UIPickerView
{ }
- (void) setSoundsEnabled: (BOOL) enabled;
#end
use this subclass instead and call [view setSoundsEnabled: NO]
I'm interested in knowing how it goes in the latest SDK, give it a shot and let us know.
Could this trick work? Someone was able to suppress the camera shutter sound effect by playing an inverted copy of the sound at the same moment: https://stackoverflow.com/a/23758876/214070
Maybe this not the answer for this particular question, but I had a similar problem - set minimumDate for datePicker, and I wanted set it without annoying "click" sound. After some time found very simple solution:
datePickerCustomTime.minimumDate = [[NSDate date] dateByAddingTimeInterval:300]// min time to set = now + 5 min
[datePickerCustomTime setDate:[[NSDate date] dateByAddingTimeInterval:300] animated:NO];
I found small quickie solution for this try below
UIPickerView *pickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, yPickerView, VIEW_WIDTH, PICKERVIEW_HEIGHT)];
pickerView.delegate = self;
pickerView.dataSource = self;
pickerView.showsSelectionIndicator = YES;
pickerView.alpha = 0.8f;
pickerView.tag = fieldTag;
[pickerView selectRow:pickerViewSelectedIndex inComponent:0 animated:NO];
set the animated:NO for selectRow: method