I have a camera app that is running fine on iOS 7. In the viewDidAppear call of my MainViewControllerI am first checking if the application state in not inactive and the application is not in background.
The code sample is given below.
-(void) viewDidAppear
{
if ((UIApplicationStateBackground != [UIApplication sharedApplication].applicationState)
&& (UIApplicationStateInactive != [UIApplication sharedApplication].applicationState))
{
// check if the camera is running
// perform the animation of opening shutter.
}
}
My problem is that on iOS 8 beta 2 [UIApplication sharedApplication].applicationState returns UIApplicationStateInactive hence the check fails. But on iOS 7 [UIApplication sharedApplication].applicationState returns UIApplicationStateActiveand works without any problem.
Has anyone else faced the same issue?
EDIT
A simple experiment of putting breakpoints in viewDidAppear and appDidBecomeActive in xcode 6 reveals that viewdidAppear gets called first. I suppose its a bug in iOS 8
Your view could not possibly appear unless the app was active, or at least becoming active. Views don't do things like appear when the app is inactive or in the background. So I would just delete that condition entirely if I were you. It was never serving any useful function.
(By the way, if you're encountering this situation on launch, what you're experiencing sounds like an issue that I've reported to Apple in another form: in iOS 8, the application doesn't switch to active (so that application:didBecomeActive: fires) until very late, well after the whole interface is up and running. This has caused me to have to rewrite quite a lot of my code. For example, if use my root view controller's viewDidAppear: to register for the applicationDidBecomeActive notification, I then receive that notification shortly afterwards — which is nutty.)
Related
I encountered this situation where I had added app state restoration APIs for the first time to a new app supporting iOS 9.3 and higher. The state restoration is working fine functionally, but I noticed that it was showing a screen snapshot instead of the LaunchScreen.xib content because of the delay during state restoration. By snapshot I'm referring to the automatic screenshot iOS takes of your apps UI as it goes into the background.
If you don't know what app state restoration is, it came out with iOS 6, here's the link from Apple on it:
Apple Documentation on App State Saving & Restoring
Showing the screen snapshot in this apps case is a significant problem as this particular app has about 4 seconds of delay, on an iPhone 4s running iOS 9.3.5, to do during app state restoration. Users would perceive the app to be hung since the launch / splash screen was not being shown during this time. This situation reproduces on all iOS versions currently available, on both simulator and device.
How can I prevent the snapshot from being shown, and force the LaunchScreen.xib to be used always, all while still preserving the app state save/restore functionality benefits?
After researching this I found that Apple has long ago provided a method off of UIApplication to deal with this situation. But its usage, even today, is poorly documented.
The solution is to use the ignoreSnapshotOnNextAppliationLaunch method from UIApplication.
Apple ignoreSnapshotOnNextApplicationLaunch method
You will have to access it via the UIApplication singleton pattern as suggested by Apple here as I will explain:
Apple UIApplication sharedApplication method
The where to use this is what is not clearly documented and that I am sharing here. The ignoreSnapshotOnNextApplicationLaunch method will have absolutely no effect unless specifically called when iOS is saving the app state from the view controller(s). Such as when you tap the home button to background the app.
You cannot call this method directly from the AppDelegate methods dealing with background / foreground transitions, as it needs to be called from the view controllers while their states are being saved for later restoration.
For this saving task Apple provides the encodeRestorableStateWithCoder method from UIViewController
Apple encodeRestorableStateWithCoder method
And this is where we need to make the change. If doing state restoration you should already have it; but by adding this method call to each view controller class where you have setup restoration IDs in storyboard, or are saving state manually, you can avoid any snapshots being used by including the ignoreSnapshotOnNextApplicationLaunch from the UIApplication singleton. This will not prevent iOS from taking the snapshot, just not showing it during app state restoration on re-launch.
// save any app state information that is not already saved automatically
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder {
// prevent taking a screen shapshot and force launchScreen xib to be used always
[[UIApplication sharedApplication] ignoreSnapshotOnNextApplicationLaunch];
[super encodeRestorableStateWithCoder:coder];
return;
}
Be sure you re-background the app after adding this during your testing, to have iOS delete the previously saved snapshot file.
When I launch my app on iOS 10, I can see that after a short delay, the Appdelegate function -> applicationWillResignActive() is called.
There is no reason for that. The app is still active and in foreground state when it occurs and the app continues to run normally.
Please see above the lifecycle of my app :
--> Click on the app icon
App launch
application --> didFinishLaunchingWithOptions
application --> applicationDidBecomeActive
RootViewController --> viewDidAppear
application --> applicationWillResignActive <-- issue !
application --> applicationDidBecomeActive <-- again ??!!
at this point, the app is still running with no error
This sequence is repeated each time I open the app.
It looks as if something forces my app to quit the foreground state for an ultra short delay.
Usually, applicationDidBecomeActive is called when the app displays an alert ( for example if the app requires an user's permission to access the camera ) or when the user clicks on the home button.
1 - It only occurs when the app starts in landscape mode
2 - It only occurs on iPhones and not on iPads
3 - The problem does NOT occur on an iOS 9 device
Did anyone noticed this problem ?
The problem is, it calls second time after dismissing system services alert (location, push notifications, photos)
So the only way to handle it is to use variable in AppDelegate which increments each time some system alert shows and decrements in applicationDidBecomeActive, so you call your code only if value of this variable is 1.
Another interesting thing is that applicationDidEnterBackground doesn't call when system alert shows, thus we can use this info to decide whether we should call our code in applicationDidBecomeActive or not (but still, it can be less reliable solution)
I have a GLKViewController subclass implementation, with an ADBannerView as a subview. Ads load and render just fine. The OpenGL application also works fine. When you click on an Ad, the popup appears. The GLKViewController has the default pause-on-resign behavior enabled.
If you set a breakpoint on GLKViewController's setPaused, you will notice that setPaused:NO gets called if you switch away from your app (double-tap home and pick another app) while the ad popup is visible. The behavior only occurs when an ADBannerView popup is visible, not if you switch away from the app otherwise.
I am able to reproduce the above behavior with even the default OpenGL template app generated by Xcode 7 in iOS 9.1.
1) Is this normal?
2) For me this causes consistent reproducible crashes because after setPaused:NO is called, the GLKViewController is redrawn, which causes OpenGL operations to occur while the app is in the background. The app is (correctly) killed at this point.
My current workaround is to discard calls to setPaused:NO as follows when the application is not active. This appears to work fine but why this is happening at all is confusing me.
- (void)setPaused:(BOOL)paused
{
long appState = (long)[UIApplication sharedApplication].applicationState;
if (!paused && (appState != 0)) {
NSLog(#"setPaused - REJECTING Unpause; %ld", appState);
return;
}
[super setPaused:paused];
}
It appears that the method viewWillTransitionToSize:withTransitionCoordinator: gets called multiple times when the app goes into the background or inactive since iOS 9.
For example, if the app is in the portrait on iPad, pressing the home button will cause the app to receive method call first with size of 1024x768 (landscape) and then with 768x1024 (back to portrait). That leads me to conclude that iOS does this in order to get screenshots for app switcher.
Our application logic depends on the screen size and changes to the screen size trigger tasks that update our model with regard to the new size. We need to do this when user rotates the device or goes into multitasking mode (split view), but we must not do it when user goes into the background.
One idea was to use UIApplicationWillResignActiveNotification notification, but that turned out to be mission impossible because sometimes viewWillTransitionToSize:withTransitionCoordinator: gets called before the notification is sent and othertimes it gets called after the notification is sent :/
Any ideas?
In viewWillTransitionToSize:withTransitionCoordinator you can use the UIViewControllerTransitionCoordinator to tell when the transition is complete. Then, check that your app is not backgrounded and perform your logic.
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
// after transition
if ([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) {
// perform logic
}
}];
}
I currently have the same problem. I do a lot of work when the size changes and can't have it happening when going into the background. One thing that is working for me "most" of time is to check if the app is in the background using [uiapplication sharedApplication] applicationState]. The application state is set to UIApplicationStateBackground before the 2 extra calls to viewWillTransitionToSize are made. This seems to be consistent. I'm having some problems when the app is swiped in using multitasking then goes into the background and then foreground again at full size. The order of calls doesn't seem to be consistent in this case to determine which size changes should be used.
Yup, same problem. You could, instead of using 0.1 seconds, use the coordinator's transitionDuration(). Then, check for background status.
I have an app that needs to do something when it’s sent to background using the Home button and something else when the device is locked using the top hardware button. The standard way of solving these requirements are the notifications and delegate methods sent out by UIApplication. On iOS 4 they look like this:
// Pressing the home button
Will resign active.
Did enter background.
// Tapping app icon on Springboard
Will enter foreground.
Did become active.
// Pressing the lock button
Will resign active.
// Unlocking the device
Did become active.
In other words, it’s quite easy to tell between locking and backgrounding. On iOS 5 the behaviour changed:
// Pressing the home button
Will resign active.
Did enter background.
// Tapping app icon on Springboard
Will enter foreground.
Did become active.
// Pressing the lock button
Will resign active.
Did enter background.
// Unlocking the device
Will enter foreground.
Did become active.
Notice that the didEnterBackground and willEnterForeground notifications are now sent out even when (un)locking the device, making it impossible to tell between locking and backgrounding. Is this change documented somewhere? Is it a regression? Do you know another way to distinguish the two cases?
iOS 6
In my preliminary testing via the simulator, checking the application state with
[[UIApplication sharedApplication] applicationState]
in either
- (void)applicationWillEnterForeground:(UIApplication *)application
- (void)applicationDidEnterBackground:(UIApplication *)application
allows you to differentiate between a call to lock the device and just switching back to the homescreen. A lock screen will return 1 (UIApplicationStateInactive), whereas a home button press will register as a 2 (UIApplicationStateBackground).
It seems consistent and should work on an iOS device just as reliably as it does in the simulator.
iOS 7
The iOS 6 method no longer works in iOS 7. In order to do this now, you have to utilize CFNotificationCenter and listen for a darwin notification (labeled: com.apple.springboard.lockcomplete). You can find the github repo with the sample project here: https://github.com/binarydev/ios-home-vs-lock-button
Credit for the iOS 7 fix goes out to wqq
I have looked into this quite a bit so I would love to be wrong here if someone knows something I don't, but technically, there is no documented way to tell the difference between locking the device, and sending to background.
One thing you can check however, is the UIApplicationState during the transition from foreground to background. Locking a device will give UIApplicationStateInactive and moving the App to the background will give UIApplicationStateBackground. But, since this behaviour is not officially documented it may change in the future.
A basic example:
- (void)applicationDidEnterBackground:(UIApplication *)application {
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
NSLog(#"Device state: %#", state);
switch (state) {
case UIApplicationStateActive:
/* ... */
break;
case UIApplicationStateInactive:
/* Device was/is locked */
break;
case UIApplicationStateBackground:
/* User pressed home button or opened another App (from an alert/email/etc) */
break;
}
}
UIApplicationState - The running states of an application
typedef enum {
UIApplicationStateActive,
UIApplicationStateInactive,
UIApplicationStateBackground
}
UIApplicationState
Constants
UIApplicationStateActive - The application
is running in the foreground and currently receiving events. Available
in iOS 4.0 and later.
UIApplicationStateInactive - The application is running in the
foreground but is not receiving events. This might happen as a result
of an interruption or because the application is transitioning to or
from the background.
UIApplicationStateBackground - The application is
running in the background.
According to the UIApplicationDelegate Protocol Reference:
applicationWillResignActive:
didEnterBackground:
// ...
willEnterForeground:
applicationDidBecomeActive:
are the only methods that ever get called in both situations.
According to the iOS 4.3 to iOS 5.0 API Diff, these are the ONLY changes regarding UIApplication or UIApplicationDelegate, so I couldn't find where they documented any of these notification changes:
UIApplication.h
Added -[UIApplication setNewsstandIconImage:]
Added UIApplication.userInterfaceLayoutDirection
Added UIApplicationDelegate.window
Added UIApplication(UINewsstand)
Added UIApplicationLaunchOptionsNewsstandDownloadsKey
Added UIRemoteNotificationTypeNewsstandContentAvailability
Added UIUserInterfaceLayoutDirection
Added UIUserInterfaceLayoutDirectionLeftToRight
Added UIUserInterfaceLayoutDirectionRightToLeft
This is more of a workaround/hack, but according to my experience it's very reliable.
When the device is screen-locked (not just home button-ed, if that's a word :)) - bound network (UDP) sockets are broken.
I was using GCDAsyncUDPSocket (also AsyncUDPSocket before) and they both fire a network/broken pipe error reliably when the device is turned off.
In my case I need the UDP socket anyway, for other apps it might be a bit smelly, however, just binding/listening on a UDP socket without any action is not too terrible if you really need to differentiate here.
This note will [self destruct]; is 5 minutes (so Apple won't find out).
There’s a thread about this issue on Apple Developer Forums (registered developers only, sorry). The gist is that the new behaviour is by design. There are requests for a new API feature to distinguish between the two use cases, but nothing working yet.
Here is what Apple's iOS Programming Guide says:
Pressing the Sleep/Wake button is another type of interruption that
causes your app to be deactivated temporarily. When the user presses
this button, the system disables touch events, moves the app to the
background but sets the value of the app’s applicationState property
to UIApplicationStateInactive (as opposed to
UIApplicationStateBackground), and finally locks the screen.
http://developer.apple.com/library/ios/#DOCUMENTATION/iPhone/conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
So, you should check the UIApplication's applicationState property in applicationDidEnterBackground:. If it is UIApplicationStateBackground the user pressed the home button. But if it is UIApplicationStateInactive the user locked the device.