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.
Related
I'm getting weird behaviour in my TableView
I turned isUserInteractionEnabled on one of my section headers because I need to put a collectionView in it. Before I did anything I noticed that when I tap on that header view (it is a UITableViewCell) it triggers the didSelectRowAt method with the index of a first cell in that section (right below the header). Does anybody know what causes that behaviour and how to turn it off?
Your header sections are part of your UITableView.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
}
In this method indexPath return rows and section, indexPath.section
-> returns section of UitableView, you have tapped and indexPath.row -> returns down of that particular section, tapped.
So this function is going to get called no matter what you do. You
need to override the table view's gesture to avoid this.
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 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?
I have an UITableView with custom cells. I would like that the first row is always visible. As I have only once section, I thought of making a header but in this case I don't really know how to do it?
Is it possible to make a header from the first row with the same gesture recognizers, same dataSource behind the rows, briefly, have the header exactly like th row, just as if the row was pined to the top of the tableView?
You should use a header, or a separate view outside the table view. You can use the same gestures (generally, though not the delete) and data source.
If you want it all, you could use 2 table views- the first with one section and one row, the second with all the other data. It would be easiest if your data source was broken down in a similar way in the view controller.
In either case you can achieve what you want, but not by flicking a switch, you will need to add some logic and different views to make it happen.
If you want to make one static cell that is pinned to the top but in all other ways the same to the others, you could simply add one to your numberOfRowsInSection
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count + 1
}
Then when you display the cells, check for the row number and always set the first row to contain your static header content.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.row == 0 {
// Create or set static cell content.
}
}
The other way is to create a custom section header and set it using viewForHeaderInSection
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
var view = UIView()
view.backgroundColor = UIColor.blueColor()
return view
}
return nil
}
So I have a UITableView which I plan to use 2 prototype cells inside of it. Let's call them Cell A and Cell B. Cell A has it's own layout and Cell B has it's own layout.
Here's the thing, in a typical UITableView implementation with only 1 prototype cell, after setting up all the cell and it's properties, cellForRowAtIndexPath takes care of populating all the rows based on the (x) number of items from numberOfRowsInSection.
Here is where I am having a problem. I've implemented both my prototype cells in my UITableView and when I run it, I notice cellForRowAtIndexPath is only being called twice, even though I have a value in (x) number of items which is greater than 2. Doesn't matter what I set it to, it only gets called twice. I already have the necessary if statements to pick a cell prototype based on the cell index etc...so that's not the issue. The issue is cellForRowAtIndexPath just gets called twice instead of looping thru all the items.
Why is this and how can I fix it?
This is my code for the DataSource methods:
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 8
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
Scripts.log("Data Count = \(indexPath.row)")
if indexPath.row == 0{
var cell: ContactDetailImageCell = tableView.dequeueReusableCellWithIdentifier(NAME_OF_CUSTOM_IMAGE_CELL) as ContactDetailImageCell
cell.cardPhoto.image = aContact.profilePicture
cell.fullName.text = aContact.getDisplayName()
cell.workplace.text = aContact.workplace
return cell
}
else{
var cell: ContactDetailPhoneNumCell = tableView.dequeueReusableCellWithIdentifier(NAME_OF_CUSTOM_PHONE_CELL) as ContactDetailPhoneNumCell
return cell
}
return UITableViewCell()
}
What height are your cells? cellForRowAtIndexPath will only get called if the table thinks it needs to display the cell. Hence the whole reuse mechanism. So if you have 8 cells and it thinks 2 fill the screen it will not ask for any more until you scroll up/down.
What are you returning for heightForRowAtIndexPath.