Tap Gesture Recognizer not received in custom UIView embedded in super view

回眸只為那壹抹淺笑 提交于 2021-02-08 11:26:59

问题


I am trying to create a custom UIView/Scrollview named MyScrollView that contains a few labels (UILabel), and these labels receive tap gestures/events in order to respond to user's selections .

In order to make the tap event work on the UILabels, I make sure they all have userIteractionEnabled = true and I created a delegate as below:

protocol MyScrollViewDelegate {
    func labelClicked(recognizer: UITapGestureRecognizer)
}

The custom UIView is being used in ScrollViewController that I created, this ScrollViewController implements the delegate method as well:

import UIKit
import Neon
class ScrollViewController: UIViewController, MyScrollViewDelegate {

var curQuestion: IPQuestion?
var type: QuestionViewType?
var lastClickedLabelTag: Int = 0 //

init(type: QuestionViewType, question: IPQuestion) {
    super.init(nibName: nil, bundle: nil)
    self.curQuestion = question
    self.type = type
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
    super.viewDidLoad()
    self.automaticallyAdjustsScrollViewInsets = false
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

override func loadView() {
    view = MyScrollView(delegate: self, q: curQuestion!)
    view.userInteractionEnabled = true
}
}

// implementations for MyScrollViewDelegate
extension ScrollViewController {

func labelTitleArray() -> [String]? {
    print("labelTitleArray called in implemented delegate")

    return ["Comments", "Answers"]
}

func labelClicked(recognizer: UITapGestureRecognizer) {
    print("labelClicked called in implemented delegate")
    let controller = parentViewController as? ParentViewController
    controller?.labelClicked(recognizer)
    lastClickedLabelTag = recognizer.view!.tag
    }
}

// MARK: - handle parent's ViewController event
extension QuestionDetailViewController {
    func updateActiveLabelsColor(index: Int) {
        print("updating active labels color: \(index)")
        if let view = view as? MyScrollView {
            for label in (view.titleScroll.subviews[0].subviews as? [UILabel])! {
                if label.tag == index {
                    label.transform = CGAffineTransformMakeScale(1.1,1.1)
                    label.textColor = UIColor.purpleColor()
                }
                else {
                    label.transform = CGAffineTransformMakeScale(1,1)
                    label.textColor = UIColor.blackColor()
                }
            }
        }
    }
}

This above ScrollViewController is added, as a child view controller to the parent view controller, and positioned to the top part of the parent's view:

override func viewDidLoad() {
        super.viewDidLoad()
        self.automaticallyAdjustsScrollViewInsets = false
        self.view.backgroundColor = UIColor.whiteColor()
        addChildViewController(scrollViewController) // added as a child view controller here
        view.addSubview(scrollViewController.view) // here .view is MyScrollView
        scrollViewController.view.userInteractionEnabled = true
        scrollViewController.view.anchorToEdge(.Top, padding: 0, width: view.frame.size.width, height: 100)
    }

The app can load everything up in the view, but the tap gesture/events are not passed down to the labels in the custom MyScrollView. For this, I did some google search and have read Event Delivery: Responder Chain on Apple Developer website and did a hit test as well. The hitTest function below can be triggered in the MyScrollView:

override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
        print("hit test started, point: \(point), event: \(event)")
        return self
    }

My observations with the hitTest is that the touchesBegan() and touchesEnded() methods are triggered in the view only when the hitTest function is there. Without hitTest, both functions do not get called with taps.

but no luck getting the UILabel to respond to Tap Gestures. So I am reaching out to experts on SO here. Thanks for helping!


回答1:


I think I found out the reason why the UILabel did not respond to tapping after much struggle: the .addGestureRecognizer() method to the label was run in the init() method of my custom UIView component, which is wrong, because the view/label may not have been rendered yet. Instead, I moved that code to the lifecycle method layoutSubviews(), and everything started to work well:

var lastLabel: UILabel? = nil

        for i in 0..<scrollTitleArr.count {
            let label = UILabel()
            label.text = scrollTitleArr[i] ?? "nothing"
            print("label: \(label.text)")
            label.font = UIFont(name: "System", size: 15)
            label.textColor = (i == 0) ? MaterialColor.grey.lighten2 : MaterialColor.grey.darken2
            label.transform = (i == 0) ? CGAffineTransformMakeScale(1.1, 1.1) : CGAffineTransformMakeScale(0.9, 0.9)
            label.sizeToFit()
            label.tag = i // for tracking the label by tag number
            label.userInteractionEnabled = true
            label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.labelClicked(_:))))

            titleContainer.addSubview(label)

            if lastLabel == nil {
                label.anchorInCorner(.TopLeft, xPad: 0, yPad: 0, width: 85, height: 40)
      //         label.anchorToEdge(.Left, padding: 2, width: 85, height: 40)
            } else {
                label.align(.ToTheRightMatchingTop, relativeTo: lastLabel!, padding: labelHorizontalGap, width: 85, height: 40)
            }

            lastLabel = label

        }

In addition, I don't need to implement any of the UIGestureRecognizer delegate methods and I don't need to make the container view or the scroll view userInteractionEnabled. More importantly, when embedding the custom UIView to a superview, I configured its size and set clipsToBounds = true.

I guess I should have read more UIView documentation on the Apple Developer website. Hope this will help someone like me in the future! Thanks to all!




回答2:


You have to set the property userInteractionEnabled = YES.




回答3:


For some reason, my simulator was frozen or something when the tap gesture recognizer wasn't working. So, when I restarted the app, then it all worked again. I don't know if this applies here, but that was the fix for me.



来源:https://stackoverflow.com/questions/37007655/tap-gesture-recognizer-not-received-in-custom-uiview-embedded-in-super-view

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