I have a ViewController with a few buttons and it also has a child view that has a sublayer added to it. Its size is equal to the size of the ViewController's main view.
I used self.view.layoutIfNeeded() on viewDidLoad() and also dispatched the sublayer to the main dispatch with DispatchQueue.main.async{...}. While the sublayer gets drawn immediately, the buttons take anywhere from 10-15 seconds to appear.
What gives?
I used self.view.layoutIfNeeded() on viewDidLoad()
Well, don't. The view is not even in the interface yet. Layout will take place at the proper time; don't cause trouble by trying to shortcircuit things.
and also dispatched the sublayer to the main dispatch with DispatchQueue.main.async{...}. While the sublayer gets drawn immediately, the buttons take anywhere from 10-15 seconds to appear.
Despite what you say about the main thread, this sort of delay is always due to a threading issue: you are trying to talk to the interface off the main thread, or doing some sort of synchronous networking. You have shown no code, so no more can be said, but a threading issue is certainly the cause.
Related
I am optimizing a transition that seems to be slow on my device. I am pushing one UIViewController from another when a UITableView's row is selected. There is a noticeable pause after row selection and before the new view is pushed.
Some logging indicates that all of my code is reasonably quick, from row selection until the pushed controller's viewWillAppear. But then the time between viewWillAppear and viewDidAppear is logged at around 0.7 seconds.
The transition itself (I believe) should only take 0.3 seconds. What could be accounting for the remainder?
I am testing on an iPhone 4, so I'm not expecting the snappiest performance. But I should be able to match the same performance of other similar apps on the same device, no?
I had a similar question a few weeks ago, and I wrote a blog post about what I found:
http://bradbambara.wordpress.com/2014/07/31/object-life-cycle-uiviewcontroller/
The TL;DR version is that iOS will:
perform the layout of your new scene
perform the transition to your new scene (if it's an animated transition)
...so my guess is the delay could be caused by an especially long transition, or if you're doing any performance-intensive work in your layout code.
The transition itself (I believe) should only take 0.3 seconds. What could be accounting for the remainder?
Resources are usually consumed in the following methods: drawRect:, layoutSubviews, viewDidLoad, viewWillAppear:. Also, loading from NIB may require quite much time.
After viewWillAppear:, iOS will make a snapshot of the new (and probably current) view to perform smooth animation between two screens. So make sure that drawing and layout code for both controller views is fast enough.
Using XCode 4.4 and Mountain Lion,
I have a UIImageView, and on that view, I have a UIProgressView and a UIButton. When the app starts, the progress view and button are both hidden, as set in the storyboard. I then try to unhide them, first the progress bar when I'm doing something, and then the button when I'm done. I have called, for both items,
[self.view bringSubviewToFront:saveToCameraRoll];
to try to put them in front of the UIView.
Problem is, when I programmatically try to unhide them, it doesn't work. I can call:
progressBar.hidden = NO;
[self.view bringSubviewToFront:progressBar];
And that does nothing.
So then I tried to set everything as visibile in the storyboard and then programmatically set everything to be invisible once the controller loads. No deal; now my calls to hidden = YES seem to be ignored.
I then removed all actual programming, so that hitting buttons should just cause the button and progress bar to appear, reasoning that maybe the main thread was getting starved and couldn't update.
How can I force elements to pay attention to being hidden?
EDIT: I've now also tried programmatically modifying the alpha from 1 to 0. No change. It's like everything I'm doing is getting ignored. I made these items via the ctrl-drag method into the #interface section of the .m file; maybe I don't have some more delegates or interfaces or whatever hooked up?
As you said that you connected the ivars with your XIB file, it seems like your problem is that you are doing stuff on the main thread which in return blocks the run loop and your UI doesn't get redrawn anymore. When you update UI elements by changing their properties, those changes aren't applied instantaneous but the next time the UI gets redrawn which only happens when you give the main threads runloop a chance to run. However, if you do something like the following code, the changes will never appear:
[button setHidden:YES];
[self doSomethingReallyExpensiveAndTimeConsuming];
[button setHidden:NO];
The result of this code is that the button is set to be hidden, but doesn't get redrawn because the system has no chance to do it and when the system actually has a chance to redraw the UI, the button is already set to be invisible. A fix for this is to either split the work up and schedule it via timers on the main thread, or to use something like GCD to offload the work on a secondary thread (but then you need to make sure that your code is threadsafe!)
Is it possible the outlets are nil because they aren't hooked up in IB? If so, no amount of manipulation will have an effect.
Try NSLog(#"%#", saveToCameraRoll);
Is it null? Fix by reconnecting outlets in IB. If that works, then .hidden = NO will work and you can get rid of any code you added to manipulate the view hierarchy.
I discovered that my drawRect is being called more than once. Unfortunately this had the unfortunate side-effect of double-drawing everything because all my subviews are drawn in drawRect (I'm s strict atheist w.r.t. Interface Builder).
What is the best way to deal with a multiple calls to drawrect? A flag to check if it's being called again? Or clear the whole view and redraw from scratch (as I have done?)
What do you mean by "your subviews are drawn in drawRect"? If you mean that you're putting calls to -addSubview: in your drawRect, don't. Move those to a more appropriate place that gets called only when it needs to (probably the -initWithFrame: method for your view if the subviews are always there), and use drawRect only to do custom drawing.
Strictly speaking, being called multiple times is the entire point of -drawRect. It's called to update small parts of your view when needed. It's actually called quite often if you're updating your view (moving it around, updating the superview, etc.), so it should be as simple and fast as possible to avoid performance problems.
When UIKit calls drawRect:, the graphics context set up to draw into should already be cleared for you (unless you have set the clearsContextBeforeDrawing property to NO). Are you perhaps calling drawRect: manually instead of calling setNeedsDisplay/setNeedsDisplayInRect:?
Also, each view is responsible for drawing only its own content and not the content of its subviews.
I have an animation in a EAGLView which is itself in a UITableViewCell. How can I pause the animation in the EAGLView when the view is not visible?
Normally, I would simply use the responsible UIViewController and listen to viewDidDisappear. But how do I do that if the EAGLView is in a table?
I don't think that this is a task to implement at all.
Once your cell is scrolled out of view, it will be deallocated instantly.
So if you have a Custom Cell, the animation will have to be stopped in -dealloc anyhow.
EDIT 1:
Actually, I was not really precise: I wrote "instantly", but of course, this depends on the OS and Apple and may be changed in future versions. Actually, the cell is deallocated whenever the OS garbage collector wants. Currently, Apple deallocates one cell whenever it needs a new one. Usually, scrolling a table implies that one row disappears and a new one appears, so that's why deallocation seems to happen instantly.
If the view, that is switched on, contains a table view, than you will see the same instant deallocation.
I have an iPhone app that displays a modal view controller. The modal view controller shows two instances of a custom subclass of UITextView called RoundedTextView, an MKMapView, and a UIToolbar. I construct the viewController only once, and reset its data and present it each time the user summons it.
When showing this view controller with presentModalViewController, I noticed that the animation to show the view was choppy on the 3G. So, to speed it up, I set the alpha of the MKMapView and the two RoundedTextView objects to 0 on viewWillDisappear and back to 1 on viewDidAppear. This made it nice and fast. I also presume that I could remove the views from the superview to speed it up as well.
Does anyone else jump through these kind of hoops on the iPhone. Is there something else I should be doing to avoid this hack?
It's not a hack to simplify drawing during animation in order to make the animation more smooth. It is indeed a very valid technique.
You may be able to achieve similar performance improvements by setting all UI elements to Opaque, a technique also used to fix table view cell performance issues. You just have to make sure background colors match.
The main problem I had was I subclassed UIButton to make gradient buttons and I had the boundary mask enabled. This made the performance terrible. I removed that option and made my buttons square and it's blazin now.