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.
Related
We have the following layout in our ViewController
As you see, it is a UIView containing a TableView, and over the tableview, a custom view that displays a message if there is nothing to display, also when the tableview is empty.
This controller reacts to the content of a local database, by using an event system. The idea is that, if the user does the gesture to refresh the tableview, the local database is deleted, and the controller receives an empty array of items, and the nothing found view should be displayed. After new info is downloaded, and the database is filled with it, the controller receives again an event with the items for the list, (or maybe again the list is empty, etc).
When debugging the view hierarchy, we can see that when starting the ViewController, the custom view "NothingFound" is there in its place. Then, we hide the NothingFound view, while downloading new contents from the server, with isHidden=true.
If it results that the server responded with an empty list, then we want the NothingFound view to be displayed again, and we set isHidden=false.
But it does not work, the view is not displayed again. Examining the view hierarchy, we can see in the navigator that the view disappeared from the hierarchy (we were expecting the view to be there, but in invisible status, but maybe we are wrong).
Maybe we are missing something, but is isHidden = false not supposed to make the view visible again? Or is there another technique to do this?
Thanks in advance
PS: Things we already tried: we already make sure that we are hiding and showing again the view on the main thread, but this does not solves the problem.
We tried to call layoutIfNeeded() after modifying the property isHidden of the view, without result.
I have the following scenario and need help in resolving a tricky situation in the scenario
There is an Xcode project and am using EzSwipeController for swipe (pagination effect) between three View Controllers at the moment.
In my first ViewController (this viewController is fetched from my custom dynamic framework as part of my requirement) -
Code to fetch ViewController:
userProfile.createProfileUI(userSession!) { result in
switch result {
case let .Success(profileViewController):
myDetailsVC = profileViewController //myDetailsVc is passed to EZSwipControllerDataSource array
default:
break
}
}
The other two ViewControllers are within my project storyboard
The Problem -
In the first ViewController, there is a tableView with canEditRowAtIndexPath enabled for few cells (phone numbers).
So when I try to swipe the row, the EZSwipeController responds first and
hence, I am not able to edit the row.
Here is what is happening - http://recordit.co/SOJgdeYchP
Here is what should happen - http://recordit.co/EBPSbjH31q
How do I handle this problem? Is there a way where I can override the default swipe controller action when I try to edit the row?
Please help!
Attach the swipe gesture recognizer to a parent in the hierarchy.
If you're using a UITableViewController, replace it with a UIViewController with a UITableView inside it. Then just drag the gesture recognizer onto the view controller in Storyboard, and it'll attach to the UIViewController's Content View instead of the UITableView.
Though at the end of the day, this is inherently a flawed approach, since swiping to flip pages in an app is only ever viable if you don't have any elements on the pages that also have swipe gestures in them. If you do, even if you code a workaround to make the gesture recognizer for the element in the view controller (in this case, a table view cell) fire instead of the page flipping swipe gesture, that creates an inconsistent user experience.
My suggestion: don't use a table view for such a form altogether. On top of the aforementioned mechanical UX issue, from a user perception perspective, there's nothing indicating visually that this is a table view and not just a scroll view, so there's nothing indicating to the user that swipe actions (Delete) are available. Use a scroll view instead, and take a different approach to deleting (Delete buttons that are always visible, Delete buttons that are only visible after the user hits Edit somewhere, etc.).
I have a HomeViewController which is loaded with views. On the top is the map view, so now when I create the table view on the top of it which does not cover the whole screen but little portion of it(actually its a drop down list in the form of tableview), it displays fine with cellForRowAtIndexPath working fine but didSelectRowAtIndexPath is not working at all.. I have tried using break point on it but its not calling this method actually infect it is selecting the view at the back i.e. the map view. Clicking on the table view is actually changing the map view behind..
I am having the table view coming from another class which is inherited by the HomeViewController
I am using some NIDropDown class .. Its working fine if no view is behind it (only superview) but not woking if it has any view behind
Few things to be ensured-
1) As you said your table view is on top of the map view. If this is the case than it's alright.
2) Check if the user interaction is enabled for the table view or not.
3) Check if you have set the delegate of the table view(responsible for detecting events on table view).
4) Check if you have not set the delegate to nil somewhere and still the table view is visible.
I think if you follow the above steps than table view's events should be triggered, otherwise it will be good if you post your code to get some other clue regarding the issue.
That cellForRowAtIndexPath works is an indication that the datasource property (UITableViewDataSource) is properly set.
If didSelectRowAtIndexPath is not firing, you should check if the delegate property (UITableViewDelegate) is properly set, either in Interface Builder or in code.
After read your problem, i strongly believe there will be two issue :
1) Map view issue, if you shows table view as *callout view
Reason:
You supposed to use “MKAnnotationView” callout view for show drop down over map.
if you do so then make sure MKAnnotationView view height would be exact same of your drop down view’s(NIDropdown view’s) height.
Table view not able to get touch, that's why it would not send message of didSelectRowAtIndexPath to it's deletegte.
In your case might be Map view getting touch, which did not suppose to get until table view is at screen.
2) Table view rest out of bound on super view
Reason:
in such case table view display normally but actually its bound goes out of own super view bound.
When you subclass UITableViewController, you normally get certain behaviors "for free". For example, when a text field in your table view becomes first responder, the view controller automatically scrolls itself to ensure the field is fully visible.
However, when the table view controller is the detail view controller of a UISplitViewController, this auto-scrolling no longer takes into account the presence of the keyboard. The table view controller will still auto-scroll to keep the text field within the bounds of the screen, but it no longer scrolls to keep the field from being covered by the keyboard.
You can test this yourself by creating a new project using Xcode's "Master-Detail Application" template, and replacing the detail view controller with a table view controller that displays cells with text fields in them.
I would like to understand why the auto-scrolling stops accounting for the keyboard in this case, and if possible how to rectify that without having to duplicate the auto-scrolling functionality myself. BTW, this has nothing to do with overriding viewWillAppear (as in some other questions here about table view controller auto-scrolling).
I know its late but this might help others who are having this issue. This happens to me as well when I added textfield in UITableViewCell. What I did was to remove
superViewWilAppear:animated
line in viewWillAppear method.So the method looks like this
-(void)viewWillAppear:(BOOL)animated{
//[superViewWilAppear:animated];
Your rest of code
}
But what this does is it removes the auto scrolling all together and you have to manage the scrolling of UITableView when textField starts editing. Don't know if this solved your problem but it will save you the trouble of considering keyboard height for different devices and its better to manage on your own. Also I am not sure if this is the right way to do it but it worked for me.
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.