Reputation: 13073
I'm having problems with table view cells not keeping their "selected" state when scrolling the table. Here is the relevant code:
@property (nonatomic, strong) NSIndexPath *selectedIndexPath;
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.selectedIndexPath = indexPath;
//do other stuff
}
-(UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCustomCell_iPhone* cell = [tableView dequeueReusableCellWithIdentifier:@"MyCustomCell_iPhone"];
if (cell == nil)
cell = [[[NSBundle mainBundle] loadNibNamed:@"MyCustomCell_iPhone" owner:self options:nil] objectAtIndex:0];
if ([indexPath compare: self.selectedIndexPath] == NSOrderedSame) {
[cell setSelected:YES animated:NO];
}
return cell;
}
And for the cell:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if (selected) {
self.selectedBg.hidden = NO;
}else{
self.selectedBg.hidden = YES;
}
}
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
[super setHighlighted:highlighted animated:animated];
if (highlighted) {
self.selectedBg.hidden = NO;
}else{
self.selectedBg.hidden = YES;
}
}
How can I get the selected cell to stay highlighted? If I scroll it off the screen, when it scrolls back on the screen it appears in its unselected state (with its selectedBg hidden).
EDIT: Removing the setHighlighted method from the cell fixes the issue. However that means that I get no highlighted state when pressing the table cell. I'd like to know the solution to this.
Upvotes: 22
Views: 20425
Reputation: 823
Had the same problem, selected cell's accessoryView disappeared on scroll. My co-worker found pretty hack for this issue. The reason is that in iOS 7 on touchesBegan event UITableView deselects selected cell and selects touched down cell. In iOS 6 it doesnt happen and on scroll selected cell stays selected. To get same behaviour in iOS 7 try:
Enable multiple selection in your tableView.
Go to tableView delegate method didSelectRowAtIndexPath, and deselect cell touched down with code :
NSArray *selectedRows = [tableView indexPathsForSelectedRows];
for(NSIndexPath *i in selectedRows){
if(![i isEqual:indexPath]) {
[tableView deselectRowAtIndexPath:i animated:NO];
}
}
Upvotes: 14
Reputation: 41
Do not use built-in system properties isSelected
.
You can create your own property, for example:
var isSelectedStyle = false
cell.isSelectedStyle = ....
Upvotes: 0
Reputation: 3549
Put the following code in your custom UITableViewCell
subclass:
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
guard !isSelected else { return }
super.setHighlighted(highlighted, animated: animated)
if highlighted {
// Style cell for highlighted
} else {
// Style cell for unhighlighted
}
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if selected {
// Style cell for selected
} else {
// Style cell for unselected
}
}
Explanation: Try setting breakpoints on both setHighlighted
and setSelected
. You'll find that the dequeueReusableCell
method calls setSelected
then setHighlighted
in that order to reset the new cell. So your highlighting code is blowing away the styling you did in your selection code. The non-hack fix is to avoid destroying your selected styling when setHighlighted(false, animated: false)
gets called.
Upvotes: 1
Reputation: 38634
I am using Xcode 9.0.1 and Swift 4.0. I found the following codes resolved my selection mark when cells off screen and back:
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if cell.isSelected {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
}
Upvotes: 4
Reputation: 31
Swift 3 solution, 2017.
I fixed the problem with this simple line of code:
cell.isSelected = tableView.indexPathsForSelectedRows?.contains(indexPath) ?? false
Inside the tableView(tableView:cellForRowAt indexPath:) method:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Dequeue a reusable cell
if let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellID") {
cell.isSelected = tableView.indexPathsForSelectedRows?.contains(indexPath) ?? false
// Now you can safely use cell.isSelected to configure the cell
// ...your configurations here
return cell
}
return UITableViewCell()
}
Upvotes: 1
Reputation: 6650
If you want to achieve the same thing in Swift then here is the code. By the way I am using Xcode 7.2 with Swift 2.1.
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if cell.selected == true{
cell.selected = true
cell.backgroundColor = UIColor.blackColor()
}else{
cell.backgroundColor = tableViewCellColor //Don't panic its my own custom color created for the table cells.
cell.selected = false
}
}
Do other customization what ever you want..
Thanks..
Hope this helped.
Upvotes: 1
Reputation: 455
I know my method is not very orthodox but seems to work. Here is my solution:
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if cell.selected {
cell.selected = true
} else {
cell.selected = false
}
}
You must implement all the methods you mentioned on your post as well (@soleil)
Upvotes: 7
Reputation: 12206
Posted a quick answer to that here: https://stackoverflow.com/a/35605984/3754003
In it, I also explain why this happens.
Upvotes: 0
Reputation: 61
UITableViewCell has a BOOL property "selected". Whenever you load the cell, check the state of selected and make selection or deselection accordingly as follows in cellForRowAtIndexPath definition:
if (cell.selected) {
// Maintain selected state
}
else{
// Maintain deselected state
}
Upvotes: 0
Reputation: 2188
iOS 7/8 both deselect the cell when scrolling begins (as Alexander Larionov pointed out).
A simpler solution for me was to implement this UIScrollViewDelegate method in my ViewController:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
NSInteger theRow = [self currentRowIndex]; // my own method
NSIndexPath *theIndexPath = [NSIndexPath indexPathForRow:theRow inSection:0];
[self.myTableView selectRowAtIndexPath:theIndexPath
animated:NO
scrollPosition:UITableViewScrollPositionNone];
}
This works because my viewController is the UITableView's delegate, and UITableView inherits from UIScrollView.
Upvotes: 2
Reputation: 3102
Here's the solution I came up with — and it doesn't even feel hacky.
1) Implement -scrollViewWillBeginDragging:
and -scrollViewWillEndDragging:withVelocity:targetContentOffset:
and manually highlight the cell for the selected row (if there is one) during scrolling.
Mine look like this:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollview {
self.scrollViewIsDragging = YES;
if( [self.tableView indexPathForSelectedRow] ) {
[[self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]] setHighlighted:YES];
}
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
self.scrollViewIsDragging = NO;
if( [self.tableView indexPathForSelectedRow] ) {
[[self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]] setHighlighted:NO];
}
}
The scrollViewIsDragging
property is there so that in -tableView:cellForRowAtIndexPath:
we can make sure any newly dequeued cells have the proper highlighting (e.g. if the cell for the selected row is scrolled onto screen after having been off screen). The pertinent part of that method looks like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// ... cell creation/configuration ...
if( self.scrollViewIsDragging && [[tableView indexPathForSelectedRow] isEqual:indexPath]) {
[cell setHighlighted:YES animated:NO];
}
}
…and there you have it. The cell for the selectedRow will stay highlighted during scrolling.
Upvotes: 0
Reputation: 2512
Have you tried comparing the rows of the index paths instead of the entire index path object?
if ((indexPath.row == self.selectedIndexPath.row) && (indexPath.section == self.selectedIndexPath.section)) {
[cell setSelected:YES animated:NO];
}
Upvotes: 0