Spritekit - How to add a swipe gesture to multiple nodes at once? - ios

I'm trying to add a swipe gesture for multiple nodes at once but it seems to only be working for the first node. When I try and swipe the other nodes it doesn't register there is a swipe. There is only one node but it is copied many times in the .sks file to fill the screen. All the nodes have the same name since it's a copy, I'm hoping to reuse them to fill the screen back up as soon as one of the nodes is swiped off.
let plankName = "woodPlank"
class PlankScene: SKScene {
var plankWood : SKSpriteNode?
var plankArray : [SKSpriteNode] = []
override func didMove(to view: SKView) {
plankWood = childNode(withName: "woodPlank") as? SKSpriteNode
let swipeRight : UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(PlankScene.swipedRight))
swipeRight.direction = .right
view.addGestureRecognizer(swipeRight)
}
func swipedRight(sender: UISwipeGestureRecognizer) {
if sender.direction == .right {
let moveOffScreenRight = SKAction.moveTo(x: 400, duration: 0.5)
let nodeFinishedMoving = SKAction.removeFromParent()
let waitForNode = SKAction.wait(forDuration: 0.5)
plankWood?.run(SKAction.sequence([moveOffScreenRight, nodeFinishedMoving]),
completion:{
})
})
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let touchLocation = touch!.location(in: self)
print("\(plankWood?.name)")
if plankWood?.name == plankName {
print("Plank touched")
}
}
override func enumerateChildNodes(withName name: String, using block: #escaping (SKNode, UnsafeMutablePointer<ObjCBool>) -> Void) {
let swipeRight : UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(PlankScene.swipedRight))
swipeRight.direction = .right
view?.addGestureRecognizer(swipeRight)
}
}

Related

Swift -How to make a passthrough view recognize a down swipe gesture

I have a second window that has a view with a passthrough view inside of it. The passthrough works fine but I need it to recognize downSwipe gestures while still passing all other touch events to the view below it. How can I do this?
class PassThroughView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
print("Passing all touches to the next view (if any), in the view stack.")
return false
}
}
class MyVC: UIViewController {
lazy var backdropView: PassThroughView = {
let v = PassThroughView(frame: self.view.bounds)
v.backgroundColor = .clear
v.isUserInteractionEnabled = true
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .clear
addGesture()
}
func addGesture() {
let swipeDown = UISwipeGestureRecognizer(target: self, action: #selector(handleGesture))
swipeDown.direction = .down
backdropView.addGestureRecognizer(swipeDown)
}
#objc func handleGesture(gesture: UISwipeGestureRecognizer) -> Void {
if gesture.direction == .down {
print("Swipe Down")
}
}
}

Tap gesture events on overlapping area

