Reloading view when returning from background - iphone

Using iPhone SDK 4.1. Sometimes when returning from the background state on iPhone 3GS device the view returned to has lost one of its images or labels. The viewDidAppear method
doesn't get called either when returning from the background. Is there any way to force
reloading the view so these methods are called?

This usually happens if your app receives a memory warning as a result of trying to store too much data in ram (in this case images).
To test whether this is the case you could either do an NSLog call inside the didReceiveMemoryWarning message or you could subclass UIImage and extend its dealloc and put an NSLog message inside it saying something like "Image being dealloced" and then check what gets written to the console. If you want to check it without being in the debugger (and therefore no console) you could create a debug UILabel inside the mainwindow xib at the very front (so its always visible) whose text value is set instead of writing to NSLog. This way youll be able to see what happened even after you return to your program.
Your best bet is the didReceiveMemoryWarning together with a UILabel object.
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application
{
debugLabel.text=#"Did receive memory warning";
}
To solve the problem (i.e. to reload the images) you could register that view to receive
the UIApplicationWillEnterForegroundNotification from the notification center and then call the necessary reloading calls i.e. check which images are nil (have been released) and should be reloaded.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(viewNeedsReload) name:UIApplicationWillEnterForegroundNotification object:nil];
- (void) viewNeedsReload
{
//Check validity of each image here and reload if necessary
}

Related

App resumed from Background, nil property of NSManagedObject

