ios 7.1
Summary: When user segues to new table view controller, system allocates too much memory that's why I need some kind of re-usable or singleton table view controller.
I have a table view controller (let say vertical home) which has inner table view's(horizontal) inside of it's cells. If I wouldn't need to create horizontal tables, I could create a single table and manipulate it's cellForRowAtIndexPath method instead creating an inner one.
Anyway there is another functionality; there are buttons inside of the most inner cells and if user taps that, it triggers a push segue which navigates to another table view controller that has same vertical & horizontal structure. It is highly potential that user is going to go to those table view controllers lot's of times.
When monitoring the memory usage of application:
If user stays at home view controller and scrolls vertically, system allocates approximately 1 more mb per each cell which has inner table view. 1 mb is acceptable even it is great actually. Btw. I am using reusable cells to be able to decrease memory usage through those lines of code:
[tableView registerNib: customCellNib forCellReuseIdentifier:HorizontalContainerTableCellIdentifier];
cell = (HorizontalContainerTableCell *) [tableView dequeueReusableCellWithIdentifier:HorizontalContainerTableCellIdentifier];
If user triggers a segue; a new table view controller is created as desribed as here. and even if user doesn't scroll system allocates around 5 mb per each table view controller.
That means cost of creating a new view controller for each segue is too much. I can accept that my view hierarchy is not light weighted but as it can be seen in the 1st usage scenario if user scrolls vertically system is re-using pre-created cells and memory cost is not that much.
So which way should I follow? Do I need to subclass segues and redirect them to pre-initialized view controller and update it's table (like this)? By this way I can force the system to reuse cells. Or is everything normal and is it how it's supposed to be?
If you segue viewController, the older viewController will be pushed into the navigation stack and a new viewController will be created and also pushed into the stack. I think you can make the usage less by freeing tableView data in viewWillDisappear and reload them on viewWillAppear. Just trigger reloadData and return 0 in numberOfRowsInSectionthat will free the memory used by tableView.
It seems that was my mistake. Because the controller view which contains the most outer table view was not releasing at all. When I profile via instruments I have seen couple instances of same controller despite I have popped them already.
The root cause of that, ARC was expecting GUI elements defined as weak. I have changed property definitions as weak instead strong. Then ARC started to release controller instances.
However I have still memory problem. Because I am creating image context for each cell which consumes memory a lot.
Related
I have a UITableView. When I click on a row, it will push a detail view controller to show detail info.
And, if user clicks the "Done" button on the detail view controller, I need to pop this controller and delete row on tableview controller.
I try write code on detail view's viewWillDisappear:
if let vc = navigationController?.viewControllers[0] as? TodoViewController{
vc.removeTodo(at:todoListIndex)
}
The code work fine, but just a warning:
UITableView was told to layout its visible cells and other contents without being in the view hierarchy (the table view or one of its superviews has not been added to a window). This may cause bugs by forcing views inside the table view to load and perform layout without accurate information (e.g. table view bounds, trait collection, layout margins, safe area insets, etc), and will also cause unnecessary performance overhead due to extra layout passes.
So, What is the correct way to delete row?
As the error message suggests, it is there to remind you to avoid updating the view when it isn't visible. This typically happens when you update a view in a viewWill____ state (in this case viewWillDisappear).
So what you want to do is delete the cell from TableView in viewDid___ (such as viewDidAppear)lifecycle. Then, you will no longer see warning messages.
If needed, you can use help from flag members and observables to guide correct deletion timing.
A project I'm working on has a storyboard set up as follows:
Tableview controller (A) contains a tableview of static cells
Each static cell is a container view
One of these container views embeds another table view controller (B)
The table view of controller B is tableview with dynamic prototypes.
Everything works and renders appropriately, however I have begin to run into a performance problem when the number of cells in the embedded table view controller (B) becomes very large.
What seems to be happening is that iOS is allocating / attempting to render all the cells (ie calling cellForRowAtIndexPath), even those which are offscreen. This is because the embedded table is large enough to show all cells, because scrolling is disabled for this table and instead the root controller (A) scrolls the content.
Does anyone know a way to make this situation more performant when the embedded table view has a large number of cells? I thought about creating an artifically paged system where cells are only loaded when the user scrolls, however initial cells which have scrolled out of the view would still remain allocated, so I would have to set up some sort of dynamic allocation, which is what iOS already does if you simple use a single table view.
I have seen this design pattern proposed elsewhere and it seems Apple is really pushing adoption of containers as it makes for clean and modular code using storyboards, so I am hoping that there is a simple solution to this that I am missing.
Thanks.
All,
I am running into some performance/memory issues when using several ChildViewControllers in my ParentViewController. Here is my situation: I have a ParentViewController with a dynamic number of ChildViewControllers - some times as many as 20. They are contained in a UIScrollView, and are paged through. I'm running into problems when I have several on the page (I'm only loading the first two, then building the others as I swipe), however, having that many within the ParentViewController is starting to cause some crashes due to memory.
The ChildViewController has a lot going on in it, and I'm going through it to make sure it's as efficient as possible, however, I have concerns about this approach working on older devices (as is, I'm getting crashes on the 5S).
It seems that it would help to change the view controllers to just be views, but it'd be a pretty large endeavor as the VC is complex. One suggestion I had was to create a view from the existing view controller's view, and set several delegate methods on the view and interact with the views from the ParentViewController that way. Does any one have any thoughts on the efficiency of that method as a opposed to the current method of using ChildViewControllers?
Another thought I had was to build a custom ContainerViewController and have all the children in there to swipe through, but I wasn't sure if that would give me an advantage over using the children in a UIScrollView.
Any thoughts?
I personally would not advocate the refactoring of your code to use views rather than view controllers. The view controller, itself, is unlikely to be the source of the memory problems, but rather the model objects they keep track of (as well as the assets the view controller's view uses). I think the key is to simply remove the view controllers (and their views) as they scroll off of the screen.
In your scrolling logic, as you're adding child view controllers that scroll into view, you are presumably doing all of the appropriate containment calls:
UIViewController *newChildViewController = ...
[self addChildViewController:newChildViewController];
newChildViewController.view.frame = ...;
[self.scrollView addSubview:newChildViewController.view];
[newChildViewController didMoveToParentViewController:self];
(See WWDC 2011 video Implementing UIViewController Containment for a discussion about why it's important to do these containment calls, namely to keep your view controller hierarchy synchronized with your view hierarchy.)
As the child views scroll out of view, you just do the appropriate containment calls to remove the child controller (and its view):
[childViewControllerToRemove willMoveToParentViewController:nil];
[childViewControllerToRemove.view removeFromSuperview];
[childViewControllerToRemove removeFromParentViewController];
// also remove any other strong references you have to that childViewControllerToRemove
Alternatively, you might want to contemplate using a UIPageViewController which (in iOS 6+) offers scrolling page view (UIPageViewControllerTransitionStyleScroll) for the transitionStyle. This simplifies the amount of custom container code you have to write to handle the view controllers that scroll in and out of view. The UIPageViewController is designed precisely for this situation of scrolling (or paging) through a bunch of different view controllers' views. See the Page View Controllers discussion in the View Controller Catalog for iOS.
I don't think moving them all to UIView's will help. You could achieve this same effect by just adding aChildViewController.view without ever technically adding it as a childView. I would optimize the loading of the views into your UIScrollView. Make sure you only have, say 4 views loaded into memory at any one time. Another option would be to use a horizontal UITableView or UICollectionView so you can reap the memory management features they have build in.
You could turn it into a tableview and let the cell dequeue take over. You would only have a few in memory at a time then.
I have seen questions asked about mutliple UITableViews in one view but they all have only one table visible at a time. I am after something different please.
In my head I want four UITableViews visible in one UIScrollView inside one UIView. The four tables will be visible and populated at once. They will have a dynamic number of rows each so the scroll view will allow users to scroll off of the page to see rows that do not fit.
The tables would be two side by side and then below them the next two side by side so that you end up with a 2x2 square.
I can (sort of) wrap my head around how to code this in the controllers etc. but I cannot figure out how to organise the hierarchi. I have tried using the storeboard to layout the tables inside the view but 9 out of 10 attempts to drop controls in fail as I am obviously not fully understanding this.
Do I need to generate the UITableViews in the UIViews implementation file and add them as objects to the UIView? Or can I use the Storyboard?
Could someone please explain how the hierarchi of objects would be structured?
In my head it would be:
UIViewController
-> UiView
---> UIScrollView
------> UITableView
------> UITableView
------> UITableView
------> UITableView
But trying this in Storyboard doesn't work. I assume each UITableView will want its own UITableViewController and what I have read in other posts I would likey need to do this connecting in the UIViewController implementation file.
Any help is appreciated.
I think you might try to drag UITableViewController into your view Controller, at least I don't have that problem to add 4 table view into a scroll view.
here is how i added it
1.> Drag the scroll view control into view controller
Your view controller should look like this:
2.> Drag the table view control into the scroll view, and set the size and position of that table view
Your view controller should look like this:
3.> Then drag all the rest 3 table views onto Scroll view
But i would like to suggest a couple of things in your case
no using that much table view in the same view controller, it's a chaos in your codes to maintain all them. There are always better
options than 4 table view, maybe consider collection view. or even
separate the use flow.
If i were you, i won't use table view inside Scroll view, they are like scroll view inside scroll view, if you don't design the
interaction very very well, they become extremely hard to use.
If you still want to use four table view in the same view controller after all, you want to pay extra attentions on your table view datasource and delegate. very carefully handle all the cases.
Hope that helps you.
Tableviews are very customized scrollviews. I wouldn't put 4 of them on a scrollview, this wouldn't be very intuitive for the user as your finger would scroll the view in many ways depending on where exactly it touches the screen.
Instead, try having your 4 tableviews in a 2x2 pattern directly onto a simple UIView. This can be done inside the Storyboard.
As for filling up and using them, you have 2 ways :
A) Your UIViewController is the delegate and datasource of each of the 4 tableviews. When executing, you perform a switch on the first parameter (the tableview that called you) to determine the appropriate course of action.
B) You create 4 classes that each handle a single tableview, instanciate 4 objects inside your UIViewController and assign the UITableviews' delegate and datasource properties to these objects.
All technicality aside, 4 tableviews in a single screen is pretty crowded. Your design will definitely not fly on a iPhone, so I'm assuming iPad only. Even then, you should make sure that everything is visually appealing and the purpose of each control is clear. It's up to you, but I'd hate to see you work hard on an application only to see your efforts wasted because your visual design doesn't appeal to your users.
If the table views take up the entire region of the scroll view then they wont let any scroll events past to the scroll view that contains them, unless the scroll is horizontal.
For a simple one to one between a table view and a view controller, I would make each table view part of it's own UITableViewController (so you have four), and then make a UIViewController that adds each of the UITableViewControllers to it as a child.
This way you don't have to do any fancy logic around if statements on which tableview is asking for data, because the table view controllers only have one table view.
https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html
I am pushing and popping from one view to the other within my App. The view is being retained in the memory so when you hit the "Back" button after pushing a view, the same screen that was before you pushed the view is retained.
For some reason, I will need to reload the parent view after popping from a child view. I need to display different content based on the actions the user taken when they were redirected to the child view.
I am using UINavigationController to navigate from one view to the other. I need it so I can easily go back and forth within the different views of the App.
The correct way to do this would be to perform your actions in viewDidAppear. Initialisation code that you write in viewDidLoad is called only once. But in viewDidAppear you can refresh your view's content every time the view is added to the window. The controller is retained in the memory for performance reasons. Removing it would hamper that factor.
Here is a stack overflow post that explains the different view* callbacks in good detail.