How to completely reset a UITableViewController while still on screen - ios

In my app, I have a very custom UITableView. The cells are all statically defined in Interface Builder, but based on the data structure the table morphs in many various ways. For example, if some data doesn't exist, some cells (or entire sections) are not displayed, custom separator lines are added to account for missing cells, extra views are loaded into the cells, VoiceOver labels change, etc. Because all the cells are static, I set up the table layout in viewDidLoad because I always have the data available at that time. I have always presented this view controller modally, which has worked great. If the user wants to display different data in this table they have to dismiss the view controller and pick a different item to present it again, and it gets rendered appropriately in all cases.
But now I am converting this into a split view controller for iPad, so this UITableViewController never disappears off screen, but I need to set up the table again when the user taps an item. The problem is, because the table is never deallocated, its previous layout still exists when I load more data into it. It would be a lot of work (and an excellent opportunity for many difficult to reproduce bugs to pop up) to test all possible scenarios and try to reset it back to its "pre viewDidLoad state" or undo those previous layout changes if not relevant anymore, if not impossible because I don't have references to the many different custom separator lines generated.
My question is, is it possible to completely reset the table view controller every time a row is selected in the master view controller, therefore allowing it to properly set up the layout because it is not stuck with the previous layout?
I essentially need some way to completely wipe it clean as if it never did any setup, then instantiate it again to cause viewDidLoad get called (or I can move that code to its own method or viewWillAppear). I'm basically looking for a way to reset the tableView back to how it is defined in Interface Builder.
I believe this would result in a flash because the table would completely disappear then reappear in a different format, but that would be acceptable. If that can be animated that'd be nice. If this is really not recommended at all, how do you suggest I proceed to ensure the layout is always appropriate for the data it is presenting?

I was over-thinking this. There's really no need to completely throw away the table and generate a new one. It turned out to be simpler than I had thought to reset the table back to its default state. Just had to be sure to catch every possible thing that could change, including VoiceOver labels, and reset to nil or the default value. Then it can run through the reset code then the layout code every time the data changes and render an appropriate layout. The most difficult part was to remove the custom separator lines, which I solved by adding each one to an array when it's created, then index through it and remove each one from its superview then remove the Autolayout constraints associated with it. One can wrap all of this into a UIView animation block to get a nice fading effect. It's working quite well.

Related

XCode autocomplete

I'm new to IOS development, I have a few questions.
1) What's the purpose of property rowheight on table view cell, I mean it does nothing even if I change its value, it always takes the value from its parent view i.e a tableview property rowheight? It visually changes in the IB but nothing happens when I run the app.
2) What's the purpose of Content View why is it even there? Let's say If I have to make some image equal to the height of the cell it restricts me. Or is there any way a content view can be changed to be equal to the cell height & width? I have to put constraints on the image in relation with the cell which is not the immediate parent of the image and I don't know if this is the correct way to do it.
3)How does Xcode Autocomplete works? like if I want to write a function tableview(_:tableview didselectrowwithindex:IndexPath) and I type tableview it shows a list, what to do next? I mean I can't type the whole fucntion with params or find the func in the huge list.
The height of the cell set at the IB is primarily used for simulation, the views described at IB are normally resized when actually used. E.g. you can set rows height to be 100 for the table view, 30 for some of the cells and keep the whole controller simulating a nice screen of iPhone 6. The same view will be used for all devices and will be scaled accordingly as well as the cells with the help of your delegate.
The content view is there for the reasons directly related to your additional requests. It holds all the content while there other views that accompany your content and are part of the cell like separators, accessory views, slide action views. Without a content view the responsibility of managing all the additional parts would most likely fall on you as a developer and while you might think that that is fine at the simple layouts, a simple enhancement to it would make a huge impact.
Fuzzy autocompletion at Xcode seems to be something Apple is working on now. If you can't wait and find it too difficult to navigate through the list, there are Xcode plugins available that provide fuzzy autocompletion.
Answering the question in the topic:
example: tableview(_:tableview didselectrowwithindex:IndexPath)
if you write tableview it will show all the symbols that start with tableview. For functions, it will show all the functions sorted by the second parameter name (didSelectRowWithIndex).
[EDIT]
it will autocomplete as far as the answer is unique and then show you a list full of options. I don't know any tricks to skip looking through the massive list. But after a while you'll know what you're looking for and it gets faster.
[\EDIT]
when you press tab, it
by the way: the delegate functions names start with the name of the object they're related to.
So UITableViewDelegate functions start with tableview.
as for your first two questions there are tons of answers for those questions on SO. This one seems closely related to yours.

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.

