This question is really basic. What is the performance difference between removing a UIView from the view hierarchy and hiding a UIView?
I have read that Views that are not needed should be removed from the view hierarchy. I currently have the situation that a UIButton should sometimes be visible. When do I hide the UIButton and when do I remove it from it's superview?
Is it expensive to change the view hierarchy?
I've done an experiment on iOS6 iPad mini, with a large scroll view that has a lot of rich content (including images, drop shadows, gradient layers, patterned background images, you know, those designers:) ) and I found that view.hidden=YES ≠ [view removeFromSuperview].
I originally thought that setting hidden to YES will make the view not being render/drawn, therefore having a lot of hidden views will have no impact on efficiency. But the actual result is:
1) if I set the offscreen views in the big scroll view to hidden (and unhide them when they come back into visible area), the scrolling is not smooth/continuous at all. When it's naturally decelerating it looks very jumpy.
2) if I remove the offscreen views from the scroll view (but still keep in memory with a tracking array, so when they come back in they can be added immediately), the scrolling is obviously smoother.
If you need to alternate between showing and hiding the subview, the best approach is definitely hiding it. For a UIButton the memory implications are not that great anyway. And the code is certainly simpler if you just switch the hidden property.
Also, you get the additional advantage that the hidden property is animatable!
Some years have passed between the initial question and the (rightfully) accepted answer. Let me add another factor: In the meantime, Apple has introduced AutoLayout, which — as some say — might come with quite a performance penalty in certain (deep) subview hierarchies.
If you are using AutoLayout, a view that is hidden will still get layouted, as opposed to the removed view (with its reference saved somewhere). Depending on your scenario, this could then make a performance difference.
Related
I have an iPad app similar to the iPad Keynote with a narrow overview on the left and a paged UICollectionView of my "slides" on the right. The collection view is using the default FlowLayout. Some of these slides are standard PDFs and some are embedded UIViewControllers that have been scaled (with a CGAffineTransform) and embedded in the cell. I'd like to smoothly animate the overview sidebar offscreen and zoom the current page cell to fullscreen. The collection view should allow paged swiping at whatever size. I'm using storyboards and autolayout.
I think I need to simultaneously animate about three things:
The collection view constraints (to the sidebar) to enlarge/shrink it
The flow layout's sizeForItemAt: value
The CGAffineTransform on the embedded view controller.
I have some pieces working (a single embedded View Controller "slide" that scales correctly) but cannot get the collectionView/cell resize dance to work correctly. The cell resize animation is jerky, or ends up with the wrong offset, or works for the leftmost cell but not for other cells.
I've tried most of the suggestions in the answers to this question but with no convincing success. I can't believe it's impossible but at this point I'm considering the smoke and mirrors approach of animating a static slide and hiding it after the animation completes. The attached video - ignoring the glitches - illustrates the kind of effect I'm after:
It's worth noting, on close inspection, that Keynote cheats somewhat when it comes to swiping between slides in edit mode, and manually manages the next slide sliding onscreen, so probably doesn't use a UICollectionView.
Has anyone done anything similar, or have any suggestions for things to try?
I managed to solve this. There's a proof-of-concept GitHub repo here.
There are a few moving parts:
There are two pieces of UIView scaling code. I suspect these could be combined with suitable delegate references as the nested view controllers are embedded. The first piece scales (statically) correctly when the Collection View cell is created. The second is an animated scale/translate when the sidebar size is changed.
I added a FlowLayout subclass to remove flicker as the collection's layout is invalidated during scaling.
The sidebar-related transform in the top-level VC uses most of the tricks in the book - invalidateLayout(), performBatchUpdates(...), layoutIfNeeded() etc. as well as animating the contentOffset to the correct value. Some manual tracking of the correct page/slide is required.
There's still a slight flicker occasionally at the start of resizing. This may not be an issue with my particular colour-scheme which will be black on dark gray. Bonus points if anyone can suggest how to track this down or alleviate it.
I've got a view controller that handles a lot of complex on-screen controls. One of these is a sliding menu that contains 9 UIButton controls drawn in a grid using constraints, like so:
The load time of this view controller is nearly 3 seconds on device, and I've narrowed down the cause to the quantity of UIButtons inside this view. (I originally thought it was because it was being drawn off screen, but moving it to the center of the view controller didn't help).
I'm trying to come up with a way to improve the performance of these buttons, but I'm not really sure which direction to go. Will loading them in a UICollectionView help? Or is there a way to lazy-load the buttons so that they're drawn after the view controller is presented?
Turns out migrating this to a UICollectionView (even using constraints written in code) has made a significant performance improvement to this view controller.
It also has the added benefit of supporting paged views - more than 9 controls in the menu.
I find myself often having to reposition subviews of a view after hiding or showing one of them. The way I'm doing this is by programmatically changing a view frame's origin and/or size, or its center. But is there an easier way I'm missing? Is there a way to do it with Autosizing masks?
I don't think there's any automatic way of doing this. You could probably get clever about how you do it programmatically (e.g. if you used a set of sequential tag identifiers, you could loop through and calculate the height of the previously visible tag to calculate the origin of the next subview; or if there are a group of subviews that are always going to move together, you could put them in a container UIView and thus move a whole bunch of them by just moving their container view; etc. ... it depends upon how they're laid out and which fields might be hidden).
This won't help you for now, but check out WWDC 2012 session 202 for a discussion of a relevant improvement in iOS 6.
I'm short on RAM most of the time.
Perhaps you should rethink your app logic if you are piling up views like that. However have you tried setting the bottom views hidden property to YES?
If its not visible, remove it from the superview, if you are so tight on RAM.
However, I can't see why you are in a state with multiple seemingly-memory-heavy stacked views in the first place.
After reading about UIView's autoresizingMask on SO and developer.apple.com I'm still unclear what the purpose is. What's a situation where setting this property is necessary?
Yes, it is often necessary to set it if you don't want to resize the views manually. Note that it is mostly useful for subviews (i.e. those views that don't take the whole screen) rather then the main view of your app.
Views typically may need resizing if:
the device is rotated
an extra view (say, an ad) is added to the view, so the existing subviews have less available space.
For example, suppose if you have a view with two buttons on it, one in the top-left corner, another in the top-right corner. In order for the buttons to get wider when the view transitions from portrait to landscape, you need to set the FlexibleLeftMargin to the right button, FlexibleRightMargin to the left button.
Edit: autoresizingMask is also the first thing to look at if you see weird holes or overlaps when device is rotated or a new subview is added. Quite often the proper setting of these masks for subviews can get you a nice looking view in both orientations without having to lay out subviews manually - but usually it takes some experimenting.
Edit2: (since this is still gathering upvotes) Autoresizing masks are now mostly superseded with "Auto Layout", which allows for much more flexible constraints on views' sizes and positions. That being said, translatesAutoresizingMaskIntoConstraints is still occasionally useful for dynamically added views.
The purpose is that UIView properly shifts and resizes when its superview changes due to resizing, orientation change, showing editing controls in tableview cells etc.