iOS swift double tap handle button and map - swift

I had mapbox map and few buttons on it. Problem is when user double tap any button maps zoom in. How to disable this? Even when user double tap zoom out, map is zoomed in and after that zoomed out. I try but in iOS I don't know how, for android I know the solution. Below code doesn't work when double tap button map is zoomed in and also button delegate double tap ...
let singleTap4 = UITapGestureRecognizer(target: self, action: #selector(MainViewController.tapPatrolaBtn))
singleTap4.numberOfTapsRequired = 1
singleTap4.numberOfTouchesRequired = 1
patrolaButton.isUserInteractionEnabled = true
patrolaButton.addGestureRecognizer(singleTap4)
patrolaButton.addTarget(self, action: #selector(multipleTap(_:event:)), for: UIControlEvents.touchDownRepeat)
#objc func multipleTap(_ sender: UIButton, event: UIEvent) {
let touch: UITouch = event.allTouches!.first!
if (touch.tapCount == 2) {
}
}

Here you need to handle double tap gesture too.
If you are using double tap gesture recogniser then double tapevent will not be delegate to mapbox map and mapbox zoom event wont call
Use below code for same:-
button.addTarget(self, action: #selector(multipleTap(_:event:)), for: UIControlEvents.touchDownRepeat)
//Don't do in action
func multipleTap(_ sender: UIButton, event: UIEvent) {
let touch: UITouch = event.allTouches!.first!
if (touch.tapCount == 2) {
}
}

I find solution, on button click I run this code so it disable map gesture for a while :
mapView.isZoomEnabled = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
self.mapView.isZoomEnabled = true }

Related

How would I make a UiPanGestureRecognizer check if the finger is in the buttons frame?

I am trying to make an app where the user could drag a finger on top of multiple buttons and get some actions for each button.
There was a similar question from a while back but when I tried to use CGRectContainsPoint(button.frame, point) it said it was replaced with button.frame.contains(point) but this didn’t seem to work for me. Here is a link to the Photo
What I have done so far:
var buttonArray:NSMutableArray!
override func viewDidLoad()
{
super.viewDidLoad()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureMethod(_:)))
a1.addGestureRecognizer(panGesture)
a2.addGestureRecognizer(panGesture)
}
#objc func panGestureMethod(_ gesture: UIPanGestureRecognizer) {
if gesture.state == UIGestureRecognizer.State.began {
buttonArray = NSMutableArray()
}
let pointInView = gesture.location(in: gesture.view)
if !buttonArray.contains(a1!) && a1.frame.contains(pointInView) {
buttonArray.add(a1!)
a1Tapped(a1)
}
else if !buttonArray.contains(a2!) && a2.frame.contains(pointInView) {
buttonArray.add(a2!)
a2Tapped(a2)
}
The code did run fine but when I tried to activate the drag nothing happened. Any tips?
You want behavior similar to the system keyboard I assume? CGRectContainsPoint is not deprecated: See the docs. In Swift it's written like frame.contains().
When dealing with rects and points you have to make sure both are translated to the same coordinate system first. To do so you can use the convert to/from methods on UIView: See (the docs).
In your case smth. like the following should work (first translate button frames, then check if the point is inside):
func touchedButtonForGestureRecognizer(_ gesture: UIPanGestureRecognizer) -> UIView? {
let pointInView = gesture.location(in: gesture.view)
for (button in buttonArray) {
let rect = gesture.view.convert(button.frame from:button.superview)
if (rect.contains(pointInView)) {
return button
}
}
return nil
}

Conflict between Pan Gesture and Tap Gestures

