Is it safe to dismiss a viewController without closing the document Object - ios

My application is a document based application I present the saved content using an UIViewController subclass EditViewsController with the help of my customized UIDocument object.
EditViewsController will look like below
Tapping the close Button on the left top corner will fire the below method
-(IBAction)closeForm:(id)sender
{
// _formDocument is my UIdocument subclass Object
[_formDocument closeWithCompletionHandler:^(BOOL completion){
[self dismissViewControllerAnimated:YES completion:nil];
}];
}
My problem is that while calling the method closeWithCompletionHandler my application freezes for a while before closing.
My question is that is it right to dismiss the viewcontroller without closing the document (simply call dismissViewControllerAnimated: inside the firing method) or I have to run that method in background thread to get rid of
freezing?

-(IBAction)closeForm:(id)sender
{
// _formDocument is my UIdocument subclass Object
[_formDocument closeWithCompletionHandler:^(BOOL completion){
[NSOperationQueue mainQueue] addOperationBlock:^{
// Dismissing it on main thread
[self dismissViewControllerAnimated:YES completion:nil];
}];
}];
}

Related

iOS, is it ok to pop a ViewController from a navigationController, even if a request called from it isn't finished?

Here is my situation:
I have a viewController whose main goal is to upload one photo. I use ReactiveCocoa like this:
- (IBAction)uploadTapped
{
[[PhotosService uploadSignal] subscribeError:^(NSError *error) {
NSLog(#"%#", error);
} completed:^{
NSLog(#"Upload completed");
}];
//I don't need to wait the upload result
[self.navigationController popViewControllerAnimated:YES];`
}
Is it ok to pop the viewController even if the upload isn't finished? I wouldn't want to risk an EXC_BAD_ACCESS, or a memory leak because of that...
Thank you for your help

Perform call to server in viewDidLoad

I'm pretty new on iOS development and I'm having the following problem. I need to fill a UITableView with data fetched from a server.
I'm doing this call using AFNetworking. I want to show a "loading view" using SVProgressHUD and I'm doing the following:
-(void) viewDidLoad{
[super viewDidLoad];
[SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeGradient];
[[MyServer sharedManager] fetchFromServerwithCompletionHandler:^(NSArray *elements, BOOL error) {
_elements = elements;
[_elementsTableView reloadData];
[SVProgressHUD dismiss];
}];
}
I'm getting the correct answer from the server but the progress hud is not being displayed. The call to the server does take some seconds so there is time for the progress hud view to be loaded but nothing happens.
I'm performing the call in the viewDidLoad because I need the call to be made as soon as the view is loaded and I want to call this just once.
If I move this block of code to the viewDidAppear the progress hud loads, but then this method is going to be called every-time the view is shown and I don't want this...
Thanks a lot!
The problem is not that the network call doesn't take place if done in viewDidLoad (because that is the right place to do it), but rather that the attempt to add the SVProgressHUD in viewDidLoad won't work, because this is too early in the process of constructing the view for SVProgressHUD to figure out where to put the HUD.
One simple solution would be to have viewDidLoad defer the invocation of this, to give the UI a chance to get to a state that SVProgressHUD can correctly place itself in the view hierarchy. As silly as it seems, you can just dispatch the code back to the main loop, which gives the UI a chance to catch up:
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeGradient];
[[MyServer sharedManager] fetchFromServerwithCompletionHandler:^(NSArray *elements, BOOL error) {
_elements = elements;
[_elementsTableView reloadData];
[SVProgressHUD dismiss];
}];
});
}
A trick can be use NSUserDefaults to save a value like #"serverAlreadyCalled" and check on viewDidAppear if you called the server before.
example:
-(viewDidAppear){
NSUserDefaults* defaults = [NSUserDefaults standardDefaults];
if ([defaults valueForKey:#"serverAlreadyCalled"] == nil) {
[SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeGradient];
[[MyServer sharedManager] fetchFromServerwithCompletionHandler:^(NSArray *elements, BOOL error) {
_elements = elements;
[defaults setValue:#"YES" forKey:#"serverAlreadyCalled"];
[_elementsTableView reloadData];
[SVProgressHUD dismiss];
}];
}
}
Don't forget to set nil to your key #"serverAlreadyCalled" before your app finishes run.
Maybe it's not the best solution, but can make the trick.
Hope it helps.

Lag between viewWillAppear and viewDidAppear

I have a strange issue using presentViewController as part of a library.
The code that is using the library calls this method. It takes up to 12 seconds from calling presentViewController to running the completion block. But not all the time normally it's almost instantaneous.
However if I touch any where on the screen while it is "lagging" it will fire instantly.
-(void) advancedMenuWithPresentingViewController
:(UIViewController *)presentingViewController
animated:(BOOL)animated
onClose:(void (^)(void))onClose
onPrint:(void (^)(NSString *, NSString *))onPrint{
AdvancedMenuViewController *amc = [[AdvancedMenuViewController alloc] init];
AdvancedMenuViewController * __weak weakAmc = amc;
[amc setOnClose:^(void)
{
[weakAmc dismissViewControllerAnimated:animated completion:^{
onClose();
}];
}];
[amc setOnPrint:onPrint];
//Time from here
[presentingViewController presentViewController:amc
animated:animated
completion:^{
//To here
}];
}
viewDidLoad and viewWillAppear are called without any lag and then there is a long delay (unless you touch the screen) until viewDidAppear is called.
There is nothing inside any of these methods that would slow it down. As it normally works fine.
If any one could shed any light on this issue I would be most grateful, the most confusing part is that if you interact with the screen after firing advancedMenuWithPresentingViewController it will occur instantly.
Replacing
[presentingViewController presentViewController:amc
animated:animated
completion:^{
//To here
}];
with
dispatch_async(dispatch_get_main_queue(), ^{
[presentingViewController presentViewController:amc
animated:animated
completion:^{
//To here
}];
});
Resolved the issue.
Thanks to rmaddys suggestion.
Run the app in Instruments with the Time Profiler.
That should at least tell you if you have some slow drawing code or if something in UIKit is slowing it down.

Waiting For Method Or Running In The Background

I have a piece of code that firstly calls a method that does quite a bit of thumbnail generation so it slows the device down for about a second. I was hoping to run a method that generates a "loading message" before the first method is called and then remove it when the first method is finished.
[picker dismissViewControllerAnimated:YES completion:^{
NSLog(#"Loading");
[self generatingThumbnailMessageShow];
[self loadAllEffects];
}];
The problem seems to be that although the "generatingThumbnailMessageShow" method is before the "loadAllEffects" method it still seems to get called after the "loadAllEffects" message is finished. What is the best method to call the "loadAllEffects" method only when the first method is finished?
The problem is that [self loadAllEffects] runs on the main thread and blocks the UI. Changes to the UI become only visible after program control has returns to the main runloop.
You have to move the execution of [self generatingThumbnailMessageShow]; to a background thread, something like
[picker dismissViewControllerAnimated:YES completion:^{
NSLog(#"Loading");
// Show "loading" message (must be done on main thread)
[self generatingThumbnailMessageShow];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// perform long running task on background thread
[self loadAllEffects];
dispatch_sync(dispatch_get_main_queue(), ^{
// Hide "loading" message (must be done on main thread again).
[self generatingThumbnailMessageHide];
})
})
}];

performSelector:withObject:afterDelay: not working on modal view controller

I have a modal view controller that appears, checks a service on the Internet and then dismisses itself when done. The nib contains an activity indicator and a label to inform the user what is going on.
When the update is complete, the label changes to "Update Complete" and then dismisses the view controller. However, I want it to delay the dismiss for a couple of seconds to give the user a chance to see the text before it disappears. So I've done this:
#pragma mark - AssetLoaderServiceDelegate
- (void)assetLoaderServiceDidFinishLoading:(AssetLoaderService *)service
{
[self.spinner stopAnimating];
self.infoLabel.text = #"Update complete";
[self performSelector:#selector(dismissUpdater) withObject:nil afterDelay:2.0];
}
- (void)dismissUpdater
{
[self dismissModalViewControllerAnimated:YES];
}
But for some reason, the selector is never being called. I've tried running it in mode NSRunLoopCommonModes too, but that doesn't work either.
I must be doing something wrong, but I can't see what...
EDIT: The delegate callback is actually happening within an NSOperationQueue, which might mean it's not on the same thread when it sends the message back to the view controller? So I tried
[self performSelector:#selector(downloadQueueComplete) withObject:nil afterDelay:0.0 inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
followed by
- (void)downloadQueueComplete
{
[delegate assetLoaderServiceDidFinishLoading:self];
}
But the performSelector doesn't seem to be working here either.
Following up your suggestion about the thread issue, would you try with:
[self performSelectorOnMainThread:#selector(downloadQueueComplete) withObject:nil waitUntilDone:YES]];
Sorted! In the AssetLoaderService, I had to perform selector on main thread:
[self performSelectorOnMainThread:#selector(downloadQueueComplete) withObject:nil waitUntilDone:YES];
After that, all the following calls were on the correct thread. :)

Resources