I have a UITableView that sometimes requires you touch it twice to select a cell.
More specifics:
Two touches are needed only after the table has been scrolled all the way up or all the way down.
Only the second touch even calls didSelectRowAtIndexPath.
When the table opens in the natural "scrolled up position", cells are indeed selectable with just one touch.
If you scroll just a little bit (not all the way down/up), the cells will select with just one touch.
If cells do not fill the whole table and scrolling is not required, it works fine.
Go all the way to the top or bottom and you have to touch twice.
I have a feeling that the first touch is really making the UITableViewCells selectable or is activating the table in some way.
Things I have checked:
My code definitely doesn't call didDeselectRowAtIndexPath anywhere.
No UIGestureRecognizers are using setCancelsTouchesInView:.
Other settings on the table:
self.tableView.scrollEnabled = YES;
self.tableView.showsVerticalScrollIndicator = NO;
self.tableView.bounces = NO;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
What's causing this?
Update
Oddly enough, setting self.tableView.bounces = YES; fixed the problem.
I am still looking into the root cause in case anyone has a better answer. Obviously I would like for the table not to bounce, but not if it costs key functionality.
May be you implemented didDeselect instead of didSelect?
Swift:
Once a cell gets tapped, it also gets selected, try deselecting it as soon as it was tapped.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
}
Other things to check also are delaysContentTouches and canCancelContentTouches properties.
Swift 4
You need to surround your updates with beginUpdates() and endUpdates()
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// get current cell
let cell = tableView.cellForRowAtIndexPath(indexPath) as! YourCustomCell
// change the value
// Update the tableView
tableView.beginUpdates()
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
tableView.endUpdates()
}
Maybe you should try to disable delaysContentTouches:
tableView.delaysContentTouches = NO;
Related
I have a UITableView on top of a MKMapView. I want to have similar functionality to how Apple's Maps app works. Where you can move a table up and down but still be able to move the map that is behind it. Right now I have a blank cell at index 0 that is clear and I want to be able to disable all touches that that cell receives and allows the map behind it to move, but when the cell in index 1 is touched that cell can scroll up and the rest of the table is now on top of the map.
If there is a better way to solve this problem I am up to try your solution!
func tableView(_ tableView: UITableView, didSelectRowAt
indexPath: IndexPath){
//UITableViewCell *cell = something
if indexPath.row == 0 {
cell.selectionStyle = .none
cell.isUserInteractionEnabled = false
}
}
What this code does is it makes sure that the cell is not highlighted when the user taps it. And then the second line in the if is to make sure that tableView:didSelectRowAtIndexPath: is not called which will be the case if the line is left out, and even though selectionStyle is none!
I am having a little trouble with buttons on a tableview.
I have a tableViewCell that I customised with 3 buttons. I set the buttons to hidden in interface builder and when the table loads the buttons are hidden as expected.
I then set the hidden property of the tableview to false when didSelectRow is called and hidden.true when didDeselectRow is called. This works fine as well. The problem is the buttons that are set to visible in the didSelectRow are also visible every seven cells down. they keep repeating themselves.
Below is the code that shows the buttons
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! ContactsViewCell
print("Table selected")
cell.insertEmailButton.hidden = false
cell.insertPhoneButton.hidden = false
cell.insertAllButton.hidden = false
cell.contactTextLabel.alpha = 0.2
cell.contactDetailTextLabel.alpha = 0.2
}
And this hides them when the tableViewCell is deselected
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! ContactsViewCell
cell.insertEmailButton.hidden = true
cell.insertPhoneButton.hidden = true
cell.insertAllButton.hidden = true
cell.contactTextLabel.alpha = 1.0
cell.contactDetailTextLabel.alpha = 1.0
}
I did some research and I learnt it might be the row with the buttons.hidden set to false are being reused by the tableview. But I understand from documentation that the cell being reused is from cellForRowAtIndexPath and not the cell at didSelectRow which is where I am setting the button.hidden to false.
I also tried using the cell.isSelected property in an if else statement in the cellForRowAtIndexPath to hide and show the buttons but this does not show the buttons at all.
Thanks in advance for your help
The tableview reuses the view of the cell when the table is scrolled, to save memory. So, for example, when you set the button to visible (inside didSelectRow) and then scroll down the table, the tableview will take the cells that go out of the visible screen at the top and will reuse them at the bottom, to save the overhead of creating new cells, improving performance.
That is why, your previous properties on the cells are repeating.
To get the desired hidden button on scrolled cells, I recommend setting button.hidden to true/false in
tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
This will set the button to hidden whenever a new row is scrolled into the visible view area.
Hope this helps.
I solved the recurring buttons by hiding them when I check if the cell is deselected in cellForRowAtIndexPath. This also means any cell I select will lose its selected status and buttons will disappear when it leaves the view.
I can live with that.
if cell.selected == false{
cell.emailButton.hidden = true
cell.phoneButton.hidden = true
cell.allButton.hidden = true
}
UITableView reuses its cell to improve performance. So, you can not do the way you are trying. What we have to do is, like other tableview cell info e.g. title, description, thumb image etc we also need to save the state for buttons in the array. When you want to hide a button for the cell take object at index from the array and change the button state for the button and reload that table view cell. Still if you face problem or feel difficult to understand, please feel free to ask.
I have created a custom UITableViewCell.I have only two rows in my tableView. Now I want to disable 2nd cell when 1st is tapped and disable 1st cell when 2nd is tapped.
How can I do that ? please Help and guide
Implement the UITableViewDelegate method willSelectRowAtIndexPath.
In that method, return the indexPath value that's passed to you if you want the user to be able to select it, or return nil if you don't want it to be selectable.
I'll leave it to you to figure out the logic that decides when different cells should/should not be selectable.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.cellForRowAtIndexPath(NSIndexPath(forRow: (indexPath.row + 1) % 2, inSection: 0))?.userInteractionEnabled = false
}
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 a UITableView with a custom cell, which has a few labels in it that dynamically decide the height of the cell. When I tap on one cell and segue to a new view controller, upon returning all the formatting for the cells is completely messed up, and I can't figure out what is causing it.
Here is what the cells normally look like:
And I have some pretty basic constraints set on them. The top label is pinned to the top and left margins, and must always be >= 20 from the right. The other labels are aligned to the left of this first label, with vertical spacing set between all of them. The middle label has a right spacing constraint to the margin, and the bottom labels are aligned to the baseline of the first and have horizontal spacing between all of them.
When I segue back to this table view it looks like this however:
I can't figure out what is causing it to layout differently than when I left. If I scroll around it seems to "reset" them back to what they should be, but on initial load they're really messed up. I can attach the project if desired, but there's really not much outside of the Storyboard.
cellForRowAtIndexPath:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomTableViewCell
let object = objects[indexPath.row]
cell.title1.text = object.name
cell.title2.text = object.color
cell.title3.text = object.roar
return cell
}
Sample project: http://cl.ly/040L2z0q0V2d
It appears that the table view cells aren't resizing based on the contents when returning from the segue. Using the sample project, I threw a reload data in the viewWillAppear and that seemed to fix the issue.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.tableView.reloadData()
}
There are actually couple of issues with your project.
Data Loading and AutoLayout.
The first one is causing a strange behavior at the time of drawing the cells with data. When unwinding from the segue you'll see those additional cells on top of your table caused by ambiguous layout calculation.
Solution: Move the data into override func viewWillAppear(animated: Bool) { and perform a tableView.reloadData() (as correctly suggested by #rFessler).
On the other hand, Autolayout is a kind of fiery beast. Tamable. It's worth investigating the topic further. I wasn't able to make your layout work with autosizing cell height but I'll leave few references and the project for you.
References:
http://www.appcoda.com/self-sizing-cells/
http://captechconsulting.com/blog/tyler-tillage/ios-8-tutorial-series-auto-sizing-table-cells
Project:
http://cl.ly/3z3a2Z3a3U2K
I've had a similar problem myself. I downloaded your project and it seems I've solved it by removing and tweaking some constraints. This is how my constraints look now:
Also I've added this to viewDidLoad:
self.tableView.estimatedRowHeight = 120
self.tableView.rowHeight = UITableViewAutomaticDimension
I also added this to test delete:
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete
{
self.objects.removeAtIndex(indexPath.row)
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
}
Now you can even rotate the device and remove rows and it's all working splendid!
However, there's still problem if you push this view on a Navigation Controller (Which is what my problem was about in the beginning). See my storyboard below to get some funky labels:
To solve this, it seems we actually have to do a hack! (Damn you apple, what is going on with this?!)
var firstAppearance=true
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if firstAppearance
{
if let indexPaths = self.tableView.indexPathsForVisibleRows()
{
self.tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None)
self.firstAppearance = false
}
}
}
At the moment, I think this is as good as it gets.
I played with this and find a simple solution, add this seems to fix the problem.
override func viewWillDisappear(animated:Bool) {
super.viewWillDisappear(animated)
self.tableView.estimatedRowHeight = 166.0
}
Since the the method tableView:estimatedHeightForRowAtIndexPath will be called every time you segue to a new MVC, and change the autolayout, you can just do
override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
to reuse the autolayout