1

I'm trying to make a UIView that recognizes tap gestures, however taps are not ever correctly registered by the UIView. Here's the code for the UIView subclass itself:

import UIKit

class ActionCell: SignalTableCell, UIGestureRecognizerDelegate {

    var icon: UIImageView!
    var actionType: UILabel!
    var actionTitle: UILabel!

    var a:Action?
    var tap:UITapGestureRecognizer?

    required init(frame: CGRect) {
        //
        super.init(frame:frame)

        tap = UITapGestureRecognizer(target: self, action: #selector(self.touchTapped(_:)))
        tap?.delegate = self
        addGestureRecognizer(tap!)


    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        tap = UITapGestureRecognizer(target: self, action: #selector(self.touchTapped(_:)))
        tap?.delegate = self
        addGestureRecognizer(tap!)


    }

    @objc func touchTapped(_ sender: UITapGestureRecognizer) {
        print("OK")
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code

        self.isUserInteractionEnabled = true


    }

    override func layoutSubviews() {


        if(icon == nil) {
            let rect = CGRect(origin: CGPoint(x: 10,y :20), size: CGSize(width: 64, height: 64))
            icon = UIImageView(frame: rect)
            addSubview(icon)
        }
        icon.image = UIImage(named:(a?.icon)!)
        if(actionType == nil) {
            let rect = CGRect(origin: CGPoint(x: 100,y :20), size: CGSize(width: 200, height: 16))
            actionType = UILabel(frame: rect)
            addSubview(actionType)
        }
        actionType.text = a.type
        if(actionTitle == nil) {
            let rect = CGRect(origin: CGPoint(x: 100,y :80), size: CGSize(width: 200, height: 16))
            actionTitle = UILabel(frame: rect)
            addSubview(actionTitle)
        }
        actionTitle.text = a?.title
    }

    func configure( a:Action ) {
        self.a = a
    }

    override func setData( type:SignalData ) {

        a = (type as! Action)
    }
}

I'm simply trying to make it so that this UIView can, you know, know when it's tapped. Is this possible without adding a separate UIViewController? This seems as though it should be fairly simple but it doesn't appear to be, confusingly.

I've stepped through the code and the init method is called and the Gesture Recognizer is added but it doesn't trigger.

Joshua Noble
  • 830
  • 2
  • 12
  • 26

3 Answers3

1

If its a table view cell, I'd recommend not using a tap gesture, since it might interfere with the didSelectRowAtIndexPath: and other delegate methods. But if you still wanna keep the tap gesture, try adding tap?.cancelsTouchesInView = false before addGestureRecognizer(tap!) and see if that works.

If you just want to know when its tapped, you could also override the following UIResponder method:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)

    // do your stuff
}
Pranay
  • 846
  • 6
  • 10
  • It's just a UIView. I've given up on using tables as they can't be used inside a scrollview, even though I've tried as hard as I could figure out to have them just generate cells and not act as a scrolling component. – Joshua Noble May 15 '18 at 03:20
  • Well, did you try setting the `cancelsTouchesInView` boolean to false? Also, is the `ActionCell` a subclass of `UITableViewCell` or a `UICollectionViewCell`? If so, try adding the gesture to the content view of the cell like this: `self.contentView.addGestureRecognizer(tap!)` instead of `addGestureRecognizer(tap!)` – Pranay May 15 '18 at 16:41
  • It's not a UITableCellView, as I mentioned I had to give up on using that architecture. – Joshua Noble May 15 '18 at 16:54
  • Well, I ran out of ideas. Your code seems fine. Its probably something very trivial like this SO post: https://stackoverflow.com/questions/26028455/gesturerecognizer-not-responding-to-tap. Anyway, the only other suggestion I have is use a plain `UIButton` with no text and the frame set to your view's bounds and add a touch up inside action. – Pranay May 15 '18 at 17:19
  • Just some food for thought, do all the superviews of your `ActionCell`'s instance have `userInteractionEnabled` set to `true`? That might also be a reason why it might not recognize the gesture. – Pranay May 15 '18 at 17:29
  • Yes, I went through and unset and then reset all of them. I think the problem is that it's inside a scrollview? I cannot understand how putting components inside a scrollview so completely breaks the interaction API but it seems to be extremely difficult to capture interactions with components inside scroll views. – Joshua Noble May 15 '18 at 17:32
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/171111/discussion-between-pranay-and-joshua-noble). – Pranay May 15 '18 at 17:40
1

I think, it is easier to override touchesBegan method. Something like this:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    print("touched")
    super.touchesBegan(touches, with: event)
}
kirander
  • 2,202
  • 20
  • 18
  • Unfortunately this isn't triggered either. Is there some setup that needs to happen in order for this to be triggered? I've added self.isUserInteractionEnabled = true but that hasn't had any effect – Joshua Noble May 15 '18 at 03:17
1

Most likely the actionType, ActionTitle, and icon are being tapped and the tap is not falling through because user interaction is disabled by default for labels and images. Set isUserInteractionEnabled = true for each of those fields that are subviews of the main view.

override func layoutSubviews() {

    if(icon == nil) {
        let rect = CGRect(origin: CGPoint(x: 10,y :20), size: CGSize(width: 64, height: 64))
        icon = UIImageView(frame: rect)
        icon.isUserInteractionEnabled = true
        addSubview(icon)
    }
    icon.image = UIImage(named:(a?.icon)!)
    if(actionType == nil) {
        let rect = CGRect(origin: CGPoint(x: 100,y :20), size: CGSize(width: 200, height: 16))
        actionType = UILabel(frame: rect)
        actionType.isUserInteractionEnabled = true
        addSubview(actionType)
    }
    actionType.text = a.type
    if(actionTitle == nil) {
        let rect = CGRect(origin: CGPoint(x: 100,y :80), size: CGSize(width: 200, height: 16))
        actionTitle = UILabel(frame: rect)
        actionTitle.isUserInteractionEnabled = true
        addSubview(actionTitle)
    }
    actionTitle.text = a?.title
}
Mark Thormann
  • 2,522
  • 1
  • 21
  • 23
  • Unfortunately this doesn't seem to help. I also had suspected the UIScrollView in which these components are being generated and added, but removing everything from a scrollview doesn't help either. – Joshua Noble May 15 '18 at 03:15
  • If this is a UITableViewCell it would sit on top of the UIScrollView that the UITableView inherits from anyways so I'd expect the scroll view not to work. You might try using the [view debugger](https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/debugging_with_xcode/chapters/special_debugging_workflows.html) if you haven't already and rotating in 3D to see what else is there. – Mark Thormann May 15 '18 at 18:53
  • Just saw your note below about this not being a `UITableViewCell`. Do you have any other code or views in the base class `SignalTableCell` that would be interfering or not have interaction turned on? – Mark Thormann May 15 '18 at 18:57
  • I've tried removing everything from the SignalTableCell. The SignalTable is just a UIStackView with a bit of logic to render cells based on a data array. This seems quite simple but apparently is difficult. My SignalTable (aka UIStackView) is inside a UIViewController. That ViewController receives tap events. The SignalTable does not, nor do any of the SignalTableCells generated by the SignalTable. – Joshua Noble May 15 '18 at 21:47
  • Just another thought. Have you put a breakpoint and verified that `awakeFromNib()` is actually getting called? If you're not spinning these views up from a .xib file, you may never hit it and not be setting isUserInteractionEnabled. – Mark Thormann May 15 '18 at 22:40