Modal segue needs 2 clicks instead of one

元气小坏坏 提交于 2019-12-03 12:08:56

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.

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.)

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

Use

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

instead of

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

Then you can write any code and it works on the first click.

FYI. until now, with Xcode 8.2.1 and Swift 3.x, the issue also exists for my customized UICollectionViewCell subclass (But I suppose subclass is not needed to reproduce this issue. I have no time to test this case.).

I summarize the issue as: tapping on the previously selected cell will consume the tap event to cause deselection, instead of expectedly triggering the selection segues.

So the solution is to, either not use the selection segues, say, but use programmatically triggering segues, or deselect the cell itself whenever a selection segue is triggered.

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 {}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!