I have a view that is with the black border and it has two different views on it. And these views are overlapping in a small area. And each of them has own UITapGestureRecognizer. When I tap each item's discrete area, the action of that item is triggered. But when I tap the common area, only the second view's action is triggered. I want that both actions have to be triggered. How can I achieve this? Here is my code:
class ViewController: UIViewController {
#IBOutlet weak var view1: UIView!
#IBOutlet weak var view2: UIView!
#IBOutlet weak var outerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
outerView.layer.borderWidth = 2.0
outerView.layer.borderColor = UIColor.black.cgColor
view1.layer.borderWidth = 2.0
view1.layer.borderColor = UIColor.red.cgColor
view2.layer.borderWidth = 2.0
view2.layer.borderColor = UIColor.blue.cgColor
self.initialize()
}
private func initialize(){
let tapGesture1 = UITapGestureRecognizer(target: self, action: #selector(detectTap1(_:)))
let tapGesture2 = UITapGestureRecognizer(target: self, action: #selector(detectTap2(_:)))
self.view1.addGestureRecognizer(tapGesture1)
self.view2.addGestureRecognizer(tapGesture2)
}
#objc func detectTap1(_ gesture : UITapGestureRecognizer) {
print("detectTap1")
}
#objc func detectTap2(_ gesture : UITapGestureRecognizer) {
print("detectTap2")
}
}
Kindly share your suggestions.
For this problem i have found this solution, maybe is not the best solution but it works, i will look for further improvements anyway
I had subclassed UIGestureRecognizer class
Updated
import UIKit
import UIKit.UIGestureRecognizerSubclass
class CustomGestureRecognizer: UIGestureRecognizer {
var anotherGestureRecognizer : CustomGestureRecognizer?
private var touchBeganSended : Bool = false
private var touchLocation : CGPoint?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
if let validTouch = touches.first?.location(in: self.view) {
if (self.view!.point(inside: validTouch, with: event)) {
if(!touchBeganSended) {
touchBeganSended = true
touchLocation = validTouch
anotherGestureRecognizer?.touchesBegan(touches, with: event)
state = .recognized
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
if let validTouch = touches.first?.location(in: self.view) {
if (self.view!.point(inside: validTouch, with: event)) {
if(touchBeganSended) {
touchBeganSended = false
anotherGestureRecognizer?.touchesEnded(touches, with: event)
state = .recognized
}
}
}
}
override func location(in view: UIView?) -> CGPoint {
if let desiredView = view {
if(desiredView == self.view) {
return touchLocation ?? CGPoint(x: 0, y: 0)
} else {
return super.location(in: view)
}
} else {
return super.location(in: view)
}
}
}
Updated
then you need to modify your initialize() method to this one, with the last update you don't need to take into account which view is on top on view hierarchy
private func initialize(){
let tapGesture1 = CustomGestureRecognizer(target: self, action: #selector(detectTap1(_:)))
let tapGesture2 = CustomGestureRecognizer(target: self, action: #selector(detectTap2(_:)))
tapGesture1.cancelsTouchesInView = true
tapGesture1.delegate = self
tapGesture2.cancelsTouchesInView = true
tapGesture2.delegate = self
self.view1.addGestureRecognizer(tapGesture1)
tapGesture1.anotherGestureRecognizer = tapGesture2
tapGesture2.anotherGestureRecognizer = tapGesture1
self.view2.addGestureRecognizer(tapGesture2)
}
this works as you can see here
Try the following:
private func initialize(){
let tapGesture1 = UITapGestureRecognizer(target: self, action: #selector(detectTap1(_:)))
let tapGesture2 = UITapGestureRecognizer(target: self, action: #selector(detectTap2(_:)))
tapGesture1.cancelsTouchesInView = false
tapGesture2.cancelsTouchesInView = false
self.view1.addGestureRecognizer(tapGesture1)
self.view2.addGestureRecognizer(tapGesture2)
}
When you set
gesture.cancelsTouchesInView = false
it propagates the gesture to the views underneath.
Try to implement this UIGestureRecognizerDelegate method:
class ViewController: UIViewController, UIGestureRecognizerDelegate {
...
...
private func initialize() {
...
gesture1.delegate = self
gesture2.delegate = self
...
}
func gestureRecognizer(
_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
) -> Bool {
// its up to you
//guard otherGestureRecognizer == yourAnotherGesture else { return false }
return true
}

Changing UIImageView transform scale broke movement system

I am trying to create an Image view which I can move and scale on screen. the problem is that when I change the scale of the Image, the movement system seams to be broken.
I wrote some code to drag the object from an anchor point which could be different from the center of the UIImage, but the scale ruined the process.
/*
See LICENSE folder for this sample’s licensing information.
Abstract:
Main view controller for the AR experience.
*/
import ARKit
import SceneKit
import UIKit
import ModelIO
class ViewController: UIViewController, ARSessionDelegate , UIGestureRecognizerDelegate{
// MARK: Outlets
#IBOutlet var sceneView: ARSCNView!
#IBOutlet weak var blurView: UIVisualEffectView!
#IBOutlet weak var dropdown: UIPickerView!
#IBOutlet weak var AddStickerButton: UIButton!
#IBOutlet weak var deleteStickerButton: UIImageView!
var offset : CGPoint = CGPoint.zero
var isDeleteVisible : Bool = false
let array:[String] = ["HappyHeart_Lisa", "Logo_bucato", "Sweety_2_Lisa", "Sweety_Lisa", "Tonglue_Lisa"]
lazy var statusViewController: StatusViewController = {
return childViewControllers.lazy.flatMap({ $0 as? StatusViewController }).first!
}()
var stickers = [Sticker]()
// MARK: Properties
var myScene : SCNScene!
/// Convenience accessor for the session owned by ARSCNView.
var session: ARSession {
sceneView.session.configuration
//sceneView.scene.background.contents = UIColor.black
return sceneView.session
}
var nodeForContentType = [VirtualContentType: VirtualFaceNode]() //Tiene sotto controllo la selezione(Tipo maschera)
let contentUpdater = VirtualContentUpdater() //Chiama la VirtualContentUpdater.swift
var selectedVirtualContent: VirtualContentType = .faceGeometry {
didSet {
// Set the selected content based on the content type.
contentUpdater.virtualFaceNode = nodeForContentType[selectedVirtualContent]
}
}
// MARK: - View Controller Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = contentUpdater
sceneView.session.delegate = self
sceneView.automaticallyUpdatesLighting = true
createFaceGeometry()
// Set the initial face content, if any.
contentUpdater.virtualFaceNode = nodeForContentType[selectedVirtualContent]
// Hook up status view controller callback(s).
statusViewController.restartExperienceHandler = { [unowned self] in
self.restartExperience()
}
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(scale))
let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(rotate))
pinchGesture.delegate = self
rotationGesture.delegate = self
view.addGestureRecognizer(pinchGesture)
view.addGestureRecognizer(rotationGesture)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
/*
AR experiences typically involve moving the device without
touch input for some time, so prevent auto screen dimming.
*/
UIApplication.shared.isIdleTimerDisabled = true
resetTracking()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
session.pause()
}
// MARK: - Setup
/// - Tag: CreateARSCNFaceGeometry
func createFaceGeometry() {
// This relies on the earlier check of `ARFaceTrackingConfiguration.isSupported`.
let device = sceneView.device!
let maskGeometry = ARSCNFaceGeometry(device: device)!
let glassesGeometry = ARSCNFaceGeometry(device: device)!
nodeForContentType = [
.faceGeometry: Mask(geometry: maskGeometry),
.overlayModel: GlassesOverlay(geometry: glassesGeometry),
.blendShapeModel: RobotHead(),
.sfere: RobotHead()
]
}
// MARK: - ARSessionDelegate
func session(_ session: ARSession, didFailWithError error: Error) {
guard error is ARError else { return }
let errorWithInfo = error as NSError
let messages = [
errorWithInfo.localizedDescription,
errorWithInfo.localizedFailureReason,
errorWithInfo.localizedRecoverySuggestion
]
let errorMessage = messages.flatMap({ $0 }).joined(separator: "\n")
DispatchQueue.main.async {
self.displayErrorMessage(title: "The AR session failed.", message: errorMessage)
}
}
func sessionWasInterrupted(_ session: ARSession) {
blurView.isHidden = false
statusViewController.showMessage("""
SESSION INTERRUPTED
The session will be reset after the interruption has ended.
""", autoHide: false)
}
func sessionInterruptionEnded(_ session: ARSession) {
blurView.isHidden = true
DispatchQueue.main.async {
self.resetTracking()
}
}
/// - Tag: ARFaceTrackingSetup
func resetTracking() {
statusViewController.showMessage("STARTING A NEW SESSION")
guard ARFaceTrackingConfiguration.isSupported else { return }
let configuration = ARFaceTrackingConfiguration()
configuration.isLightEstimationEnabled = true
session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
}
// MARK: - Interface Actions
/// - Tag: restartExperience
func restartExperience() {
// Disable Restart button for a while in order to give the session enough time to restart.
statusViewController.isRestartExperienceButtonEnabled = false
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
self.statusViewController.isRestartExperienceButtonEnabled = true
}
resetTracking()
}
// MARK: - Error handling
func displayErrorMessage(title: String, message: String) {
// Blur the background.
blurView.isHidden = false
// Present an alert informing about the error that has occurred.
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let restartAction = UIAlertAction(title: "Restart Session", style: .default) { _ in
alertController.dismiss(animated: true, completion: nil)
self.blurView.isHidden = true
self.resetTracking()
}
alertController.addAction(restartAction)
present(alertController, animated: true, completion: nil)
}
//Create a new Sticker
func createNewSticker(){
stickers.append(Sticker(view : self.view, viewCtrl : self))
}
#IBAction func addNewSticker(_ sender: Any) {
createNewSticker()
}
//Function To Move the Stickers, all the Touch Events Listener
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches as! Set<UITouch>) {
var location = touch.location(in: self.view)
for sticker in stickers {
if(sticker.imageView.frame.contains(location) && !isSomeOneMoving()){
//sticker.imageView.center = location
offset = touch.location(in: sticker.imageView)
let offsetPercentage = CGPoint(x: offset.x / sticker.imageView.bounds.width, y: offset.y / sticker.imageView.bounds.height)
let offsetScaled = CGPoint(x: sticker.imageView.frame.width * offsetPercentage.x, y: sticker.imageView.frame.height * offsetPercentage.y)
offset.x = (sticker.imageView.frame.width / 2) - offsetScaled.x
offset.y = (sticker.imageView.frame.height / 2) - offsetScaled.y
location = touch.location(in: self.view)
location.x = (location.x + offset.x)
location.y = (location.y + offset.y)
sticker.imageView.center = location
disableAllStickersMovements()
isDeleteVisible = true
sticker.isStickerMoving = true;
deleteStickerButton.isHidden = false
}
}
}
}
func disableAllStickersMovements(){
for sticker in stickers {
sticker.isStickerMoving = false;
}
}
func isSomeOneMoving() -> Bool{
for sticker in stickers {
if(sticker.isStickerMoving){
return true
}
}
return false
}
var lastLocationTouched : CGPoint = CGPoint.zero
var lastStickerTouched : Sticker = Sticker()
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches as! Set<UITouch>) {
var location = touch.location(in: self.view)
for sticker in stickers {
if(sticker.imageView.frame.contains(location) && sticker.isStickerMoving){
lastLocationTouched = location
location = touch.location(in: self.view)
location.x = (location.x + offset.x)
location.y = (location.y + offset.y)
sticker.imageView.center = location
//sticker.imageView.center = location
}
if(deleteStickerButton.frame.contains(lastLocationTouched) && isDeleteVisible && sticker.isStickerMoving){
sticker.imageView.alpha = CGFloat(0.5)
}else{
sticker.imageView.alpha = CGFloat(1)
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for sticker in stickers {
if(deleteStickerButton.frame.contains(lastLocationTouched) && isDeleteVisible && sticker.isStickerMoving){
removeASticker(sticker : sticker)
disableAllStickersMovements()
}
}
disableAllStickersMovements()
isDeleteVisible = false
deleteStickerButton.isHidden = true
}
func removeASticker(sticker : Sticker ){
sticker.imageView.removeFromSuperview()
let stickerPosition = stickers.index(of: sticker)!
stickers.remove(at: stickerPosition)
for sticker in stickers {
sticker.isStickerMoving = false;
}
}
var identity = CGAffineTransform.identity
#objc func scale(_ gesture: UIPinchGestureRecognizer) {
for sticker in stickers {
if(sticker.isStickerMoving){
switch gesture.state {
case .began:
identity = sticker.imageView.transform
case .changed,.ended:
sticker.imageView.transform = identity.scaledBy(x: gesture.scale, y: gesture.scale)
case .cancelled:
break
default:
break
}
}
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
#objc func rotate(_ gesture: UIRotationGestureRecognizer) {
for sticker in stickers {
if(sticker.isStickerMoving){
sticker.imageView.transform = sticker.imageView.transform.rotated(by: gesture.rotation)
}
}
}
}
and then the sticker class
import UIKit
import Foundation
class Sticker : NSObject, UIGestureRecognizerDelegate{
var location = CGPoint(x: 0 , y: 0);
var sticker_isMoving = false;
let imageView = UIImageView()
var isStickerMoving : Bool = false;
init(view : UIView, viewCtrl : ViewController ) {
super.init()
imageView.image = UIImage(named: "BroccolFace_Lisa.png")
imageView.isUserInteractionEnabled = true
imageView.contentMode = UIViewContentMode.scaleAspectFit
imageView.frame = CGRect(x: view.center.x, y: view.center.y, width: 200, height: 200)
view.addSubview(imageView)
}
override init(){
}
}
This is because the imageView.bounds and the touch.location(in: imageView) are in unscaled values. This will overcome the problem:
offset = touch.location(in: imageView)
let offsetPercentage = CGPoint(x: offset.x / imageView.bounds.width, y: offset.y / imageView.bounds.height)
let offsetScaled = CGPoint(x: imageView.frame.width * offsetPercentage.x, y: imageView.frame.height * offsetPercentage.y)
offset.x = (imageView.frame.width / 2) - offsetScaled.x
offset.y = (imageView.frame.height / 2) - offsetScaled.y
Basically it converts the offset into a percentage based on the unscaled values and then converts that into scaled values based on the imageView frame (which is modified by the scale). It then uses that to calculate the offset.
EDIT (NUMBER TWO)
This is more complete way to do it and it should solve any issues that may arise due to scaling or rotation.
Add this structure to hold the details of the dragging for images:
struct DragInfo {
let imageView: UIImageView
let startPoint: CGPoint
}
Add these instance variables (you can also remove offset if you want):
var dragStartPoint: CGPoint = CGPoint.zero
var currentDragItems: [DragInfo] = []
var dragTouch: UITouch?
Change touchesBegan to this:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard self.dragTouch == nil, let touch = touches.first else { return }
self.dragTouch = touch
let location = touch.location(in: self.view)
self.dragStartPoint = location
for imageView in self.imageList {
if imageView.frame.contains(location) {
self.currentDragItems.append(DragInfo(imageView: imageView, startPoint: imageView.center))
}
}
}
Change touchesMoved to this:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let dragTouch = self.dragTouch else { return }
for touch in touches {
if touch == dragTouch {
let location = touch.location(in: self.view)
let offset = CGPoint(x: location.x - self.dragStartPoint.x, y: location.y - self.dragStartPoint.y)
for dragInfo in self.currentDragItems {
let imageOffSet = CGPoint(x: dragInfo.startPoint.x + offset.x, y: dragInfo.startPoint.y + offset.y)
dragInfo.imageView.center = imageOffSet
}
}
}
}
Change touchesEnded to this:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let dragTouch = self.dragTouch, touches.contains(dragTouch) else { return }
self.currentDragItems.removeAll()
self.dragTouch = nil
}
Set the following properties on the gesture recognisers used:
scaleGesture.delaysTouchesEnded = false
scaleGesture.cancelsTouchesInView = false
rotationGesture.delaysTouchesEnded = false
rotationGesture.cancelsTouchesInView = false
Some explanation about how it works.
With all the touch events it only considers the first touch because dragging from multiple touches doesn't make much sense (what if two touches were over the same image view and move differently). It records this touch and then only considers that touch for dragging things around.
When touchesBegan is called it checks no touch for dragging exists (indicating a drag in progress) and it finds all image views that are under the touch and for each one it records the details of itself and it start centre position in a DragInfo structure and stores it in the currentDragItems array. It also records the position the touch started in the main view and the touch that initiated it.
When touchesMoved is called it only considers the touch that started the dragging and it calculates the offset from the original position the touch started in the main view and then goes down the list of images involved in the dragging and calculates their new centre based on their original starting position and the offset calculated and sets that as the new centre.
When touchesEnded is called assuming it is the dragging touch that is ended it clears the array of DragInfo objects to ready for the next drag.
You need to set the delaysTouchesEnded and cancelsTouchesInView properties on all gesture recognisers so that all touches are passed through to the view otherwise the touchesEnded methods in particular are not called.
Doing the calculations like this removes the problems of scale and rotation as you are just concerned with offsets from initial positions. It also works if multiple image views are dragged at the same time as their details are kept separately.
Now there are some things to be aware of:
You will need to put in all the other code you app required as this is just a basic example to show the idea.
This assumes that you only want to drag image views that you pick up at the start. If you want to collect image views as you drag around you would need to develop a much more complicated system.
As I stated only one drag operation can be in progress at a time and it takes the first touch registered as this source touch. This source touch is then used to filter out any other touches that may happen. This is done to keep things simple and otherwise you would have to account for all kinds of strange situations like if multiple touches were on the same image view.
I hope this all makes sense and you can adapt it to solve your problem.
Here is an extension that I use to pan, pinch and rotate an image with UIPanGestureRecognizer, UIPinchGestureRecognizer and UIRotationGestureRecognizer
extension ViewController : UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func panGesture(gesture: UIPanGestureRecognizer) {
switch gesture.state {
case .ended: fallthrough
case .changed:
let translation = gesture.translation(in: gesture.view)
if let view = gesture.view {
var finalPoint = CGPoint(x:view.center.x + translation.x, y:view.center.y + translation.y)
finalPoint.x = min(max(finalPoint.x, 0), self.myImageView.bounds.size.width)
finalPoint.y = min(max(finalPoint.y, 0), self.myImageView.bounds.size.height)
view.center = finalPoint
gesture.setTranslation(CGPoint.zero, in: gesture.view)
}
default : break
}
}
func pinchGesture(gesture: UIPinchGestureRecognizer) {
switch gesture.state {
case .changed:
let scale = gesture.scale
gesture.view?.transform = gesture.view!.transform.scaledBy(x: scale, y: scale)
gesture.scale = 1
default : break
}
}
func rotateGesture(gesture: UIRotationGestureRecognizer) {
switch gesture.state {
case .changed:
let rotation = gesture.rotation
gesture.view?.transform = gesture.view!.transform.rotated(by: rotation)
gesture.rotation = 0
default : break
}
}
}
setting the UIGestureRecognizerDelegate will help you do the three of gestures at the same time.

