sudo
sudo

Reputation: 1658

Disable multiple taps on uitableviewcell

I have a uitableview that implements a popover (PopoverView) when a cell is tapped and then the popover will dismiss on any other tap on the screen. The issue is that if a user would to double tap or tap repeatedly on the cell, it will cause multiple instances of popoverviews to display and then the application will crash.. I am looking for a way to either disable double tapping on the cell and/or the UITableView in general OR is there a way to delay touches on a UITableViewCell any ideas?

I already tried this but it does not work in my case. Another approach would to be to check if PopoverView is already present, if so then don't allow another one to instantiate. I tried this and this and both do not work in my case.

Here is my code where I call the popover view on didSelectRowAtIndexpath:

- (void)tableView:(UITableView *)TableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
   UITableViewCell *cell = [TableView cellForRowAtIndexPath:indexPath];
sti = [[SelectedTeamsInfo alloc] init];
MyLeagueStandings *info = [fetchedResultsController objectAtIndexPath:indexPath];
[sti getAllScheduleForTeam:info.urlforteam];
NSString *title = info.teamname;

// If title length is greater then 32 truncate it to fit.
if (title.length > 32) {
    title = [info.teamname substringToIndex:29];
    title = [title stringByAppendingString:@"..."];
}


[PopoverView showPopoverAtPoint:cell.center inView:self.view withTitle:title withContentView:sti.view delegate:self];
}

SOLUTION:

In interface class:

 BOOL PopoverYN;

In Implementation class:

- (void)tableView:(UITableView *)TableView 
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        // If the popover is not available then display it else do nothing since one is already displayed.
        if (PopoverYN == NO) {
            PopoverYN = YES;
            UITableViewCell *cell = [TableView cellForRowAtIndexPath:indexPath];
            sti = [[SelectedTeamsInfo alloc] init];
            MyLeagueStandings *info = [fetchedResultsController objectAtIndexPath:indexPath];
            [sti getAllScheduleForTeam:info.urlforteam];
            NSString *title = info.teamname;

            // If title length is greater then 32 truncate it to fit.
            if (title.length > 32) {
                title = [info.teamname substringToIndex:29];
                title = [title stringByAppendingString:@"..."];
            }
            [PopoverView showPopoverAtPoint:cell.center inView:self.view withTitle:title withContentView:sti.view delegate:self];
        }

}

#pragma mark - popover methods.
- (void)popoverViewDidDismiss:(PopoverView *)popoverView;
{
    PopoverYN = NO;
}

Upvotes: 5

Views: 3764

Answers (3)

Lance Samaria
Lance Samaria

Reputation: 19622

Here's the Swift version of @mask answer but my withTimeInterval time duration worked better with 2 secs instead of 1.

It should be noted that even though this timer doesn't repeat it was causing a problem after I tapped a cell, the new vc got pushed on, I returned to the parent (the one with the timer), then switched tabs. The tab was frozen. After several tries of commenting and uncommenting out the timer I realized it was the timer.

To fix I just cancelled the timer in viewWillDisappear:

var avoidDoubleTapTimer: Timer?

@objc func cancelDoubleTapTimer() {
    avoidDoubleTapTimer = nil
}

// use it for a cell
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    if avoidDoubleTapTimer == nil {

        avoidDoubleTapTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false, block: { [weak self](_) in
            self?.cancelDoubleTapTimer()
        })
    } else {

        return
    }

    // run your code, do whatever ...
}

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

    cancelDoubleTapTimer()
}

// or use it for a tapGestureRecognizer

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(labelWasTapped))
yourLabel.addGestureRecognizer(tapGesture)

@objc func labelWasTapped() {

    if avoidDoubleTapTimer == nil {

        avoidDoubleTapTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false, block: { [weak self](_) in
            self?.cancelDoubleTapTimer()
        })

    } else {

        return
    }

    // run your code, do whatever ...
}

Upvotes: 0

mask
mask

Reputation: 6232

I have one more solution. Hope this will help someone. If you want to detect the second tap and consume it then this is it, this worked for me. I was loading web view on single tap, if there are two consecutive taps, the bug was getting NSURLErrorCancelled event and was causing white flash screen on webview. I could handle it at web view level but I though I should kill the problem at root.

NSTimer *avoidDoubleTapTimer;

- (void) onTimer {  
    NSLog(@"Timer out");
    avoidDoubleTapTimer = nil;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(avoidDoubleTapTimer == nil) {
        avoidDoubleTapTimer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(onTimer) userInfo:nil repeats:NO];
    } else {
        NSLog(@"Double Tap Detected");
        return;
    }
// do you stuff on single tap
}

Upvotes: 4

KHansenSF
KHansenSF

Reputation: 604

Attach the popover to a property on that view. Clear that when it is dismissed (via delegate method). In didSelectRowAtIndexPath don't create another popover if the first has not been dismissed.

Upvotes: 0

Related Questions