I have a UITableView that has multi selection enabled. I have been using the "selection" to actually change the height of the rows, showing extra detail when "selected". E.g.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return (self.tableView.indexPathsForSelectedRows?.contains(indexPath) ?? false) ? 200 : 92
}
This seems to work pretty well. Until I start doing any swipes actions. When I add some swipe actions, the swipe action seems to clear all of my selections. I actually wanted to deselect the one I was swiping, so it would shrink back down. But the clearing of all my selections doesn't seem to trigger any of the normal delegate callbacks. Even though I have allowsMultipleSelectionDuringEditing set to true.
Is there a way to do this? Should I skip (ab)using the selection state as a way to indicate whether the row is showing details with a different height or not? Or is there a way to use it in conjunction with the behavior of the swipes being done in "edit mode" and clearing all of my selections?
The best way is using NSArray to store indexPath of selected cells, and base on saved indexPath you can check and do anything you want. another bug may happened in your code is: What happened in the case user make scroll on tableview? Does cell will reuse and lose select state? New cell reuse the old cell with 200 height will has wrong height?
Related
I'm using leadingSwipeActionsConfigurationForRowAt to detect and process swipes. However, I need to be able to detect when the user started to swipe, but before it completes. The problem is that if I update the table using reloadData() or beginUpdates() / endUpdates() while a cell is being swiped, it resets the cell to the un-swiped position (and I don't like that kind of user experience). I want to be able to delay the cell/data update until the swipe is finished or cancelled.
Even though Andreas' answer is absolutely correct, I found an easier way to do it. Apparently, whenever leadingSwipeActionsConfigurationForRowAt is used, the cell is marked as being edited whenever it starts being swiped. And that allows us to use willBeginEditingRowAt and didEndEditingRowAt on the UITableView, without having to worry about the UITableViewCell
var swipedRow: IndexPath? = nil
func tableView(_ tableView: UITableView, willBeginEditingRowAt indexPath: IndexPath) {
swipedRow = indexPath
}
func tableView(_ tableView: UITableView, didEndEditingRowAt indexPath: IndexPath?) {
swipedRow = nil
}
And then you can just check if swipedRow == nil (plus, if it's not nil, you will know the indexPath of the row being swiped)
You could implement willTransition(to:) in the UITableViewCell` to detect a swipe, see:
UITableViewCell documentation
There, just set a flag in the view controller to prevent updates.
You might also need to do something in viewWillDisappear or so, because maybe the transition callbacks are not called under some circumstances.
I have two action on a table view cell like such:
After I touch them, the actions get executed but the cell goes back to its original state (closed) like such:
How can I prevent the cell from closing and keep show the actions after the touch?
Are you using trailingSwipeActionsConfigurationForRowAt to show the options, if yes then it is the default swipe behavior of UITableViewCell. On any action on the + or - the cell will hide those options, it does not retain it's trailingSwipeActions and goes back to the normal visible cell state.
func tableView(_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration?
I am working on a ViewController with a TableView populated with dynamic cells from a prototype nib. I have run into a dead-end trying to recreate certain a cell-expanding animation.
My goal:
When a cell is selected, the selected cell should "expand" (grow in height to twice it's starting size) while at the same time creating space between the cell directly above and below the selected cell.
I've found an example of EXACTLY the asethetic I am looking for in the app "Things". Below are two screenshots showing the table before and and after a cell is selected:
Screenshot of table BEFORE expansion
Screenshot of table AFTER expansion
The best way I can describe the desired animation is the UITableView version of "the parting of the Red Sea". When a cell is selected, the surrounding cells give way lending the selected cell more room.. and the user's focus.
What I've tried:
I found Simon Lee's method answering a similar question and implemented it into my project. And although it animates the row-height change perfectly, it only pushes the adjacent cells on one side of the selected cell. (ie: if the cell at index 4 is selected, all the cells from index 5+ move down but those from index 0-3 stay static. Thereby not achieving the look I'm seeking.)
Using that method, the relevant sections of my code looked something like:
var cellHeight: CGFloat = 72
var selectedCell: IndexPath?
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath == selectedCell {
return cellHeight * 2
} else {
return cellHeight
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedCell = indexPath
tableView.beginUpdates()
tableView.endUpdates()
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
selectedCell = nil
tableView.beginUpdates()
tableView.endUpdates()
}
Because that didn't result in my desired animation, I tried insertRows:at:with: to insert 2 empty cells (1 above and 1 below the selected row) -- then deleteRows:at:with: to remove them upon deselecting the cell. This ultimately made for a better looking animation, and looked closer to the "Things" example I'm shooting for. However this made the table overly complicated because by adding and removing cells each time a row is selected, it would change the index of the other cells making it frustrating to predict which cells would have what index at any given time.
A possible solution idea?
After working on this for a couple of days the only other way I could think to accomplish what I want is to somehow scroll the table slightly at the same time that the selected cell's height it changed.. so that as the cell expands (moving the following cells downward) it would make the previous cells appear to move upward. I'm hesitant to try this because it feels like a hack, there should be a better way to accomplish this.
ANY help would be thoroughly appreciated! I've been pulling my hair out at an alarming rate. Thank you to anyone who can share their knowledge.
I am working on xamarin.ios. I have a UITableView control at my view. I select a row in it and then move to next screen. If I come back to UITableView screen again the selected row doesn't highlight. It highlight for a second and then deselect automatically. How it can be managed if I come back to the tableview, the selected row should be highlighted.
Hard to say without seeing any code, but looks like the tableview is being reloaded in viewDidAppear. You may want to store the selected row index in NSUserDefaults or somewhere else to persist the selection between view loads/appearances.
Make sure you are setting it selected after the tableview reloads as well, using an appropriate delegate method. Again, without any code to look at, it's hard to see where - and which order - you're doing this, but an example (Swift):
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == tableView.numberOfRowsInSection(0) - 1 {
cell.selected = true
}
}
Alternatively, you could set the selected property in your datasource directly, and then you could do:
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
datasource[indexPath.row].selected == true {
cell.selected = true
}
}
If you do it this way, then the selected row will always be set correctly every time the tableview is loaded.
(Full disclosure, I'm not a Xamarin dev, so I'm not sure how those translate from Swift/Obj-C to Xamarin)
I want to expand my UITableViewCells with a UITableView that has multiple sections. The way I'm doing it is as follows:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRowIndex = indexPath
habitTableView.beginUpdates()
habitTableView.endUpdates()
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if (selectedRowIndex != nil && indexPath == selectedRowIndex!){
return 147
}
return 90
}
However there are some strange behaviors, for example, if one cell expands it sort of "eats up" the next section header underneath it so the section header disappears. I am just wondering - is there any nuances with a UITableView that has multiple sections?
So, you can do one thing is to have two protocol type of UITableViewCell. One is for normal and another one is for expanded. Once you type on a cell, you just need to update the delegate to use expanded one instead normal one. When updating, you only need to call reloadRowsAtIndexPaths to prevent reload everything.
You only need to create one more cell prototype and have a boolean value for indicating the state. Then, add your logic to cellForRowAtIndexPath.