Capturing a scroll wheel event in MacCatalyst - mousewheel

I have been able to capture the mouse scroll wheel with a UIPanGestureRecognizer.
`
let tPan = UIPanGestureRecognizer(target: self, action: #selector(self.wasPanned))
tPan.maximumNumberOfTouches = 1
tPan.minimumNumberOfTouches = 1
if #available(macCatalyst 13.4, *) {
tPan.allowedScrollTypesMask = .discrete
}
self.addGestureRecognizer(tPan)
}
#objc func wasPanned(_ pan: UIPanGestureRecognizer) {
let thispan = pan
print(pan)
}
`
However, this captures any pan action. How can I determined if this was a consequence of the mouse scroll wheel?

You can check the event passed to the delegate method gestureRecognizer:shouldReceiveEvent: and only accept the event whose type is scroll:
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive event: UIEvent) -> Bool {
event.type == .scroll
}
}
Or you can simply set the property allowedTouchTypes to an empty array, which means all touch types are ignored:
gestureRecognizer.allowedTouchTypes = []

Related

Pinch gesture does not work if view registers pan gesture

I have an app that manipulates view with gestures. View is connected to pan, pinch and rotate gestures. If I start interacting with the view with two fingers all gestures are working simultaneously (expected behavior). But if you start with one finger pan, pinch and rotate gestures do not work. None of the methods in the delegate are called when I am trying to start pinch or rotate while panning.
shouldRecognizeSimultaneouslyWith
is always true.
Expected behavior,
one finger pan on the view
add second finger and start pinch/rotate interaction
(similar to instagram stories editor)
From a clean project I added the following:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.addGestureRecognizer({
let gesture = UIPanGestureRecognizer(target: self, action: #selector(onPan))
gesture.delegate = self
return gesture
}())
view.addGestureRecognizer({
let gesture = UIPinchGestureRecognizer(target: self, action: #selector(onPinch))
gesture.delegate = self
return gesture
}())
view.addGestureRecognizer({
let gesture = UIRotationGestureRecognizer(target: self, action: #selector(onRotate))
gesture.delegate = self
return gesture
}())
}
#objc private func onPan(_ sender: UIPanGestureRecognizer) {
print("Did pan to \(sender.translation(in: sender.view))")
}
#objc private func onPinch(_ sender: UIPinchGestureRecognizer) {
print("Did pinch to \(sender.scale)")
}
#objc private func onRotate(_ sender: UIRotationGestureRecognizer) {
print("Did rotate to: \(sender.rotation)")
}
}
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { true }
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { true }
}
All 3 methods report changes after the scenario you described:
I started with single touch drag gesture
I pressed another finger and started rotating gesture and pinching gesture
Does already this example not work for you? What is the behavior you are experiencing and what is expected behavior?

Programmatically made buttons conflict with viewController’s gesture recognizers

Referring to my last question:
Spritekit: passing from UIButtons to buttons as SKSpriteNode
I’m working on a SpriteKit game: the user can tap and swipe on the screen to move sprites inside the scene and I added gesture recognizers in my ViewController for that.
Then I create a HUD to keep 4 buttons programmatically made with which the user could add other sprites to the scene.
I want my buttons fade and scale a little when pressed and then turn back to the original state, but it seems that they conflict with viewController’s gesture recognizers: buttons fade and scale down, but they stay in that state, don’t go back to normal state.
What can I do?
This is the Button class:
import SpriteKit
protocol ButtonDelegate: NSObjectProtocol {
func buttonClicked(sender: Button)
}
class Button: SKSpriteNode {
weak var delegate: ButtonDelegate!
var buttonTexture = SKTexture()
init(name: String) {
buttonTexture = SKTexture(imageNamed: name)
super.init(texture: buttonTexture, color: .clear, size: buttonTexture.size())
self.isUserInteractionEnabled = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var touchEndedCallback: (() -> Void)?
weak var currentTouch: UITouch?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isUserInteractionEnabled {
setScale(0.9)
self.alpha = 0.5
if let currentTouch = touches.first {
let touchLocation = currentTouch.location(in: self)
for node in self.nodes(at: touchLocation) {
delegate?.buttonClicked(sender: self)
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
setScale(1.0)
self.alpha = 1.0
touchEndedCallback?()
print("tapped!")
}
}
This is the code I used in View Controller:
class ViewController: UIViewController, UIGestureRecognizerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as! SKView
skView.isMultipleTouchEnabled = false
skView.presentScene(scene)
#IBAction func didTap(_ sender: UITapGestureRecognizer) {
game.myCode
}
#IBAction func didPan(_ sender: UIPanGestureRecognizer) {
let currentPoint = sender.translation(in: self.view)
if let originalPoint = panPointReference {
if abs(currentPoint.x - originalPoint.x) > (SquareSize * 0.9) {
if sender.velocity(in: self.view).x > CGFloat(0) {
//game.myCode
panPointReference = currentPoint
} else {
//game.myCode
panPointReference = currentPoint
}
}
} else if sender.state == .began {
panPointReference = currentPoint
}
}
#IBAction func didSwipe(_ sender: UISwipeGestureRecognizer) {
//game.myCode
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive shouldReceiveTouch: UITouch) -> Bool {
if UITouch .isKind(of: Button.self) {
return false
}
return true
}
func buttonClicked(sender: Button) {
//myCode
}
}
SOLVED IT!
Ok, it was simpler than I think.
(I took a break).
- I deleted UIgestures and added them programmatically;
- in my GameScene’s touchesEnded method I created a conditional to check which one of my node has been touched.
- in View Controller I add the gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) method, to avoid conflicts with gesture recognizers attached to different view (my nodes are in two different views):
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer.view != otherGestureRecognizer.view {
return false
}
return true
}
I hope this will help…
I don't think they are interfering with the inbuilt gesture recognizers of the View Controller as you are not adding any gestures of your own, you are just overriding touchesBegin and touchesEnded.
Does "tapped" get printed out?
It is possible that touchesEnd() is not getting called, try implementing touchesCancelled() as well and see if that gets called.

