MonoTouch, UITableViewController, How to insert a row in a section? - ios

I designed a UITableViewController with static cells in XCode.
After click on a button I want to add a row in one of the sections with a result.
My code:
InvokeOnMainThread (() => {
List<NSIndexPath> indexPaths = new List<NSIndexPath>();
indexPaths.Add(NSIndexPath.FromRowSection(0, 2));
TableView.InsertRows(indexPaths.ToArray(), UITableViewRowAnimation.Fade);
});
didn't work :-)
I get this error message: * Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-2380.17/UITableView.m:1070

Do you add data to your data source (IE a new row or section) after you insert the rows. You need to because inserting automatically does a myTable.reloadData, resulting in all the datasource methods being called again. If you explicitly told it to add a row at a specific index and it goes to reload and finds that index doesn't exists you get errors like this. Check cellForRow and numCellInSection

Related

RxSwift - auto reloading UITableView initialized with Observable

I have main UITableView with some Todos that are populated used an array of Observables:
// on viewDidLoad
self.todosViewModel.todos.asObservable()
.bind(to: tableView.rx.items(cellIdentifier: "TodoViewCell", cellType: TodoTableViewCell.self)) { (row, todo, cell) in
cell.status.image = todo.getStatusImage()
cell.title.text = todo.title.value
cell.todoDescription.text = todo.description.value
cell.dueDate.text = String(describing: Utilities.dateFormatter.string(from: todo.dueDate.value))
}.disposed(by: disposeBag)
On another screen I can add/edit the data and then, click "save" button to append a new todo or modify the one being edited. This works great, except that the tableView don't reload automatically, forcing me to call tableView.reloadData() on viewDidAppear, which is triggered when the other screen is dismissed.
So,
Is there a native way for me to reload a table automatically when then todosViewModel variable is updated?
EDIT:
If on the screen of edition of the todo I reassociate the todosViewModel's value property with the same value, it also works:
self.todosViewModel.todos.value = self.todosViewModel.todos.value
That's pretty ugly, but I only know how to reload a tableView using one of those ways.
When I append a new todo or reassociate a new value to the base todos it works. When I edit, no.
That's the whole point. For the UITableView to update, your todos have to emit when an item inside has been edited or you have to bind something from your todo within the cell (this way you cannot change hight of the cell but you can push some dynamic information onto the cell).
Another option would be to map some observable from todo to the index of the cell where it's presented and call UITableView.reloadRows to update your edited cell.
I would recommend you to take a look at RxDataSources examples.

Reloading data from a tableView within a collectionViewCell

I have a tableView inside a collectionViewCell and I get an error when I try to reload the data.
*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3600.6.21/UITableView.m:1610
I tried using Dispatch.main.async and it seems to get rid of the problem. The only thing is that it doesn't reload the data and nothing changes in the tableView
func cleanItems(completion: #escaping (Bool) -> Void) {
Dispatch.main.async {
cell.tableView.beginUpdates()
// Make changes in the Data Source
cell.tableView.deleteRows(at: selectedItems, with: .fade)
cell.tableView.endUpdates()
// Reloading sections accordingly depending on what the user has deleted
// Do I need to reload data here again? It used to work without Dispatch, but it wasn't stable
cell.tableView.reloadData()
// Updating items with if statements to reload data in Firebase
completion(true)
}
}
This doesn't reload the data at all and nothing seems to change. The good thing is that I don't get a random crash, which was the case before implementing Dispatch.main.async
I've retrieved the numberOfRows in each section to see how many rows there are after ending updates.
print(cell.tableView.numberOfRows(inSection: 1))
and I get the same number of rows that are in the current view.
This is crucial, because if the tableView sections are all zero, the collectionViewCell should disappear. And we never get here in the completion block as it says that the numberOfRows has never changed. Leaving us with a non updated tableView.
I solved this by moving Dispatch.main.async outside the function call.
Dispatch.main.async {
cleanItems(completion: { (success) in
etc.
})
}

NSInternalInconsistencyException when removing rows from a UITableView

