Navigating to ViewController containing ScrollView Going too Slow - ios

This is how am pushing from ViewController A to ViewController B
UIStoryboard *mainStory = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ListingViewController *listView = [mainStory instantiateViewControllerWithIdentifier:#"listing"];
[self.navigationController pushViewController:listView animated:YES];
and i've tried this way as well using a segue in StoryBoard
[self performSegueWithIdentifier:#"Associate3" sender:sender];
Pushing to this ViewController for the first time freezes (around 4 sec) before start pushing, knowing that VC B contains UIScrollView Object in its XIB,
Did this happened to any of you, any one knows how to solve this delay?
EDIT:
i've already commented all webservice calling methods, nothing Changed! I think its an allocating delay, am using the storyboard initiating with identifier to push VC B, but when i used allocating method: VC *B = [[VC alloc] init], then pushing to this view works without delay, but the issue that i don't need to use the allocating method!!

Finally found the issue, it was the custom font that am using in the labels of the VC B, just set them back to System Font instead of Roboto solved my problem, i think the system takes time to find the Font with specific name. Hope this would be useful for others

It seems you are performing some heavy operation on main thread in ViewWillAppear or ViewDidLoad of ViewController B
Comment whole code of both above mentioned methods and then run your application which verifies problem in app.
So if its verified then you must move your heavy operation from main thread to background thread.

This might be because pushing is executed in different thread. Try pushing the viewController in main thread. i.e.,replace your push controller code with
dispatch_async(dispatch_get_main_queue(), ^{
UIStoryboard *mainStory = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
ListingViewController *listView = [mainStory instantiateViewControllerWithIdentifier:#"listing"];
[self.navigationController pushViewController:listView animated:YES];
});

Try to change in ListingViewController class viewDidLoad method to call async
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
//move your code here
// if you need to do something on ui (reload table view e.t.c) - call async in main thread
dispatch_async(dispatch_get_main_queue(), ^{
//Your UI code
});
});
}

You can use 'Instruments-Time Profiler', a developer tool with Xcode. It can check the time expense. Then, I think you will find you problem.

Related

Time delay issue when loading a view controller

I used many properties as Textfield, labels, Textviews, Uiviews, Tableview, scrollview in employer_REG_NEW Viewcontroller. Now when I call push viewcontroller it takes upto 3 seconds to load the new viewcontroller
My code is
employer_REG_NEW *empSIGHN_UPVC = [[employer_REG_NEW alloc] initWithNibName:#"employer_REG_NEW" bundle:nil];
[[self navigationController] pushViewController:empSIGHN_UPVC animated:NO];
Make sure that you are not doing any heavy processing in
viewDidLoad: , viewWillAppear: and viewDidAppear: methods which may overload the main thread.

PresentViewController completion block called too soon

I am modally presenting a ViewController from another one. The completion block is being called immediately, while the presented VC is still presented. Why would that be? Code follows.
UIStoryboard* sb = [UIStoryboard storyboardWithName:...];
UINavigationController* nc = [sb instantiateViewController...];
nc.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:nc
animated:YES
completion:^{ /* Called immediately! */ }];
There is stuff I want to do only when the presented VC is finished. I have a workaround but my understanding is I should be able to do it in the completion block.
The completion block is called once the viewController has been presented, as per Apple's documentation. This is the intended behavior: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/instm/UIViewController/presentViewController:animated:completion:
I'd suggest wiring up some sort of callback in your viewController's viewWillDissappear to perform an action if necessary.
OK I had RTFM and saw this: "completion: The block to execute after the presentation finishes." Which really does imply what I thought. Ambiguous at best.
HOWEVER... elsewhere we read "The completion handler is called after the viewDidAppear: method is called on the presented view controller." Which is quite another thing, and confirms Ian.
So my workaround is actually the right way to do it...

How do I present a ViewController over another with that VC having no knowledge of the new one?