I'm currently working on a game that uses UIGestureRecognizers. I'm using the pan gesture to move the player around and the tap gesture to detect other UI button touches. Everything seems to work fine except that there is a conflict between the 2 gestures. Whenever the player is on the move (pan gesture gets recognized) the game ignores all my tap gestures (Once the pan gesture is recognized, the view won't recognize tap gestures).
Can someone please show me how to make the 2 gestures work together. I want the player to stop moving when a UI button gets tapped. In another word, I want to cancel the pan gesture whenever a tap gesture is recognized.
Thank you so much in advance!
Here is how I setup the 2 gestures:
let singleTap = UITapGestureRecognizer(target: self, action: #selector(doSingleTap))
singleTap.numberOfTapsRequired = 1
singleTap.delegate = self
self.view?.addGestureRecognizer(singleTap)
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
panGesture.minimumNumberOfTouches = 1
panGesture.delegate = self
self.view?.addGestureRecognizer(panGesture)
#objc func handlePan(gestureReconizer: UIPanGestureRecognizer) {
if isPaused || player.isInAction {return}
let translation = gestureReconizer.translation(in: self.view)
if gestureReconizer.state == .changed {
let angle = 180 + (atan2(-translation.x, translation.y) * (180/3.14159))
player.movementAngle = angle
player.atState = .Walk
}
if gestureReconizer.state == .ended {
player.movementAngle = 0.0
if player.atState == .Walk {
player.atState = .Idle
}
}
}
#objc func doSingleTap(gestureReconizer: UITapGestureRecognizer) {
let originaTapLocation = gestureReconizer.location(in: self.view)
let location = convertPoint(fromView: originaTapLocation)
let node = atPoint(location)
switch node.name {
case "HeroAvatar":
//do stuff here
break
case "Fire":
//do stuff here
break
case "Potion":
//do stuff here
break
default:
break
}
}
You need to implement delegate method of UIGestureRecognizerDelegate like below:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
// Don't recognize a single tap gesture until a pan gesture fails.
if gestureRecognizer == singleTap &&
otherGestureRecognizer == panGesture {
return true
}
return false
}
Hope this will work for you :)
For more info Apple Doc Preferring One Gesture Over Another

Swift 3 Draggable UIButton

Apologies as I'm still learning the basics of Swift.
I'm trying to move a button when I drag it which sounds simple. I can't figure out how to pass along the sender information to the drag function so I can associate it with the button that is being dragged.
I create multiple one word buttons which are only text and attach a pan gesture recognizer to each of them:
let pan = UIPanGestureRecognizer(target: self, action: #selector(panButton(_:)))
let word = UIButton(type: .system)
word.addGestureRecognizer(pan)
I've created this function to trigger when the button is moved:
func panButton(sender: UIPanGestureRecognizer){
if sender.state == .began {
//wordButtonCenter = button.center // store old button center
} else if sender.state == .ended || sender.state == .failed || sender.state == .cancelled {
//button.center = wordButtonCenter // restore button center
} else {
let location = sender.location(in: view) // get pan location
//button.center = location // set button to where finger is
}
}
I'm getting the following error:
Use of unresolved identifier 'panButton'
First of all, your action needs to be a selector in Swift 3. So that would look something like this:
let pan = UIPanGestureRecognizer(target: self, action: #selector(panButton(_:))
Also, you can't pass the value of a button through a selector, so you would need to change your func to be:
func panButton(sender: UIPanGestureRecognizer){
...
}
If you're wondering how you are supposed to find the button if you can't pass it as a parameter, then you might consider using tags.
As #benjamin points out in Swift 3 you needs to be a selector. I've updated my code to the following in order to extract the button tag:
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.panGestureHandler(panGesture:)))
panGesture.minimumNumberOfTouches = 1
let word = UIButton(type: .system)
With the following selector:
#objc func panGestureHandler(panGesture recognizer: UIPanGestureRecognizer) {
let buttonTag = (recognizer.view?.tag)!
if let button = view.viewWithTag(buttonTag) as? UIButton {
if recognizer.state == .began {
wordButtonCenter = button.center // store old button center
} else if recognizer.state == .ended || recognizer.state == .failed || recognizer.state == .cancelled {
button.center = wordButtonCenter // restore button center
} else {
let location = recognizer.location(in: view) // get pan location
button.center = location // set button to where finger is
}
}
}

Drag GoogleMaps-Marker via swiping over map

I have a Google-Map + marker. I know how to make a marker draggable. The standard behaviour is to 'Long-Press' a marker and you can drag it.
What I want is to drag the marker by swiping over the map. It shouldn't be neccessary to hit the marker. User swipes over the map from left to right and simultanously the marker changes position from left to right where the distance equals swipe-length.
I can't find a suitable solution in the GM-API. Any ideas?
I'm using Swift 2.2
var marker: GMSMarker!
func createMarker(title: String, target: CLLocationCoordinate2D) {
marker = GMSMarker(position: target)
marker.appearAnimation = kGMSMarkerAnimationPop
marker.map = map
}
func activateDragMode() {
marker.draggable = true
map.settings.scrollGestures = false
map.settings.zoomGestures = false
map.settings.rotateGestures = false
}
The GoogleMap-API doesn't provide the method I need. But i found a solution:
map.settings.consumesGesturesInView = false
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panRecognition))
view.addGestureRecognizer(panGestureRecognizer)
func panRecognition(recognizer: UIPanGestureRecognizer) {
if marker.draggable {
let markerPosition = map.projection.pointForCoordinate(marker.position)
let translation = recognizer.translationInView(view)
recognizer.setTranslation(CGPointZero, inView: view)
let newPosition = CGPointMake(markerPosition.x + translation.x, markerPosition.y + translation.y)
marker.position = map.projection.coordinateForPoint(newPosition)
}
}
I had to deactivate 'consumesGesturesInView' to add my own PanGestureRecognizer, which manipulates the marker.
Swift 5.1
By analysis on google api and other method, I did not get the proper one. The best and sweet answer for this question is by using pan gesture.
add the pan gesture to the mapView as
self.mapView.settings.consumesGesturesInView = false
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self. panHandler(_:)))
self.mapView.addGestureRecognizer(panGesture)
Implementation of pan gesture method as
#objc private func panHandler(_ pan : UIPanGestureRecognizer){
if pan.state == .ended{
let mapSize = self.mapView.frame.size
let point = CGPoint(x: mapSize.width/2, y: mapSize.height/2)
let newCoordinate = self.mapView.projection.coordinate(for: point)
print(newCoordinate)
//do task here
}
}

