Single and double taps on UITableViewCell in Swift 3

非 Y 不嫁゛ 提交于 2019-12-06 11:21:51

问题


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]
}

回答1:


Details

  • Xcode 10.2.1 (10E1001), Swift 5

Solution

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() }
    }
}

Usage

class MyView: UIView, MultiTappable {
    weak var multiTapDelegate: MultiTappableDelegate?
    lazy var tapCounter = ThreadSafeValue(value: 0)
    override func awakeFromNib() {
        super.awakeFromNib()
        initMultiTap()
    }
}

Full sample

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>

Result



来源:https://stackoverflow.com/questions/43153530/single-and-double-taps-on-uitableviewcell-in-swift-3

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