Uipangesturerecognizer Is Not Working in iOS 13

iOS 13 UIPanGestureRecognizer selector not called

WKWebView might be suppressing the gesture, found related discussion here: Can't handle touches simultaneously with WKWebView on iOS 13.4

Hope this could help you.

UIPanGestureRecognizer does not work

Here is the answer:

class ViewController: UIViewController {

@IBOutlet weak var redView: UIView!
@IBOutlet weak var whiteView: UIView!

var lastLocation:CGPoint = CGPointMake(0, 0)

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let panRecognizer = UIPanGestureRecognizer(target:self, action:#selector(ViewController.handlePan))
whiteView.addGestureRecognizer(panRecognizer)

}

func handlePan(recognizer:UIPanGestureRecognizer) {
let translation = recognizer.translationInView(redView)
whiteView.center = CGPointMake(lastLocation.x + translation.x, lastLocation.y + translation.y)
}

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// Promote the touched view
lastLocation = whiteView.center

}
}

UIPanGestureRecognizer doesn't trigger action

The problem you're experiencing is due to your definition of the panGestureRecognizer variable in the class definition here:

//Here I create the reference to the recognizer
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))

You can initialize it this way, but it seems like self is not setup when this variable is created. So your action is never registered.

There are a couple ways you can fix this using your code, you can continue to initialize it as an instance variable, but you'll need to setup your target/action in your initialize() function

let panGestureRecognizer = UIPanGestureRecognizer()

func initialize() {
// add your target/action here
panGestureRecognizer.addTarget(self, action: #selector(handlePan(_:)))
panGestureRecognizer.minimumNumberOfTouches = 1
panGestureRecognizer.maximumNumberOfTouches = 1
addGestureRecognizer(panGestureRecognizer)
}

Or you can simply initialize your gesture recognizer in your initialize function and not use an instance variable

func initialize() {
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
panGestureRecognizer.minimumNumberOfTouches = 1
panGestureRecognizer.maximumNumberOfTouches = 1
addGestureRecognizer(panGestureRecognizer)
}

My original answer

Here's a solution that works using constraints with a view defined in the storyboard or by manipulating the frame directly.

Example with Constraints

import UIKit

// Delegate protocol for managing constraint updates if needed
protocol MorphDelegate: class {

// tells the delegate to change its size constraints
func morph(x: CGFloat, y: CGFloat)
}

class ExpandableView: UIView {

var delegate: MorphDelegate?

init() {
// frame is set later if needed by creator when using this init method
super.init(frame: CGRect.zero)
configureGestureRecognizers()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configureGestureRecognizers()
}

override init(frame: CGRect) {
super.init(frame: frame)
configureGestureRecognizers()
}

// setup UIPanGestureRecognizer
internal func configureGestureRecognizers() {
let panGR = UIPanGestureRecognizer.init(target: self, action: #selector(didPan(_:)))
addGestureRecognizer(panGR)
}

@objc func didPan(_ panGR: UIPanGestureRecognizer) {

// get the translation
let translation = panGR.translation(in: self).applying(transform)

if let delegate = delegate {

// tell delegate to change the constraints using this translation
delegate.morph(x: translation.x, y: translation.y)

} else {

// If you want the view to expand/contract in opposite direction
// of drag then swap the + and - in the 2 lines below
let newOriginY = frame.origin.y + translation.y
let newHeight = frame.size.height - translation.y

// expand self via frame manipulation
let newFrame = CGRect(x: frame.origin.x, y: newOriginY, width: frame.size.width, height: newHeight)
frame = newFrame
}

// reset translation
panGR.setTranslation(CGPoint.zero, in: self)
}

}

If you want to use this class by defining it in the storyboard and manipulating it's constraints you'd go about it like this.

First define your view in the storyboard and constrain it's width and height. For the demo I constrained it's x position to the view center and it's bottom to the SafeArea.bottom

Create an IBOutlet for the view, as well as it's height constraint to your ViewController file.

I set the background color to blue for this example.

storyboard setup

In the view controller I defined a function to setup the view (currently only sets the delegate for constraint manipulation callbacks) and then defined an extension to handle delegate calls for updating constraints.

class ViewController: UIViewController {

@IBOutlet weak var expandingView: ExpandableView!
@IBOutlet weak var constraint_expViewHeight: NSLayoutConstraint!

override func viewDidLoad() {
super.viewDidLoad()

// call to configure our expanding view
configureExpandingView()
}

// this sets the delegate for an expanding view defined in the storyboard
func configureExpandingView() {
expandingView.delegate = self
}
}

// setup the delegate callback to handle constraint manipulation
extension ViewController: MorphDelegate {

func morph(x: CGFloat, y: CGFloat) {

// this will update the view's height based on the amount
// you drag your finger in the view. You can '+=' below if
// you want to reverse the expanding behavior based on pan
// movements.
constraint_expViewHeight.constant -= y
}
}

Doing it this way via constraints gives me this when I run the project:

animation with constraints

Example with frame manipulation

To use this and manipulate the height using just the frame property the view controller might implement the view creation something like this:

override func viewDidLoad() {
super.viewDidLoad()

setupExpandingView()
}

func setupExpandingView() {

let newView = ExpandableView()
newView.backgroundColor = .red
newView.frame = CGRect(x: 20, y: 200, width: 100, height: 100)
view.addSubview(newView)
}

Using just frame manipulation I get this:

Demo Gif

UIPanGestureRecognizer isn't firing up. Swift 3

OK, I took a look at the code and I see two possible issues:

1: You have pinch, pan, and rotate gesture recognizers set in the storyboard as well. These might be superfluous. Just mentioning that. This really isn't the issue your pan gesture recognizer isn't working though.

2: The UIView for controls covers your emoji and image view and will take over touches. You don't need a full-screen view for the UI controls since you don't seem to do anything with the view itself. Instead, you can simply put the button at the right position on the main screen over the viewForImgAndEmoji. This way, any on screen touches reach viewForImgAndEmoji except at the spots where you have the buttons.

I modified the storyboard that way and I could get the pan gestures fine at that point. Do let me know if you need me to clarify anything since I am not sure if I was clear enough about the issue :)

Note: One issue that you will run into when you fix the above is that you will find that not all touches register because the gesture recognizer is attached to each sticker and the sticker itself might be too small to detect something like a pinch gesture. You might be better off adding viewForImgAndEmoji as the gesture handler and activating each sticker/emoji as the recipient of the the gesture based on selecting the emoji by a tap before you try to pinch, rotate, or pan. Just a suggestion :)



Related Topics



Leave a reply



Submit