Swift: how to detect if swipe occurred in subview?

Assume a UIView, ParentView, contains a subview, ChildView, and other subviews.
The UIViewController attaches a swipe gesture recognizer to ParentView.
Swipes on ChildView trigger this swipe handler.
Inside of ParentView's swipe handler, is there a way to detect if the swipe occurred on ChildView?
Per Josh's answer, here's the attempted code, which does not work:
class TestViewController: UIViewController {
#IBOutlet weak var targetView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
initSwipeGestures()
}
fileprivate func initSwipeGestures() {
let leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(didViewSwipe))
let rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(didViewSwipe))
let upSwipe = UISwipeGestureRecognizer(target: self, action: #selector(didViewSwipe))
let downSwipe = UISwipeGestureRecognizer(target: self, action: #selector(didViewSwipe))
leftSwipe.direction = .left
rightSwipe.direction = .right
upSwipe.direction = .up
downSwipe.direction = .down
view.addGestureRecognizer(leftSwipe)
view.addGestureRecognizer(rightSwipe)
view.addGestureRecognizer(upSwipe)
view.addGestureRecognizer(downSwipe)
}
func didViewSwipe(_ sender:UISwipeGestureRecognizer) {
let location = sender.location(in: view)
let touchedView = view.hitTest(location, with: nil)
// Ignore swipes if targetView was swiped
if touchedView == targetView {
print("YO YO YO")
}
}
}
You can use the UIView method hitTest(_ point: CGPoint, with event: UIEvent?) to get the subview under the gesture's location.
func swipe(_ recognizer: UISwipeGestureRecognizer)
{
let location = recognizer.location(in: self)
let touchedView = self.hitTest(location, with: nil)
}

how to get tap point (coordinates) in iOS swift

I'm trying to get the coordinates of a touch on the screen, To show a popup menu
how to use in page view controller
What am I doing wrong here?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let position = touch.locationInView(view)
print(position)
}
}
In PageViewController,if you want to get pop up
In viewDidLoad:
let tap = UITapGestureRecognizer(target: self, action: "showMoreActions:")
tap.numberOfTapsRequired = 1
view.addGestureRecognizer(tap)
Make the page view controller inherit UIGestureRecognizerDelegate then add:
func showMoreActions(touch: UITapGestureRecognizer) {
let touchPoint = touch.locationInView(self.view)
let DynamicView = UIView(frame: CGRectMake(touchPoint.x, touchPoint.y, 100, 100))
DynamicView.backgroundColor=UIColor.greenColor()
DynamicView.layer.cornerRadius=25
DynamicView.layer.borderWidth=2
self.view.addSubview(DynamicView)
}
UIPageViewController with TouchEvent
Swift 5:
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(touchedScreen(touch:)))
view.addGestureRecognizer(tap)
}
#objc func touchedScreen(touch: UITapGestureRecognizer) {
let touchPoint = touch.location(in: self.view)
print(touchPoint)
}

Resources