I have a UIViewcontroller that hosts a lot of Viewcontrollers. I set up autorotating on it and its subview and it works fine once the app has loaded. The problem I have is that during the initial loading process, the app has to connect and download some files locally. During this load if the user rotates the iPad the UIViewController doesn't rotate properly! I did set the flags and shouldautorotate but still not working.
PS : I am allowing these four orientations :
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait ||
interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown ||
interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
After the load is complete the behavior is correct and rotates properly. Now I thought this could be because some of those inner VC's are loading some data, let me explain:
The big View Controller hosts all other VC's ( Let's Call him BigDaddy), in the Viewdidload of bigdaddy I have (for argument sake ) three other ViewControllers : Busta, Skeezix, and lawrence. So I do:
[[Busta alloc] initWithNibName:#"Busta" bundle:nil];
and this in turn calls the -viewDidLoad of Busta, so I made the file loading section of that asynchronous by doing:
dispatch_async(dispatch_get_main_queue(), ^{
});
Those changes did help a little bit, in the sense that before the async dispatch the load process would take 5 seconds (and in those seconds any tilt in the iPad would cause the rotation event and my whole view is gone. Now I got those 5 seconds down to 1.2 seconds.
My question is, is there a solution to my problem? Will it never work? Is it possible to have an app rotate in four directions and respond to rotation changes while loading data?
I suspect the issue is with how you're doing your 'connect'. I assume you mean you're connecting to a remote server using NSURLConnection or similar. Be aware that if you use a synchronous connection on the main thread, you will freeze the app until the connection is finished. The appropriate method is to utilize the asynchronous calls, or better yet move the work to a secondary thread (see NSOperationQueue documentation) and then move any updating of the UI based on that information back to the main thread (see again the NSOperationQueue documentation).
There is a good example for this in the WWDC 2012 videos.
Why not just implement your -shouldAutorotateToInterfaceOrientation... method such that it allows rotation only after the app has finished loading the required data?
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
BOOL appIsFinishedLoadingData = <your code here>
return shouldRotate;
}
(I shortened your code in consideration of the fact that you're allowing all possible rotations, so there's really no need to check which rotation is being offered. Your way is more future-proof, mine is a little easier to read.)
Related
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.
My app supports background location updates (specifically with significant location monitoring).
Do I need to prevent UI (via controllers etc.) from loading when I identify that the app is in background (application.applicationState == UIApplicationStateBackground)?
My intention is to avoid heavy UI loading (it's a BIG app) in background, that might waste all the limited time I have in background to actually respond to the location update.
For example (in ObjC, but the question is also for Swift), let's say I have some RootViewController which initializes and holds the whole controllers/view hierarchy, should I, in the viewDidLoad do:
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
// Root view controller loaded while in background, so doing only background stuff
[self justDoBackgroundStuffWithoutUI];
} else {
// Root view controller loaded normally, so loading normal UI
[self initializeUIAndChildControllers];
}
? Or should I just "trust" iOS to ignore all of those UI tasks, because it will know it's in a background state?
You can ignore those and let the OS handle that, just be careful if you have long running BG tasks, they may or may not have time to complete so it is best to very careful as it won't allow you to run tasks forever.
Updating UI in background is unnecessary and confusing and non-efficient.
unnecessary because there is no added value to the user who's not using the app. A simple doing of things in viewWillAppear would suffice.
confusing because users maybe expecting to see changes themselves as the screen is appeared, I don't know of your app, but this is more of a business choice. Maybe you have something similar in the manner of displaying changes like gmail/whatsapp and you want users to see the new emails/messages.
non-efficient because you are just doing something battery consuming too early. You even said "heavy UI loading". What happens if the location changes again and again and again? could the changes be overridden in the sense that they are no longer necessary or you will always need the changes regardless.
To summarize: I'm not saying don't do any UI updates, every app has a sweet spot. Likely you won't need to do most of them and lazy loading the changes ie upon loading screen seems to be the better way. Though I'm sure there are some advanced guidelines which I'm unaware of. Hopefully other answers would will come.
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 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.)
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];
...
}