Conditions
Getting a notification (txt message)
or Opening/Closing Notification Center
or Switch to another app, come back
Basically, the app comes back from background for a short period.
Issue
Layout is ok, but some of my properties that I get from CoreData are empty after resuming from Background. This issue is there on pretty much all of my view controllers.
The project
This is an app that has a main tabbar controller, with two navigationcontrollers within the tabs and maybe two levels of viewcontrollers, that have themselves child UIViews (that use some of the informations). The back-end is made of Parse and CoreData.
The weird part
Back from background -> properties are ok on viewWillAppear (Create a backup of the id) -> they are nil seconds after -> I need to manually get them back (from the ID I just stored)
Here is a screenshot, when putting a break point within a function called every 5sec to check the current time (link to bigger) :
What I did
NSCoder implemented for state restoration and every view controllers have a restoration ID, however it doesn't get called when app is becoming active. I don't think NSCoder is the issue here since from the documentation it is used when the OS will kill it on its own, or a force quit from the user.
I tried to manually refresh the content in the appropriate ViewControllers from NSNotificationCenter if the NSManagedObjects are nil, but it is not future-proof, and is never the same logic on each view.
Any thoughts? Thank you!
My AppDelegate :
- (void)applicationWillResignActive:(UIApplication *)application
{
NSLog(#"Will Resign Active");
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSLog(#"Entered Background");
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
NSLog(#"Will enter Foreground");
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[NSNotificationCenter defaultCenter] postNotificationName:#"needsRefresh" object:self userInfo:nil]; // Helpful in some viewcontrollers to save variables that are not nil.
[PF_FBSession.activeSession handleDidBecomeActive];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
NSLog(#"Will Terminate");
}
For those finding this thread, here is the solution I found.
My issue and answer was specific to my project.
I had another controller, the one that takes care of all my database connections, that was listening to applicationDidBecomeActive.
This was calling up a refresh of my data, but also a "cleanup", that was deleting/editing some NSManagedObjects, then saving.
Conclusion : the memory address wasn't the same, the object wasn't considered the same, and therefore was empty on my current page.
I fixed it by stopping cleaning up on each AppDidBecomeActive, but move that logic to AppDidFinishLaunching instead.
Hope that helped!

How to deal with ViewController being released, after firing off background thread which referes to now released ViewController?

I have a ViewController which is pushed onto the NavigationController stack. As soon as it's pushed onto the stack it starts to download some images, by means of a downloader object, which is responsible for downlading the images in a background thread. The images can take several seconds, even over WiFi to download. When an image has finished being downloaded the downloader object instructs the ViewController to layout its images, putting the newly downloaded image(s) to the back of a paged UIScrollView. However during this time the user could have pressed the Back button and the ViewController could now have been released / deallocated and so the downloader object will cause a SIGABRT error message and the app will crash.
How should I deal with this situation? Is there some way to check for released / deallocated instances? Or some way to catch the error and log, then ignore, it?
Your best bet is to use a zeroing weak reference. Of course, with iOS 5.0, this is simply a "weak" reference. But, if you are targeting below iOS 5.0 though, then you need a custom solution for it. There is a nice one described by Mike Ash in this article.
Maybe you could avoid the problem by using notifications? Instead of your downloader object referencing the viewController, it posts a notification, which the viewController (if it's loaded) responds to.
in your data loading object:
[[NSNotificationCenter defaultCenter] postNotificationName:#"allDataLoaded" object:nil];
in your view controller viewDidLoad:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(refreshMyLayout) name:#"allDataLoaded" object:nil];
in your view controller dealloc:
[[NSNotificationCenter defaultCenter] removeObserver:self];
You could even put a reverse notification from your viewController in it's viewDidUnload to tell the data downloader to cancel the downloads (if you want).
The best would be to cancel the download once the result is no longer needed. This prevents unnecessary data traffic and memory consumption.
If you can't do that, the downloader object should have some sort of delegate (your view controller probably) that you can set to nil when you're no longer interested in the results (e.g. in your view controller's dealloc method). Zeroing weak references are also an option, if you target iOS 5, but again, it would be much better to cancel the downloading NSURLConnection.

Xcode4, iOS: Certain parts of instance method being ignored, no errors, just passed by

new iOS guy here. I have a problem that Googling and searching on here has not shed any light on. I'm assuming this is basic. I have a simple app (app delegate and 1 view controller), and as part of it I'm using local notification. So, in the app delegate I use the 'didReceiveLocalNotification' to watch for the notifications. Depending on which one comes in, I then call one of several instance methods in my main view controller.
ie in the AppDelegate.m
- (void)application:(UIApplication *)application didReceiveLocalNotification: (UILocalNotification *)notification {
MyViewController* controller = [[MyViewController alloc] autorelease];
if ([[notification.userInfo objectForKey:#"id"] isEqualToString:#"myKey"]) {
[controller checkActive];
}
}
Through logging and watching some breakpoints, this is all working. If the app is in the background, the notification comes in, app opens, and the correct instance method is called.
What I cannot figure out at all is why some parts of the instance method are simply being passed by, with no effect. For a simple example, if we have this:
-(void)checkActive {
ViewThing.alpha = 1.0;
NSLog(#"checkActive ran");
}
The log statement will show up fine, but the ViewThing will not change. Elsewhere in the main view controller I'm calling the same checkActive method with no problems and it changes the ViewThing. (via another interface button IBAction method in that case).
There are no errors, no warnings, the console shows nothing, putting a breakpoint on ViewThing shows that it hits the line. I'm stumped, cannot see what is different from trying to calling the method from the delegate vs. on an IBAction.
Thanks for any tips!
If the alpha is not correctly changing there a few possible issues with 1 and 2 being the most likely.
ViewThing is nil. Reasons could be is the view unloaded and you set it to nil or checkActive was called before the outlets were set.
ViewThing.alpha is being set on a thread that is not the main thread. Attempting to change UI elements on a separate thread will caused undefined behavior such as never updating the change or taking an extended amount of time to update the change. You can check if it is the main thread using [NSThread isMainThread].
ViewThing is pointing a different view.
1 & 2 can easily be checked by logging view
NSLog(#"checkActive ran %#", ViewThing);

iPhone app crashes when user exits UITableView with MBProgressHUD running in separate thread

In my app, I have a UITableViewController which loads calculated data from my Core Data store. This can take quite some time and hangs the main thread, so to give the user visual feedback, I have installed the MBProgressHUD widget which will show a progress indicator (just the spinning wheel) while the calculation method runs in a separate thread.
This is fine, except that the user can still exit the UITableViewController if they think it's taking too long. Of course, this is a good thing, except that when the separate thread concludes its operation, it still tries to call its delegate (the UITableViewController) to hide the MBProgressHUD. This causes a crash because since the user already exited the UITableViewController, it has dealloc'd and release'd itself.
MBProgressHUD has the following code to try to stop this:
if(delegate != nil && [delegate conformsToProtocol:#protocol(MBProgressHUDDelegate)]) {
if([delegate respondsToSelector:#selector(hudWasHidden)]) {
[delegate performSelector:#selector(hudWasHidden)];
}
}
However, my app somehow seems to still be running this inner code ([delegate performSelector:#selector(hudWasHidden)]) even though the UITableViewController is totally gone--causing the app to crash.
Any suggestions? I am not running with NSZombiesEnabled.
From your UITableViewController viewWillDisappear, viewDidDisappear or dealloc method, set the MBProgressHUD.delegate = nil;
Once the user has exited the table controller, the delegate property of the hud points to a deallocated object (= a memory zone that could contain anything). That causes the crash when the computing thread ends and tries to send any message to the delegate.
In your table view controller dealloc, you must set the hud delegate to nil.

How to avoid data lose when UIImagePickerController unloads my controller?

I am using UIImagePickerController to take a photo from the camera. However, the I find that randomly my calling controller (the one that is shown before the UIImagePickercontroller is shown) gets unloaded. I logged the viewDidUnload, and indeed it does get called. When the camera is done and dismissed, my controller's viewDidLoad will be called, unfortunately all the state is now gone. Things like text entered or things selected will be gone.
Obviously this is something to do with running out of memory. But is this behavior normal? Should I be handling it? This is NOT typically how modalViewController works. Usually you show it and dismiss it, everything should be intact.
What is a good way to avoid data lost in this case? Should I have to write a bunch of code to save the full state?
what's happening is that your view controller's didReceiveMemoryWarning is being called, and then since your view isn't visible, it's being unloaded.
the solution is that any data that wants to be persistent should be stored in your view controller rather than in your view.
you can prevent this from happening by providing an implementation of didReceiveMemoryWarning in your UIViewController class that doesn't call [super didReceiveMemoryWarning] which is where the unloading of the view happens, but it's still a good thing to try to understand what's going on.
It's really not advisable to override -didReceiveMemoryWarning so that UIViewController can't deallocate the view because then you may run into a "real" problem of low memory and your app will be forced to quit by the system.
What you really should do is store the text and other bits of entered data from the view in your -didReceiveMemoryWarning method (making sure to call super). Then in -viewDidLoad you can put the text and other bits back into the UI.
This will lighten the memory usage and reduce the likelihood of your app being forced to quit because there's no more memory left. All the while, the user won't know it ever disappeared!
My solution to the same issue described above was to declare an instance variable in my view controller which stores the contents of the UITextView should the view get unloaded. Thus in my viewWillDissapear method I save the contents of UITextView.text into my instance variable and in my viewWillAppear method I restore the contents.
- (void)viewWillAppear:(BOOL)animated
{
if (messageTextString != nil)
{
textEdit.text = messageTextString;
}
[super viewWillAppear:animated];
}
(void)viewWillDisappear:(BOOL)animated
{
self.messageTextString = textEdit.text;
[super viewWillDisappear:animated];
}