UIPageViewController cache and swap view hierarchy for pages with similar content

My UIViewController has a UIPageViewController embedded in it. The pager can contain anywhere from 5-38 pages (each page is an instance of a UIViewController subclass) depending on the situation. I've noticed that depending how complicated I make the UI elements on each page, the app slows down considerably, and is very slow when swiping to go to the next page.
Here's the thing - the view on each page is identical, except for the values of a few UITextViews. I am building the view in each page's viewDidLoad method each time viewControllerAtIndex is called for a new page. I feel like there must be a way to re-use the same view for each page, and just swap the text values that are supposed to be different. Can anyone describe a strategy to do this?
Like I said, every page has an identical view hierarchy except the values for some of the text, so I'm really just looking for a way to maintain one basic view controller per page, but cache the view hierarchy to be re-used on each page, and swap out some simple text values depending on page number.
EDIT
Something I forgot to mention in the original post is that I'm building my view programmatically because the number of elements on the page is dependent on choices the user made on previous screens. i.e. there may be 5 TextViews or 10 TextViews, etc. depending on what the user selected on a previous screen (before coming to the UIPageViewController). With that said, I do not believe an xib based approach will work because the initial layout is dynamic.
Thanks in advance!
Have you tried using a xib based view, which you could pull out of the xib once, and keep a strong reference to it. In your controller's viewDidLoad, you could set its view to this xib base view, then populate the text view(s) with the proper text.

UITableView display distorted before setAnimationTransition during view swap

Greetings! I'm trying to borrow the view flip concept from Apple's TheElements sample app. This sample employs a container UIView in which you can swap between two subviews. The flip is achieved using setAnimationTransition:forView:cache: and removing/adding each subview.
In general, the flip works and I can swap between my two views (a UITableView with headers/footers, and a MKMapView). However, various (consistently chosen) areas of my table view are obliterated (using the table view's background color) before and after the table view is flipped, and I don't understand why.
I can't find anything unusual about the table view in terms of drawing. In the case of the sample app, the view is drawn from scratch, but I would hope that doesn't factor in to it! I'm really hoping it's something simple - maybe a UITableView property setting?
Clues appreciated. Thanks!
Update: When I slow down the animation, I begin to get an understanding of what is being disturbed (though I still don't know why). My table header view contains an image view and label view. Now, imagine the CGRects for those two views (only without any visible content, just a background color) being redrawn further down the table view, right over the table rows.
This also happens with another chunk of real estate that appears to come from a table view cell that was set with a custom height (to size the text within it).
I dropped a gratuitous number of breakpoints throughout my code (where things are sized, created and whatnot), and not one of them is hit during the transition.
I even tried placing my table view inside a UIView and targeting that for the transition instead. No difference.
From the API docs:
Caching can improve performance but if
you set this parameter to YES, you
must not update the view or its
subviews during the transition.
Updating the view and its subviews may
interfere with the caching behaviors
and cause the view contents to be
rendered incorrectly (or in the wrong
location) during the animation. You
must wait until the transition ends to
update the view.
That's all well and good, but I'm not updating any views or subviews during the transition. (At least not on purpose! Again, see the note about the breakpoints. Nothing got hit.)
Amazing.
Answer: In this case, it appears to only happen in ... the Simulator! On the device, it's fine.
I was thinking that such a run-of-the-mill transition would be the sort of thing that would render identically on the Simulator and the device.
I was wrong. So if you ever see animation transition glitches on the simulator, hang in there. It might be fine after all.

Resources