Swift Game - Tap and Tap + Hold Gestures?

I'm in the process of learning swift (and spritekit) and trying to make a simple game.
In my game, the hero should jump or duck...
The hero needs to jump when the screen is tapped,or duck if the screen is tap+held (long gesture)
So basic pseudo code:
if tapped
heroJump()
else if tappedAndHeld
heroDuck()
I have a func which is seen in almost all tutorials, which handles the touch event:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self) //need location of tap for something
switch gameState {
case .Play:
//do in game stuff
break
case .GameOver:
//opps game ended
}
break
}
}
super.touchesBegan(touches, withEvent: event)
}
Is there a way, to include in this touch event, to decide if it was tapped or held? I can't seem to get my head around the fact, the program will always recognise a tap before a long gesture?!?
Anyway, in an attempt to solve my problem, I found THIS question, which introduced to me recognisers, which I tried to implement:
override func didMoveToView(view: SKView) {
// other stuff
//add long press gesture, set to start after 0.2 seconds
let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: "longPressed:")
longPressRecognizer.minimumPressDuration = 0.2
self.view!.addGestureRecognizer(longPressRecognizer)
//add tap gesture
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "tapped:")
self.view!.addGestureRecognizer(tapGestureRecognizer)
}
And these are the functions the gestures call:
func longPressed(sender: UILongPressGestureRecognizer)
{
if (sender.state == UIGestureRecognizerState.Ended) {
print("no longer pressing...")
} else if (sender.state == UIGestureRecognizerState.Began) {
print("started pressing")
// heroDuck()
}
}
func tapped(sender: UITapGestureRecognizer)
{
print("tapped")
// heroJump()
}
How can I combine these two things?
Can I add a way to determine whether it was tapped or hold in my touchBegins event, or can I scrap that method and use only the two functions above?
One of many problems being getting the location if using the latter?
Or maybe I'm looking at this completely wrong, and there's a simple and/or built in way in swift/spritekit?
Thanks.
You only need the UITapGestureRecognizer and the UILongPressRecognizer. You do not need to do anything with touchesBegan and touchesEnded, because the gesture recognizer analyses the touches itself.
To get the location of the touch you can call locationInView on the gesture recognizer to get the location of the gesture or locationOfTouch if you are working with multitouch gestures and need the location of each touch. Pass nil as parameter when you want the coordinates in the window’s base coordinate system.
Here is a working example:
func setupRecognizers() {
let tapRecognizer = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
let longTapRecognizer = UILongPressGestureRecognizer(target: self, action: Selector("handleLongPress:"))
view.addGestureRecognizer(tapRecognizer)
view.addGestureRecognizer(longTapRecognizer)
}
func handleLongPress(recognizer: UIGestureRecognizer) {
if recognizer.state == .Began {
print("Duck! (location: \(recognizer.locationInView(nil))")
}
}
func handleTap(recognizer: UIGestureRecognizer) {
print("Jump! (location: \(recognizer.locationInView(nil))")
}
If a long press is recognized handleTap: tap is not called. Only if the user lifts his finger fast enough handleTap: will be called. Otherwise handleLongPress will be called. handleLongPress will only be called after the long press duration has passed. Then handleLongPress will be called twice: When the duration has passed ("Began") and after the user has lifted his finger ("Ended").
you do the same thing you are doing for longpress, wait till the .Ended event
func tapped(sender: UITapGestureRecognizer)
{
if sender.state == .Ended {
print("tapped")
}
}
A tap event will always happen, this can't be prevented because lets face it, you need to touch the screen. What should be happening though is when you enter the long press event, the tap event should go into a Cancel state instead of an Ended state