The code below is used to push another view controller onto the navigation stack.
When using instantiateViewControllerWithIdentifier, the segue is noticeably sluggish the first time (~3 seconds) but occurs reasonably fast each subsequent time. Other SO posts suggested ensuring the segue occurs on the main thread, which the code accomplishes, but this didn't fix the problem.
However, using performSegueWithIdentifier causes no delay.
The viewDidLoad code for SendViewController is the same for the first and subsequent pushes.
Tried blanking out viewDidLoad for the destination view controller, but still the lag exists for instantiateViewControllerWithIdentifier but not for performSegueWithIdentifier.
How to fix the delay with instantiateViewControllerWithIdentifier?
No delay:
#IBAction func buttonTapped(sender: UIButton) {
performSegueWithIdentifier(SendSegue, sender: self)
}
Results in delay when showing SendViewController for first time:
#IBAction func buttonTapped(sender: UIButton) {
dispatch_async(dispatch_get_main_queue()) {
let vc = self.storyboard!.instantiateViewControllerWithIdentifier(self.SendViewControllerID) as! SendViewController
self.navigationController!.pushViewController(vc, animated: true)
}
}
This issue could occur in many different scenarios. The best way determine what is causing your specific problem is by profiling with the instruments included in Xcode.
Click and hold the Build button in your xcode window. You will see four options appear, select Profile.
Once the build runs a window with instruments will pop up. Select, Time Profiling from the options.
A new window will appear with various metrics in it. The top left corner will have a red record button. Click the red record button and this will launch the app on your phone.
Proceed to the transition giving you problems. End the recording after the transition occurs by selecting the same button you started the recording with.
Review the "Details" pane in the bottom left corner. You will see a column titled "Running time" that shows the time it took to execute every method in your code (both OS methods and user generated code)
Determine if anything is out of place or occurs that is not intended. Possibly go back and execute the transition again to compare the difference between the two. Clicking the function in the list will take you directly to the code being executed. This can be very helpful.
It is very likely that if a transition takes 3-5 seconds one particular function will be obvious when following these steps. Happy profiling!
WWDC from last year has a great segment on this as well. Def worth checking out here: (open in Safari only) WWDC Profiling Talk
The problem was isolated to the presence of a UITextField in the destination view controller, that is, removing the UITextField removes the lag.
Then it was further isolated to the presence of a custom font.
In other words, using the system font on the UITextField, rather than a custom font, removes the lag. No explanation why, but it works.
After time profiling I realized it was the call to instantiateViewController which I couldn't find anything that could help me with that.
Unfortunately, the only thing that worked was either using a separate storyboard for that view controller and instantiating it from there, or redoing the view controller programmatically.
Related
I was following the tutorial here and had to make a few changes to fit my project (code of my variation here, sorry I put it on GitHub because it's a bit lengthy to copy and paste). Sometimes when I swipe right to get to the last VC, it automatically closes on sight. Other times it works properly where if I swipe to the last VC it stays there until I swipe right again to close it. I've placed print statements in the code to help me find where the bug is. Here are the print statements when it works and when it doesn't work. Looking at the logs, since the exact same code is accessed in the same order, I'm not sure how to fix this bug. This problem doesn't occur when the transition style is PageCurl instead of Scroll.
First you need to know how pageViewControllers work.
After the first page is loaded, the moment you start to scroll it will try to load the next page. this is just for the first page. after that as soon as you reach a page it will preload the next page (depending on the direction) and have it ready.
In your case when you reach page 5 the pageViewController will try to preload the next page which leads to this part of your code
guard orderedViewControllersCount != nextIndex else {
print("last item in coachamrks going to dismiss")
self.dismissViewControllerAnimated(true, completion: nil)
return orderedViewControllers.first
}
and it will dismiss the viewController as soon as you reach page 5.
This is not a bug. It's the default behavior of pageViewController.
I think the reason that you sometimes find it behaving the way you wrongly expected is that when you scroll through the pages fast enough the pageViewController stops preloading pages for performance reasons.
The only way you could achieve what you want is a hackish way of accessing the scrollView inside of the pageViewController but it is not safe and might break your app if Apple changes the architecture of pageViewControllers in a future update.
I suggest you put a button in the last page and use it to dismiss the pageViewController.
I'm using Xcode 5.1.1
When i click the BACK button from ViewController 1 to ViewController 2, and when VC 1 appears the ImageView glitches for 1 second before going back to normal. I tried programming the segue with performSegueWithIdentifier but that didn't solve the issue. I tried using Modal's cross dissolve transition, cover vertical and partial cross but they don't help the glitch either. Only Flip Horizontal doesn't have the glitch but it wouldn't look good with my app. I'm currently using unwind segue to go from VC 2 to VC 1 but the VC 1 is glitching for 1 second. I tried regular modal but that creates worse problems. What do i do?
Well you should admit that "The UIImageView" is quite a generic assumption to figure out what your issue actually is. Just for pure diagnosis purpose I'd do the following checks:
Check the size of your UIImage you're assigning to the UIImageView. If the UIImage is too big the higher resolution might cause some rendering problem in the animations.
To have a proper check of what's going on you might want to run instruments adding "Time Profiler" to your tab. Performing either the pop or dismiss of your VC2 you should be able to figure out where the glitch comes from.
I know it's quite a generic answer but with the little info you provided don't think anyone might be able to help you more than that.
Hope this helps.
I have several tableviews in a tab bar controller. On some of these I have set a UIRefreshControl.
After swiping down to start a refresh the 'spinner' animation begins. If I select a different tab and then move back to the original tab, the refresh control is still shown but the animation has stopped.
How do I restart this animation?
I've tried starting the refresh again and got no results.
I also inspected the view hierarchy, hoping to find a UIActivityIndicatorView but could only find a _UIRefreshControlModernReplicatorView that seems to be the view doing the animation. I don't know of any method I can call of this to get the animation restarted.
I had a similar problem, although not exactly the same problem as described in the original question.
The issue: When I moved from a screen with a tableView with refresh control (not a UITableViewController) to a different one, then I found when I returned to the screen, the activity indicator portion of the refresh control was displaying (not animating) as an artifact just under the top of the table view.
I tried a lot of ideas (hiding the refresh control, ensuring endRefreshing was called, etc.) until I found something that actually worked:
In viewWillAppear(animated:), add the following:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.refreshControl = nil
tableView.refreshControl = refresher
}
Just adding that code fixed the issue for me. Your mileage may vary, but perhaps somebody will find this useful.
I am working on an ipad project whose UITabBarController (it's also the root) has 5 tabs.
The second tab is a tableview where there are quite rich UI elements.
The problem is that everytime I launch the app and click the second tab, I would be blocked for 0.3 second to show the viewcontroller. However, after the first time enter this tab, it only takes me 0.03 to enter this tab again.
So I thought the ViewDidLoad cost too much, because I do a lot addSubView or reloadData there. So I add
[libraryVC viewDidLoad];
[libraryVC viewWillAppear:YES];
[libraryVC viewDidAppear:YES];
in application:didFinishLaunchingWithOptions, I think if I do this view load stuff before user enter the second tab, then there will be no block.
However after I tested this approach, I found the cost was not be reduced. So I guess the time cost may be used in the view render when first show this viewcontroller. When the viewcontroller's view has been rendered before, then the following show will be fast. But I have no idea to solve this problem.
I think this problem is very common when the viewcontroller's view is really complicated, so does anyone has an idea of the approach to solve this problem? Or does anyone can give a detail description of the reason why the block happens?
Thanks for any help!
Perhaps multithreading the time intensive methods/calls is the solution you are looking for. If you search "ios grand central dispatch" on Google, some great resources will be available. Apple's Grand Central Dispatch reference is one of the better pieces of documentation they have.
I have always been a bit unclear on the type of tasks that should be assigned to viewDidLoad vs. viewWillAppear: in a UIViewController subclass.
e.g. I am doing an app where I have a UIViewController subclass hitting a server, getting data, feeding it to a view and then displaying that view. What are the pros and cons of doing this in viewDidLoad vs. viewWillAppear?
viewDidLoad is things you have to do once. viewWillAppear gets called every time the view appears. You should do things that you only have to do once in viewDidLoad - like setting your UILabel texts. However, you may want to modify a specific part of the view every time the user gets to view it, e.g. the iPod application scrolls the lyrics back to the top every time you go to the "Now Playing" view.
However, when you are loading things from a server, you also have to think about latency. If you pack all of your network communication into viewDidLoad or viewWillAppear, they will be executed before the user gets to see the view - possibly resulting a short freeze of your app. It may be good idea to first show the user an unpopulated view with an activity indicator of some sort. When you are done with your networking, which may take a second or two (or may even fail - who knows?), you can populate the view with your data. Good examples on how this could be done can be seen in various twitter clients. For example, when you view the author detail page in Twitterrific, the view only says "Loading..." until the network queries have completed.
It's important to note that using viewDidLoad for positioning is a bit risky and should be avoided since the bounds are not set. this may cause unexpected results (I had a variety of issues...)
This post describes quite well the different methods and what happens in each of them.
currently for one-time init and positioning I'm thinking of using viewDidAppear with a flag, if anyone has any other recommendation please let me know.
Initially used only ViewDidLoad with tableView. On testing with loss of Wifi, by setting device to airplane mode, realized that the table did not refresh with return of Wifi. In fact, there appears to be no way to refresh tableView on the device even by hitting the home button with background mode set to YES in -Info.plist.
My solution:
-(void) viewWillAppear: (BOOL) animated { [self.tableView reloadData];}
Depends, Do you need the data to be loaded each time you open the view? or only once?
Red : They don't require to change every time. Once they are loaded they stay as how they were.
Purple: They need to change over time or after you load each time. You don't want to see the same 3 suggested users to follow, it needs to be reloaded every time you come back to the screen. Their photos may get updated... you don't want to see a photo from 5 years ago...
viewDidLoad: Whatever processing you have that needs to be done once.
viewWilLAppear: Whatever processing that needs to change every time the page is loaded.
Labels, icons, button titles or most dataInputedByDeveloper usually don't change.
Names, photos, links, button status, lists (input Arrays for your tableViews or collectionView) or most dataInputedByUser usually do change.