I have storyboard segue on TableViewCell, which I use for transfering to another VC on cell click in didSelectRowAt
method. Now I made double tap TapGestureRecognizer
to handle touble tap on cell. Problem is that on single tap, segue is performing and double tap is not working. Double tap works fine with clicking out of cell. Is possible to solve this somehow with my code so far? Or I need to delete segue and handle single tap and double tap separately.
Thanks for any suggestions
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap))
doubleTap.numberOfTapsRequired = 2
view.addGestureRecognizer(doubleTap)
}
func handleDoubleTap(recognizer: UIGestureRecognizer) {
let p = recognizer.location(in: tableView)
let indexPath = tableView.indexPathForRow(at: p)
if let _ = indexPath {
tableView.deselectRow(at: indexPath!, animated: true)
update(index: (indexPath?.row)!, isFinished: true)
}
print ("doubke")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "showSingleTask") {
if let indexPath = tableView.indexPathForSelectedRow {
let nav = segue.destination as! UINavigationController
let destinationVC = nav.topViewController as! ShowTaskVC
destinationVC.singleTask = tasks[indexPath.row]
}
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath as IndexPath, animated: true)
self.selectedTask = tasks[indexPath.row]
}
protocol MultiTappableDelegate: class {
func singleTapDetected(in view: MultiTappable)
func doubleTapDetected(in view: MultiTappable)
}
class ThreadSafeValue<T> {
private var _value: T
private lazy var semaphore = DispatchSemaphore(value: 1)
init(value: T) { _value = value }
var value: T {
get {
semaphore.signal(); defer { semaphore.wait() }
return _value
}
set(value) {
semaphore.signal(); defer { semaphore.wait() }
_value = value
}
}
}
protocol MultiTappable: UIView {
var multiTapDelegate: MultiTappableDelegate? { get set }
var tapCounter: ThreadSafeValue<Int> { get set }
}
extension MultiTappable {
func initMultiTap() {
if let delegate = self as? MultiTappableDelegate { multiTapDelegate = delegate }
let tap = UITapGestureRecognizer(target: self, action: #selector(UIView.multitapActionHandler))
addGestureRecognizer(tap)
}
func multitapAction() {
if tapCounter.value == 0 {
DispatchQueue.global(qos: .utility).async {
usleep(250_000)
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
if self.tapCounter.value > 1 {
self.multiTapDelegate?.doubleTapDetected(in: self)
} else {
self.multiTapDelegate?.singleTapDetected(in: self)
}
self.tapCounter.value = 0
}
}
}
tapCounter.value += 1
}
}
private extension UIView {
@objc func multitapActionHandler() {
if let tappable = self as? MultiTappable { tappable.multitapAction() }
}
}
class MyView: UIView, MultiTappable {
weak var multiTapDelegate: MultiTappableDelegate?
lazy var tapCounter = ThreadSafeValue(value: 0)
override func awakeFromNib() {
super.awakeFromNib()
initMultiTap()
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.tableFooterView = UIView()
}
}
extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int { return 1 }
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10 }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
cell.label.text = "\(indexPath)"
cell.delegate = self
return cell
}
}
extension ViewController: TableViewCellDelegate {
func singleTapDetected(in cell: TableViewCell) {
if let indexPath = tableView.indexPath(for: cell) { print("singleTap \(indexPath) ") }
}
func doubleTapDetected(in cell: TableViewCell) {
if let indexPath = tableView.indexPath(for: cell) { print("doubleTap \(indexPath) ") }
}
}
TableViewCell.swift
import UIKit
protocol TableViewCellDelegate: class {
func singleTapDetected(in cell: TableViewCell)
func doubleTapDetected(in cell: TableViewCell)
}
class TableViewCell: UITableViewCell, MultiTappable {
weak var multiTapDelegate: MultiTappableDelegate?
lazy var tapCounter = ThreadSafeValue(value: 0)
@IBOutlet weak var label: UILabel!
weak var delegate: TableViewCellDelegate?
override func awakeFromNib() {
super.awakeFromNib()
initMultiTap()
}
}
extension TableViewCell: MultiTappableDelegate {
func singleTapDetected(in view: MultiTappable) { self.delegate?.singleTapDetected(in: self) }
func doubleTapDetected(in view: MultiTappable) { self.delegate?.doubleTapDetected(in: self) }
}
Main.storyboard
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12118" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="g2V-T0-sqD">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12086"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="stackoverflow_43153530" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="fQm-mQ-a9u">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="TableViewCell" id="nsF-ue-0bK" customClass="TableViewCell" customModule="stackoverflow_43153530" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="nsF-ue-0bK" id="pT6-2N-oTC">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fEK-J3-oqH">
<rect key="frame" x="8" y="8" width="42" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="fEK-J3-oqH" firstAttribute="leading" secondItem="pT6-2N-oTC" secondAttribute="leadingMargin" id="Vfg-Ij-f6c"/>
<constraint firstItem="fEK-J3-oqH" firstAttribute="top" secondItem="pT6-2N-oTC" secondAttribute="topMargin" id="tc0-qJ-N1n"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="label" destination="fEK-J3-oqH" id="YBJ-tG-J5T"/>
</connections>
</tableViewCell>
</prototypes>
</tableView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="fQm-mQ-a9u" firstAttribute="bottom" secondItem="wfy-db-euE" secondAttribute="top" id="8Vy-l8-jpB"/>
<constraint firstItem="fQm-mQ-a9u" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="Wwr-ox-Qbd"/>
<constraint firstItem="fQm-mQ-a9u" firstAttribute="top" secondItem="y3c-jy-aDJ" secondAttribute="bottom" constant="-64" id="xJR-Uk-rbj"/>
<constraint firstAttribute="trailing" secondItem="fQm-mQ-a9u" secondAttribute="trailing" id="zxs-ED-Whb"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="pLJ-Bz-NIm"/>
<connections>
<outlet property="tableView" destination="fQm-mQ-a9u" id="DhZ-jj-zmB"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1079.2" y="137.18140929535232"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="w7e-Wj-oUR">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="g2V-T0-sqD" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="7qG-8v-S0O">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="yqZ-pK-Yf3"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="tnz-x0-vDN" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="140" y="137.18140929535232"/>
</scene>
</scenes>
</document>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With