speed of UIView add/remove subview versus hiding/showing - ios

Let's say I have a full-screen UIView that overlays the main screen when a button is touched, and then goes away when this overlayed view is touched. This UIView could either be added and removed from the current view using addSubview: and removeFromSuperview, or it could be added when the current view is initialized and then shown and hidden by accessing and setting the hidden property of the UIView. Which is generally faster and better for performance (or are they the same)?

I did try add imageView and try loop 1000000 times to hide and show in each loop and add remove in each loop. Result is hide and show take 1s to do 1000000 loop. And add remove take 3s. I do it in simulator :)

I'd bet show and hide will be faster. The other way requires object creation/destruction, and fiddling with subviews.
More importantly, I think show and hide will be simpler, and the fight against complexity is paramount.

As Clay says, showing hiding will probably be faster, but you would need sensitive instruments to detect the difference. It's going to be single-digit hundredths of a second at the most, and probably much less than that. You won't be able to sense the difference "by eye".
Thus what matters is other things, like what is the easiest to understand and maintain? One problem with making a view exist in the view controller and showing/hiding it at well is that the layout of the view covers the other contents of the view controller and makes it hard to manage.
You can create a second XIB (or an XIB that goes along with your storyboard) that has your view controller's class as it's "File's owner" and link up IBOutlets to the views you want. Then you load the view from an XIB when you need it, install it as a subview of your current view. Then you remove it from the superview when you're done with it. I use that approach a fair amount.

Related

How to decide what view should be on top in Interface Builder

So i have a problem that i know how to solve but not in ann efficient/fast way. Look at this example that i have right now:
So I had made all the outlets before I decided that I need a background image. You can see that I marked them but they are under the UIImage. I can solve this by first removing the image and dragging all the views to another view controller then adding the UIImage and putting the views back on the ImageView but this is time consuming as I have other view controllers. Is there another way to move views up/down?
Think of the views as being listed in the order they are placed on top of others. You start at the top of the list, adding views, and then add the ones next on the list on top, one a time. That's how I remember the ordering.
As Luk2302 says, you can simply drag your views around inside your storyboard to change their order.
Note that there is also an "arrange" sub-menu in the editor menu for IB. You could select your background image, then pick editor>arrange>send to back to move the background view to the back of the stack of views.
P.S.: Don't call them outlets. They are views. Views can be linked to your code using IBOutlets, but they don't have to be. Plus other non-view objects like constraints can also be linked to your code with IBOutlets
Its simple.what you have to do is simply drag you imageview to the top of the view(then it becomes the first IBOutlet you added). I have added some images, then it will easy to understand.
this is your situation
this is the solution(drag it to the top of the view)
then your other IBOutlets come to the front like this

Computationally, which makes more sense when loading subviews into a view