I'm receiving an error indicating that I need to update the number of rows in my UITableView after I have deleted a row. I realise that It's because I've not updated the amount of rows in the UITableView, I'm just not sure how to make this change in the code I have below.
Any assistance is really appreciated.
!-- code
NSIndexPath[] rowsToReload = new NSIndexPath[] {
NSIndexPath.FromRowSection(1, 1)
};
dv.TableView.BeginUpdates ();
dv.TableView.DeleteRows(rowsToReload,UITableViewRowAnimation.Automatic);
dv.TableView.EndUpdates ();
!-- error
Name: NSInternalInconsistencyException Reason: Invalid update: invalid number of rows in section 1. The number of rows contained in an existing section after the update (3) must be equal to the number of rows contained in that section before the update (3)
Your code above is OK. However, you need to call it after you have removed the corresponding items from your data source.
For example, if you are filling your table view from a List<string> (myList) and you wanted to remove the first row:
myList.RemoveAt(0); // remove the item from the list first
NSIndexPath[] rowsToDelete = new NSIndexPath[] {
NSIndexPath.FromRowSection(0, 0)
};
dv.TableView.DeleteRows(rowsToDelete, UITableViewRowAnimation.Automatic);
No need to wrap the DeleteRows call in a Begin/EndUpdates, as you only have one delete action to perform. That is used when you have multiple different actions (eg. DeleteRows + InsertRows) so that the animations are performed smoothly.
#Dimitris thanks for answering my question. I ended up solving the problem by:
calling the reload method on my UITableView
NSIndexPath[] rowsToReload = new NSIndexPath[] {
NSIndexPath.FromRowSection(1, 1)
};
dv.TableView.ReloadRows (rowsToReload,UITableViewRowAnimation.None);
in my UITableViewCell GetCell method
I removed the label, field and button that were included on the first load of the table. Since this screen in my app will be seen only once, when the user loads it for the first time.

Delete dynamic cell in GetCell method

I am trying trying to remove a cell from the UITableView if the image for the cell is a bad image. Basically my code does a threadPool call for each cell's image to make the flow of binding the data as a user scroll smooth as so inside the GetCell method:
public override UITableViewCell GetCell (UITableView tableView, MonoTouch.Foundation.NSIndexPath indexPath)
{
//other stuff
ThreadPool.QueueUserWorkItem(new WaitCallback(RetrieveImage),(object)cell);
}
within that asynchronous method I call the cell.imageUrl property and bind it to an NSData object as so:
NSData data = NSData.FromUrl(nsUrl); //nsUrl is cell.imageUrl
From there I know that if data==null then there was a problem retrieving the image so I want to remove it. What I currently do is set the hidden property to true, but this leaves a blank space. I want to remove the cell entirety so the user won't even know it existed.
I can't set the cell height to 0 because I don't know if the imageUrl is bad until that indexrowpath initiates getcell call. I don't want to just check all images from the data thats binded to the UITableView because that would be a big performance hit since it can be easily a hundred items. I am currently grabbing the items from a webservice that gives me the first 200 items.
How can I remove the cell altogther if the data==null example
NSUrl nsUrl = new NSUrl(cell.imageUrl);
NSData data = NSData.FromUrl(nsUrl);
if (data != null) {
InvokeOnMainThread (() => {
cell.imgImage = new UIImage (data);
//cell.imgImage.SizeToFit();
});
} else {
//I tried the below line but it does't work, I have a cell.index property that I set in the getCell method
_data.RemoveAt(cell.Index);//_data is the list of items that are binded to the uitableview
InvokeOnMainThread (() => {
//cell.Hidden = true;
});
}
You remove cells by removing them from your source and then you let UITableView know that the source has changed by calling ReloadData(). This will trigger the refresh process. UITableView will ask your source how many rows there are and because you removed one row from your data, the cell representing that row will be gone.
However ReloadData() will reload your entire table. There is a methods which allows you to remove cells specifically, named DeleteRows(). You can find an example here at Xamarin.
When deleting rows it is important that you update your model first, then update the UI.
Alternatively, I recommend you to check out MonoTouch.Dialog which allows interaction with UITableView in a more direct way. It also includes support for lazy loading images.

reloadRowsAtIndexPaths don't work in another controller

I have 2 viewControllers: in one controller i have tableView (that include 3 table view 2 popovers and one default on screen). i have 3 arrays to all tableview and in numberOfRowsInSection i return [array count];.
In second controller i have some tableview with another data. and if i select row i download some data and call method reloadRowsAtIndexPaths for tableView in first Controller.
If i was in first controller and then go to second it works. But if i at second Controller select row and it call reloadRowsAtIndexPaths i have an error:
Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-2380.17/UITableView.m:862
-[ViewController updateCell:], attempt to delete row 0 from section 0 which only contains 0 rows before the update
And my tableView is empty in first controller. For first controller i have xib but all connected. I saw that numberOfRows return 0.
Any ideas??? What can i do?
I make a check
if([self.Model.arr count]==0)
{
return;
}
So now it's work fine. If array not empty it reload row at indexpath.Thanks Meera J Pai for help!

Resources