ChikabuZ
ChikabuZ

Reputation: 10215

UIView catch touch events and pass throught

I want to implement UIView subclass that will catch all touch events (especially touchesMoved) and pass them through. Is it possible?

class PassthroughView: UIView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event)
        return view == self ? nil : view
    }

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

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("touchesMoved") //not called
        super.touchesMoved(touches, with: event)
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("touchesEnded")
        super.touchesEnded(touches, with: event)
    }
}

Upvotes: 0

Views: 1514

Answers (2)

ChikabuZ
ChikabuZ

Reputation: 10215

I use this class:

class TouchlessView: UIView {

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let buttons = findViews(subclassOf: UIButton.self)
        for button in buttons where button.frame.contains(point) {
            return true
        }
        return false
    }
}

Upvotes: 0

anon
anon

Reputation:

You can’t really do this. If your view receives touches it can’t pass it on to the view that would have received if your view isn’t there. To do this you’d basically have to reimplement the whole event routing. This would be really hard, or maybe even impossible without access to Apple internal API. But even if you got this right it could break with every iOS update.

But there is a different way to get all touch events while still having them delivered to the appropriate views. For this you create a subclass of UIApplication and override the sendEvent(_:) method. This is called for each event and dispatches it to the appropriate views. From there you can first do your special processing and then call super.sendEvent() to send it to your windows as usual.

Of course there you don’t get different calls for touches began/moved/ended, but the information is there in your event object. First you need to check the event.type property to see if you actually got a touch event. If so you can get all the touches from the event.allTouches property. For each touch you can get the phase which describes whether this touch began, moved, ended and so on.

Of course subclassing is not enough, you also have to tell the system to use your new subclass. To do this you annotate it as @objc(MyApplication) where MyApplication is the name of the class. This makes sure the Objective-C runtime can find the class by the name you gave it. Then you edit your Info.plist file and set this name for the "Principal class" key. (If you are not using the Xcode editor the key is also called NSPrincipalClass).

Upvotes: 2

Related Questions