I had a method to do this, but it stopped working at some point.
The motivation here is for debugging. I have a button that shows a debugging action sheet from whatever VC calls it. This works great. However, in the action sheet, after I select one, the action wanted is in some cases the presentation of a new VC. The first example of this was a VC that displays my internal log. It's very valuable when not debugging in a "tethered" mode.
Each debugging VC is represented as a scene in the Main storyboard. I instantiate the VC with instantiateViewControllerWithIdentifier:. Then I am trying to get it presented.
The tricky part is that the new VC has to be presented and then dismissed without writing any code in the VC that is currently active. Neither do I want to create a Segue from every VC where this might be called. The whole point is that the DebugActionSheet is self contained except for the single call to fire it up.
You should be able to access the top most view controller like this from your ActionSheet delegate method.
+ (UIViewController*) topMostController
{
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
return topController;
}
then in the calling code:
[MyDebugController.topMostController presentViewController:myLoggingView
animated:YES
completion:nil];
and your myLoggingView can dismiss itself by calling
[self.presentingViewController dismissViewControllerAnimated:YES
completion:nil]
Try presenting it on the main thread?
dispatch_async(dispatch_get_main_queue(), ^ {
[self presentViewController:vc animated:YES completion:nil];
});
It turned out my problem was that the current top controller is using a Navigation controller, so the required code is different.
UIStoryboard *story = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
_paletteVC = [story instantiateViewControllerWithIdentifier:#"PaletteDisplayVC"];
[[_delegate navigationController] pushViewController: _paletteVC
animated: YES];
I pass the current top controller to my DebugActionSheet as delegate, so I do not need the topMostController method above. However, I presume it would work with that also.

pushViewController Extremely slow

What could cause pushViewController to be extremely slow? (it takes 30+seconds for the new view to appear)
Basically, I'm doing something like this:
SecondViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"correctID"];
vc.something = something;
[self.navigationController pushViewController:vc animated:YES];
CLS_LOG(#"Pushed Controller...");
and i'm logging at the beginning of viewdidload inside the second view controller.
I'm not subclassing other methods.
Between Pushed Controller... and the next log from viewdidload there's a huge delay.
How would you debug this?
I already tried with the TimeProfiler but apparently it shows nothing.
Had similar problem before, try the following
dispatch_async(dispatch_get_main_queue(), ^{
// your navigation controller action goes here
});
Dino's answer saved my life. He got my upvote. I'm just adding this for modern swift users.
DispatchQueue.main.async {
self.navigationController?.pushViewController(vc, animated: true)
}
Setting the background color e.g. vc.view.backgroundColor = .white solves the issue for me.

Navigation controller popViewControllerAnimated : yes is not working as expected

I am using following line of code:
[self.navigationController popViewControllerAnimated:YES];
But it is not behaving in ios 7 as it doing in ios 6.Some times it does not pop controller while we are pressing back button 2- 3 times in succession.
Resulting in abrupt behaviour in navigation bar and deallocating a controller but showing the same on ui .
So when we press anything on that controller it results to a crash since controller is already deallocated.
Check if you're running the code on the UI thread
[self.navigationController popToRootViewControllerAnimated:YES];
This method will navigate to the root of your navigationController.
You can check your viewController hierachy With following code.
NSLog(#"%#",self.navigationController.viewControllers);
I resolved this problem with this way:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UINavigationController * nav = tabbarControl.selectedViewController;
[nav.viewControllers objectAtIndex:0];
[nav setViewControllers:#[[nav.viewControllers objectAtIndex:0]] animated:NO];
tabbarControl.selectedIndex = 0;
});
When you delay one second the view will pop from UI, then the view will pop from the navigation stack, I think is the problem of the animation serial.
I had the same problem on iOS 8.
I solved by subclassing UINavigationController and adding this code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.delegate = self;
}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
return [super popViewControllerAnimated:animated];
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
{
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
}
I basically block all the user interactions during the pop animation. I know it's a dirty solution, but it's the only one that I found that solves the problem.
I think that should be working without dispatch_async.
I got to the same issue, but i got to know the reason.
We should check it if the current scene is assigned to a proper view controller name in the storyboard.(identity inspector -> class)
If you connect a button action to m file and then insert the name of the view controller, that is not working.
So, you should delete the connect, and you insert the proper view controller name, and then you should connect the action to m file again.
I created my project from master-detail template, that uses split view controller. In my case, removing the split view controller resolved this issue.
It is important that that calls to popViewController(animated:), popToRootViewController(animated:) and related calls be made in the main queue, but under some conditions this doesn't seem to be good enough, and the animation doesn't occur.
I was able to fix it as described in some other answers here, performing the pop navigation later in the main queue. The Swift equivalent:
DispatchQueue.main.async {
self.rootViewController.popViewController(animated: true)
}
This might be explained by other animations that are still in progress, and by scheduling the block this way it happens at the end of the current work taking place or currently scheduled in the main queue, which allows the animation to execute correctly.
Try this code for popup a view controller from navigation stack
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count -2] animated:YES];

Resources