My app is crashing when you return to it after switching away from it using the 4-finger task-switch gesture on an iPad.
When the user does the 4-finger gesture to switch away from my app, I see applicationWillResignActive: then applicationDidEnterBackground: in my app delegate, then (assuming we're currently in portrait orientation) my top-level view controller gets viewWillTransitionToSize:withTransitionCoordinator: with a size that indicates landscape layout (even though the device has not rotated). This of course results in a lot of resizing and rearranging of views for the new orientation. Immediately after that I get viewWillTransitionToSize:withTransitionCoordinator: with a size that indicates portrait (the original orientation of the device). And again, I go through all my layout again for the new (actually, original) orientation.
What I'm finding is that if I wait for all of this to complete, I can switch in and out of my app all day. If, on the other hand, I switch back to my app while this needless work is going on, I get a crash deep in some iOS transition code.
At this point I'm trying to understand why I get these stray rotation events. If I can avoid getting those, I can avoid getting into whatever situation is causing the crash. They don't seem necessary.
I can't think of a reason why it would be useful or correct to get viewWillTransitionToSize in the background, so just bounce out if you are in the background:
if UIApplication.shared.applicationState == .background {
return
}
Still, this feels like a bug, and in my opinion you should report it to Apple.
It turns out there isn't a way to prevent the rotation events from being reported to the app. I suspect that's an iOS bug. However, ignoring viewWillTransitionToSize:withTransitionCoordinator: when the application state is UIApplicationStateBackground and doing the same in the view's layoutSubviews (if present) allowed me to work around the problem.
Related
I noticed some bizarre behavior in my iPad app: when the app transitions to the background, my active view controller receives viewWillAppear/viewDidAppear messages.
From my investigation, it seems this is happening because when my app moves to the background, my UISplitViewController for some reason first transitions its display mode to .PrimaryHidden and then immediately back to what it was before (.AllVisible).
Is this expected behavior? If so, why does UISplitViewController need to change its display mode (twice) when the app suspends, and is there a way to prevent it from doing so?
Edit: I just verified this behavior with a simple test app, but I'm no closer to understanding why it happens or how to prevent it.
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.
In our app we show the Camera modally on top of another UIViewController. On iOS 8.x only, about 1/10 of the time if you zoom you wind up with a crash:
PLImagePickerCameraView didHideZoomSlider:]: message sent to deallocated instance
There is an existing SO post which has a supposed workaround - How-to find out what causes a didHideZoomSlider error on IOS 8? - but every variation I have tried fails to solve the crash. The suggestion involves putting a delay before dismissViewControllerAnimated:completion: . No matter what delay I try I can still reproduce the crash.
It only occurs (1) if you zoom the camera view (2) either choose Cancel or take a photo and exit the camera, all shortly before the zoom indication animation fades away. It appears to be a problem in iOS 8.x which Apple hasn't fixed. It crashes in Apple's code with no involvement in anything we have.
I do see "Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates." but this seems to be unrelated and is also referring to code inside the UIImagePickerController not anything we are doing.
I am about to deal with Apple DTS to see if we can find some way to avoid this or what in the environment might be causing this to happen. I thought to ask here in case anyone has another idea.
This crash did not occur under iOS 7.X. Happens on any model iPhone or iPad.
The answer from Apple is "it's a bug, file it in Radar" which of course says nothing about when it will be fixed. There is no workaround other than to tell users to wait a little until the slider fades (which our support people tell the users). There is nothing you can do other than implement your own camera and zoom support and do it yourself correctly.
Perhaps Apple will fix it in 8.2.
Doing some investigation online, I've noticed that gpus_ReturnNotPermittedKillClient gets thrown when OpenGL actions are triggered while an app is backgrounded. I'm currently working on a project that involves a map view and an overlay (using Apple's Breadcrumb sample code). These actions are performed on the main thread, but there's a possibility that the app could get backgrounded as the map view becomes initialized or the view gets pushed onto the screen.
I also use location services to retrieve points through GPS, but I don't update the overlay unless we're in the foreground.
Is it to my understanding that since iOS 6, MKMapView's are now created with OpenGL? If this is the case, then could drawing the overlay also be through OpenGL? This could help explain why I've been getting this error randomly.
I've heard of some ways to cancel all OpenGL actions, such as invoking glFinish() in applicationDidEnterBackground and applicationWillResignActive or using [[CCDirector sharedDirector] pause]. Considering this, what would be the best solution to eliminate any OpenGL drawing with an MKMapView/MKOverlayView?
After trying multiple solutions, I realized that the thing that was causing this crash was an adjustment of the map view's frame that was being fired off a few seconds after the map was allocated. A status bar is dropped down, and the map's frame was animated downward by a few pixels. Because of this, the map (supposedly) had to be re-drawn, causing a crash if this animation was occurring in the background.
I now keep track of the state of the status bar in relation to the app's active status, and only animate if the application state is UIApplicationStateActive. Having done this, I haven't had a crash since.
When a foreground app gets backgrounded (e.g. Home button gets pressed), how can I change elements on the topmost view controller prior to when iOS takes a snapshot of it and starts the animation to show the next screen?
I ask because I'm writing an app requiring HIPAA compliance, and I am concerned that the snapshot that the OS takes in order to do this animation sometimes contains sensitive data which should not be visible even for a split second when the app gets foregrounded later.
I'm aware that view controllers have lifecycle methods such as viewWillDisappear which might be usable, but I have a lot of controllers and I'd rather just have something in my App Delegate to handle this (e.g. by adding an opaque full-screen UIImageView overlay) rather than having to write custom code for this in every last controller.
I tried putting overlay-generating code in applicationWillResignActive, and I've been digging with Apple's docs and Google, but it's not working. I suspect the screenshot gets taken before the app has a chance to update the screen.
Thanks!
Not sure about HIPAA's requirements about backgrounding and possibly leaving the user logged in for someone else to resume, but the safest sounds like it would be to add a key UIApplicationExitsOnSuspend with a boolean value of YES to info.plist.
That will prevent the app from backgrounding entirely, and restarts it (possibly triggering a login procedure) every time you go back to it.
Most (if not all) mobile banking applications I've tested do this for safety reasons.
I believe the answer is to not concern oneself with changing what's on the screen before the backgrounding animation begins, but to simply modify what's displayed on the screen once the app enters the background (i.e. inside of applicationDidEnterBackground: in your App Delegate.) This solved my problem.
My UIImageView overlay idea worked here, although I decided just to pop to the root view controller instead. Simpler that way. My root view doesn't have any sensitive info.
Here's what it looks like:
-(void)applicationDidEnterBackground:(UIApplication *)application {
UINavigationController *navigationController =
(UINavigationController *)self.window.rootViewController;
[navigationController popToRootViewControllerAnimated:NO];
...
}