Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interactively deselect selected cell with swipe back gesture

In apps such as Settings, when you tap on a cell which pushes a screen, then you swipe back from the left of the screen, you can see the deselection of the selected cell’s background color fading, and it's fully interactive - if you swipe half way then swipe back the selected background view returns to full opacity.

In my app, I haven't changed any of the default behavior, and when I swipe from the left to go back, the selected cell background color remains completely opaque until the swipe gesture is completed, and then it quickly fades to deselect it.

How can one implement the interactive deselection of cells via the swipe to go back gesture?

like image 278
Jordan H Avatar asked May 27 '14 16:05

Jordan H


2 Answers

To enable interactive deselection in iOS 11 and newer, you can use UITableViewController because it implements it for you, or you can implement it by animating the deselection alongside the transition coordinator, like so:

- (void)viewWillAppear:(BOOL)animated { 
    [super viewWillAppear:animated]; 

    NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
    if (selectedIndexPath) {
        if (self.transitionCoordinator) { 
            [self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) { 
                [self.tableView deselectRowAtIndexPath:selectedIndexPath animated:YES]; 
            } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) { 
                 if (context.cancelled) { 
                     [self.tableView selectRowAtIndexPath:selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; 
                 } 
            }]; 
        } else { 
             [self.tableView deselectRowAtIndexPath:selectedIndexPath animated:animated]; 
        }
    }
}

And in Swift:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    if let selectedIndexPath = tableView.indexPathForSelectedRow {
        if let coordinator = transitionCoordinator {
            coordinator.animate(alongsideTransition: { context in
                self.tableView.deselectRow(at: selectedIndexPath, animated: true)
            }) { context in
                if context.isCancelled {
                    self.tableView.selectRow(at: selectedIndexPath, animated: false, scrollPosition: .none)
                }
            }
        } else {
            self.tableView.deselectRow(at: selectedIndexPath, animated: animated)
        }
    }
}

A similar implementation works with UICollectionView:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    if let indexPath = collectionView.indexPathsForSelectedItems?.first {
        if let coordinator = transitionCoordinator {
            coordinator.animate(alongsideTransition: { _ in
                self.collectionView.deselectItem(at: indexPath, animated: true)
            }, completion: { context in
                if context.isCancelled {
                    self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: [])
                }
            })
        } else {
            collectionView.deselectItem(at: indexPath, animated: animated)
        }
    }
}
like image 68
Jordan H Avatar answered Nov 18 '22 21:11

Jordan H


It seems as though the clearsSelectionOnViewWillAppear might actually be getting called by viewDidAppear: rather than viewWillAppear: The change only happens once the transition is completely over and if you cancel the interactive transition it does not happen at all (if it was in viewWillAppear:, it would). This looks like a UIKit bug as the docs clearly state it should be getting called in viewWillAppear:

Put the following line of code into viewWillAppear: and you will get the exact behavior you are looking for, I just tried it. This is probably the exact behavior that property triggers, just in the wrong method.

[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
like image 13
Dima Avatar answered Nov 18 '22 21:11

Dima