Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modal segue needs 2 clicks instead of one

My UITableView needs 2 clicks to show the detail page of the selected cell : one for selection and another one for show the detail view. I would like one clic to directly show the detail view of the clicked cell.

I use a modal segue with this method inside my UITableViewManager.m :

    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"showDetail"]) {
        NSIndexPath *indexPath = [playerList indexPathForSelectedRow];
        TCPlayerStat *object = _objects[indexPath.row];
        [[segue destinationViewController] setPlayerStat:object];
    }
}

I can't work with a push segue because I don't have Navigation Controller (and don't really want to).

I've looked in the TableView Attribute Inspector but didnt find out anything relevant for this. I have Selection "Single Selection" selected and "Show Selection on Touch" checked. I don't know if this is possible and if it is, where to look..

Thanks for your help.


EDIT 1: When I write the two methods like this, it still doesn't work (needs 2 clicks) and I have a new warning log:

"Warning: Attempt to present TCDetailViewController: 0xa27b900 on TCRootViewController: 0xa24f050 while a presentation is in progress!"

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self performSegueWithIdentifier:@"showDetail" sender:self];
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"showDetail"]) {
        NSIndexPath *indexPath = [playerList indexPathForSelectedRow];
        TCPlayerStat *object = _objects[indexPath.row];
        TCDetailViewController *detailViewController = [segue destinationViewController];
        [detailViewController setPlayerStat:object];
    }
}

EDIT 2: I don't know why, but sometimes it works perfectly and doesn't need a second click on the table view. Can't find out :/


Solution below

like image 788
Tulleb Avatar asked Oct 20 '13 22:10

Tulleb


4 Answers

A solution that works for me (XCode 9.2) and seems easy, but still allows for None for the Selection is :

As @Tulleb says, in the storyboard use anything but None for the Table View Cell "Selection", but then set it to .none in the code.

For example, like this...

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tv.dequeueReusableCell(withIdentifier: "cell")
    cell?.selectionStyle = .none  // This is the important line
like image 110
tom10 Avatar answered Oct 26 '22 23:10

tom10


I already had the selection attribute set to "Default" and was still experiencing the two-click problem. That suggestion did, however, point to a related solution. In didSelectRowAtIndexPath, call:

self.tableView.deselectRowAtIndexPath(indexPath, animated: false)

(I'm using Swift, obviously.)

like image 23
Scott D. Strader Avatar answered Nov 19 '22 00:11

Scott D. Strader


I fond out what the problem was!

I changed a parameter on the Storyboard that I shouldn't have. I wanted the selection cell not to display a background highlight color when I click on it, but it seems the Segue is based on it to work decently.

HOW TO FIX: In the storyboard, select your Table View Cell in the Navigator and don't chose the "None" option in "Selection" (Attributes Inspector). "Blue", "Gray" or "Default" seems to work nice.

like image 16
Tulleb Avatar answered Nov 19 '22 01:11

Tulleb


I think the more correct way to solve this problem is to perform any action on Main thread, in your case triggering the segue:

Objective-C

dispatch_async(dispatch_get_main_queue(), ^{
    [self performSegueWithIdentifier:@"showDetail" sender:self];
});

Swift

DispatchQueue.main.async {
    self.performSegue(withIdentifier: "showDetail", sender: self)
}

It's actually a bug, related to the main RunLoop and tableViewCell, while selectionStyles force RunLoop to be awake (as it has to process the animation), if you don't have selectionStyle, plus you don't do any startup animations inside your presented viewController, RunLoop will go asleep sometimes.

If you, for some reason, don't want to do the action (in your case it's triggering the segue) on main thread, or you don't have startup animation on presented ViewController, you can simply add empty main thread call at the end and it will also trigger RunLoop to be awake.

Objective-C

[self performSegueWithIdentifier:@"showDetail" sender:self];
dispatch_async(dispatch_get_main_queue(), ^{});

Swift

self.performSegue(withIdentifier: "showDetail", sender: self)
DispatchQueue.main.async {}
like image 3
sabius Avatar answered Nov 19 '22 01:11

sabius