Detect any tap outside the current view

Is there a way to detect any tap outside the current view? I unsuccessfully tried to implement something with the hitTest method but I am not sure to understand it well.
What you have to do is, In touchesBegan you have to get the first touch object from the touches set and you have to check the location of that touch in a view(inside which you want to detect the touch).
After you get the location of touch in View, you have to check whether your currentView(The view which you have to check whether tap was inside or outside).
If currentView's frame contains the touch location, that means touch has occurred inside the view otherwise outside.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
let touch = touches.first
guard let location = touch?.location(in: self.view) else { return }
if !currentView.frame.contains(location) {
print("Tapped outside the view")
} else {
print("Tapped inside the view")
}
}
Hope it Helps!
You can use UIGestureRecognizerDelegate protocol
extension YourViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldReceive touch: UITouch) -> Bool {
return (touch.view === self.view)
}
}
This will only returns "true" when the touch was on the background view but "false" if it was inside the your View.
Note : The identity operator === to compare touch.view with self.view. You want to know whether both variables refer to the same object.
And in viewDidLoad() you will create the gestureRecognizer and set delegate.
let gestureRecognizer = UITapGestureRecognizer(target: self,action: #selector(yourActionMethod))
gestureRecognizer.cancelsTouchesInView = false
gestureRecognizer.delegate = self
view.addGestureRecognizer(gestureRecognizer)

How to detect the location of my finger through in a scrollview or webview in Swift? (using touchesMoved if possible)

I want to print the location of the user's finger on a webview.
Using this (below) doesn't work on a webview
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self.view) // even when i replace this part with UIWebview
print(position.x)
print(position.y)
}
}
I think that creating a UIPanGestureRecognizer should solve it, add the following code into viewDidLoad() method:
override func viewDidLoad() {
// ...
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(webViewtouchMoved(panGesture:)))
panGesture.delegate = self
webView.addGestureRecognizer(panGesture)
// ...
}
webViewtouchMoved(panGesture:) method:
func webViewtouchMoved(panGesture: UIPanGestureRecognizer) {
if panGesture.state == .began || panGesture.state == .changed {
let position = panGesture.location(in: view)
print(position.x)
print(position.y)
}
}
also, you should add this extension for the desired ViewController:
// change 'ViewController' to your class name:
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
hope this helped.

Google login iOS - "My GIDSignInButton only works in long press"

The google login button provided by google (GIDSignInButton), is not working in normal press but in long press. Otherwise evrything is normal.
Any idea guys?
That was due to a tap recognizer I had in the same viewcontroller. Issue got solved.
Google sign in default button not works in single tap, it works after 1
long press because of the Tap Gesture included in same
viewcontroller...
So the Solution is Handle touch event in sameViewcontroller:
override func viewDidLoad() {
super.viewDidLoad()
let touchRecognizer = UITapGestureRecognizer(target: self, action:
#selector(onBaseTapOnly))
touchRecognizer.numberOfTouchesRequired = 1
touchRecognizer.delegate = self
self.view.addGestureRecognizer(touchRecognizer)
}
func onBaseTapOnly(sender: UITapGestureRecognizer) {
if sender.state == .ended {
//react to tap
self.view.endEditing(true)
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldReceive touch: UITouch) -> Bool {
return touch.view == gestureRecognizer.view
}

Resources