From an expense point of view, which option provides the best computational results when adding subviews to a UIView's hierarchy:
Scenario 1
In viewWillAppear:animated or willLayoutSubviews remove all subviews of the container view using [[aView subviews] makeObjectPerformSelector:#selector(removeFromSuperView], reset all pointers or properties and then (re)initialise all subviews and add them to the container view hierarchy.
Scenario 2
Initialize and add all subviews in the viewDidLoad method (called once only, thus only adding them to the view hierarchy once, then in the viewWillAppear:animated or willLayoutSubviews methods setting the constraints or frames for each subview.
Scenario 2 is much more faster than the first one. Creating and deleting views is expensive you should try to limit as much as possible.
If you are using autolayout setting the constraints can also be done in the viewDidLoad, and setting the frame is not recommended.
So the best is to create and add all the subviews in the viewDidLoad set up the autolayout constraints there and do not set the views frame, only the constraints in other methods if needed.
Similar to your condition there is another condition :
Take the situation where you need to present a view when the user taps a button. There are at least two approaches to this:
1.Create the view when the screen is first loaded and hide it; then when you need it, show it.
2.Do nothing until you need to show the view. Then, create the view and show it, all at once.
Each approach has its own pros and cons.
Using the first method, you consume more memory because you
immediately create the view which holds on to that memory until it’s
released. However, when the user does taps the button, your app will
appear more responsive as it only needs to change the view’s
visibility.
Taking the second approach will have the opposite effect; by creating
the view only when it’s required, you consume less memory; however,
the app won’t appear as responsive when the button is tapped.

Delay between viewWillDisappear and viewDidDissapear

I'm seeing some performance degradation in my application after some time and I'm trying to figure out what's going exactly.
I have a complex view controller (VC1) which contains scroll view, few table views inside, some custom cells with horizontal scrolling and custom drawing etc.
After several (around 10) refreshes of all these objects (reloading tables, reposition subviews etc) when I try to call presentViewController to push another view controller above VC1 I can see about 2 seconds delay between viewWillDisappear and viewDidDissapear
I tried to profile the app to see if there are memory leaks but couldn't find any. Memory usage grows when view refreshes and switches between different modes, but then it become more or less stable in around 30m.
Works fine in Simulator, but visible slower on iPhone5. And this slowness is visible only when I try to switch from that view controller.
I ran a profiler and recorded where these 2 seconds are spent. Here is link to trace file: https://dl.dropboxusercontent.com/u/6402890/trace.trace.zip
Majority of the time spent by UIKit doing layout as I can see.
What can I do to optimize it? Is there way to take may be a snapshot of a view and use it for "leaving view" animation and restore view hierarchy when we're coming back?
UPDATE: Adding screenshot for the profiler (click for full resolution):
UPDATE2:
After analyzing output from recursiveDescription I can see the following:
In the easiest case I have ~200 lines in the output. And performance is ok.
When I switch to more complex scenario hierarchy of views growth to ~500 lines, but still performs ok.
After multiple refreshes this number goes to ~2000 and this is where it become slow. Analyzing output with 2000 views I can see that ~1500 of them belong to hidden cells that are not even displayed in this mode anymore. When I'm refreshing table views cell types change too, and I'm utilizing different cells, but why the cells that are not used anymore are still being in subviews of table views?
Any recommendations?
From your stack, I suspect you've added a large number of views you didn't mean to add. Since it's related to reloads, I would check your reload logic and make sure it doesn't re-add all the views in your hierarchy without removing the previous views. You can write a quick debug routine use -recursiveDescription to recursively walk the -subviews of each view and print them out to see what's in the hierarchy.
It's possible that your issue is in the layer hierarchy rather than the view hierarchy, but the symptoms you describe make me think views.
EDIT: From your update, you probably have one of two things going on. Most likely, if these are actual UITableViewCells that shouldn't even exist anymore, then you have a retain loop somewhere. Alternately, your cellForRowAtIndexPath: may be incorrect and may be adding new views to an existing cell when it should just be reconfiguring the cell.
In either case, though, 200 views seems a lot of views for a "best case." You may be overusing views in places that you should be doing custom drawing. If the performance is ok, then… ok, but I'd test carefully on your slowest supported devices.
From the Instruments Time Profiler output, you can see that NSISEngine is eating up a ton of CPU. That class is responsible for doing the Auto Layout constraint evaluation and layout calculation.
So it looks like you are using Auto Layout, at least for some of the views.
Are you by any chance removing and re-adding constraints anywhere at runtime? I've seen this exact problem caused by that (can explain more if this is relevant).
If you aren't removing constraints, it sounds like you might have a fairly complex view hierarchy, and if you're using Auto Layout throughout, it's likely that you have a lot of constraints. As you may know, Auto Layout degrades in performance pretty quickly above a certain point due to super-linear time complexity of solving constraints. Check the output of po [[UIWindow keyWindow] recursiveDescription] from the debugger to see what your view hierarchy looks like.
I'm not sure what your view controller transition looks like, but you could try removing the disappearing view controller's view from its superview before doing the present. That should prevent it from doing layout calculations as it transitions. If that solves the performance issue, you could quickly snapshot the view hierarchy and then replace it with a single new UIImageView of the snapshot to display during the transition animation.
(One final thing: are any of your table views using Auto Layout in their cells? do any of these table views have more than ~20 cells?)
When presenting another controller your original controller has to be animated out of the window, which causes the view's frame to change and probably triggers all layoutSubviews methods and your manual adjustments.
You could try to avoid this by deactivating autoresizesSubviews in viewWillDisappear.
It is not very clean, but then probably all the calculations you are making may not be as well!
Try to optimize them:
Don't call layoutSubviews directly and call setNeedsLayout only if really needed.
Try to replace your manual resizing code with autoresizingMask or autoLayout.
Adjust views lazily and only if they are visible and if their size and not origin really has changed.
Don't reload your tables entirely but try to change only individual rows.
Try to use only a single table view.
Make sure to reuse cells.
The problem may have something to do with that you are placing UITableView instances inside a UIScrollView. That is explicitly prohibited by Apple's documentation for UIWebView (surprise!):
Important: You should not embed UIWebView or UITableView objects in UIScrollView objects. If you do so, unexpected behaviour can result because touch events for the two objects can be mixed up and wrongly handled.
I suspect that may also mess up table view cell reuse mechanism. Anyway, I'd also recommend to check if you are not 'leaking' any views at all. Keep in mind that even invisible views participate in layout if they are in the view hierarchy.
Edit: in response to Update 2
It's evident that cell reuse mechanism is not functioning properly. Try to make sure you are using correct instances of UITableView when dequeueing table view cells from reuse queue (check your data sources).

UIPageViewController: how to have "negative" spacing between view controllers (with scroll-transition style)

I have to implement a view controller (on iPhone, portrait only, full screen view) where the upper part of the view must have an horinzontal, paged scrolling behavior, potentially infinite.
I already used for similar purposes UIPageViewControllers, to take advantage of the datasource and delegate protocols, which are very helpul for manage memory and other stuff (keeping only 3 view controllers in memory, providing delegates to handle actions exactly when a transition is done and so on): so I think that in this case too this component is the best choice.
But here comes my problem. In the view I'm realizing, I have to let the user understand that he can swipe left and right to move to another view: a page control is not a good choice, since the scroll could be potentially infinite, so I would like to let a small portion of the views of the left and right view controllers to be visible.
Something like that:
link to the image (sorry I cannot include images in my posts yet)
Up to now I have not been able to figure out how to realize this. In the options during initialization, UIPageViewControllerOptionSpineLocationKey can be specified to set (from documentation) "Space between pages, in points": but this seems to work only with positive value, so that the space increases, while it ignores negative values that could reduce the space.
I hope there might be a solution using page view controllers, since at the same time I need to refresh a table view in the lower part of the screen when a transition is complete, and the delegate method of page controllers is ideal for this aim.
Maybe a collection view can be an alternate solution, but it is more complicated and I'm not sure how to obtain a behavior like the one I described to refresh the table view.
If needed I can attach some code and a screenshot of the prototype
Ok, I understand that this is not possible and why it is.
The datasource methods load, when needed, the view controllers that are before and after the current one. Making these view controllers' views always visible, as I desired, will require that the datasource loads more than one view controllers after (or before, depends on the direction of scrolling) the current one, to be ready for the pan actions (potentially, before the animation is ended by the user lifting up its finger, two view controllers "after" or "before" could become visible it my desired configuration), and this is not intended by UIPageViewController on iPhone, especially in portrait mode.
So actually, the only way to achieve that more than one view is visible in an horizontal-scrolling component at any time, is to implement a UIScrollview with horizontal paging and calculate the contentSize and other sizes accordingly.

IOS Storyboard: Load Multiple Subviews for given position

I have 3 different subviews all defined directly in the storyboard, so they have outlets to them as well. All of these subviews are meant to occupy the same coordinates on a screen at different times, with only one occupying the space at any time, so that it looks like some appearance is changing. How do I go about doing this? Say I also have a enumeration that defines what state I'm currently in and thus what subview is shown for that location.
Two basic options:
Just go ahead and add the three subviews to your scene. If you do this, there are a couple of tricks that will make your life much easier in IB:
For each of the views, go to the "identity inspector" tab (the third one) in the far right panel, expand the "Document" section, and give each of the three views unique "labels" (not to be confused with UILabel controls; this is just a label or description that IB will use internally to refer to your view). That way, as you navigate the tree of controls listed in the "Document Outline" (that list of all of your scenes that appears in the left side of the center panel), you'll be able to figure out which is which. As you work with these overlapping views, a strong command of this "document outline" will make your life much easier.
When you have the three views on the scene, you may find that it will be easiest to drag the view you want to work on to the end of the list of the three views (but at the same level as its peers) in that "Document Outline". You can then edit that subview. Repeat that process for the three subviews as you do your IB work on them.
You can make an outlet collection for your three subviews, if you want. This makes it easier when you want to perform some action on all of the subviews. Perhaps not of great utility when dealing with only three, but if you ever had more subviews, the collections can be useful.
You can define unique UIView subclasses for each of the three views, which can be useful to keep your list of IBOutlet references a little more structured. Also any view-specific UI logic can be isolated into the individual UIView subviews.
If you use this technique, if you plan on animating the transition between these three subviews, it's actually quite useful to not just put these three subviews on the top-level view of the scene in question. It's quite useful to have a view on the scene that defines the dimensions of the three subviews, and then put your three subviews inside this new interim subview. This way, when you animate changes, you can constrain the animation to just that portion of the screen. This new, interim UIView is often called a container view, but should not be confused with the iOS 6 container view that you'll see in IB, which is related to the next technique, defined below.
While all of those tricks can make the manipulation and management of the three sets of overlapping views in a single scene a little easier, I actually think that a custom container view controller is the best way to go. One scene for the parent scene/view controller, and a separate view controller and IB scene for each of the three different child views. It takes a little extra code up front (not hard, but a little alien the first time you do it), but then your code and the IB scenes are nicely isolated. Architecturally, this is the most elegant approach, IMHO. If you want to do this, you should refer to:
WWDC 2011 #102 on UIViewController Containment (Apple developer ID required)
the containment section of the View Controller Programming Guide
the containment section of the UIViewController Reference document

Resources