I'm trying to implement Gesture recognizer over UIWebView to detect double taps. But there is no recognition.
I have looked over web for a few tutorials and I could compile to this. Code below. But there is no success.
class PageContentViewController: UIViewController, UIGestureRecognizerDelegate, UIWebViewDelegate {
#IBOutlet weak var webView: UIWebView!
var pageIndex: Int = 0
var strTitle: String!
var flag : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
DispatchQueue.main.async {
let req = URLRequest(url: URL(fileURLWithPath: Bundle.main.path(forResource: self.strTitle+"/index" , ofType: "html")!))
self.webView.loadRequest(req)
self.view.addSubview(self.webView)
}
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action:#selector(self.goToback))
tapGestureRecognizer.delegate? = self
tapGestureRecognizer.numberOfTapsRequired = 2
webView.isUserInteractionEnabled = true
webView.addGestureRecognizer(tapGestureRecognizer)
webView.scrollView.addGestureRecognizer(tapGestureRecognizer)
webView.gestureRecognizerShouldBegin(tapGestureRecognizer)
}
func gestureRecognizer(_: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith:UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_: UIGestureRecognizer, shouldReceive:UITouch) -> Bool {
return true
}
func goToback(){
print("On the Back")
}
}
What is wrong in the code.?
I'm noob to swift.
Thanks in advance
I think you will need to add
webView.delegate = self
Also add UIWebViewDelegate to your class
Edit:
Your shouldRecognizeSimultaneouslyWith and shouldReceive look a bit weird maybe a copy paste mistake? It should look like:
func gestureRecognizer(_: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith:UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_: UIGestureRecognizer, shouldReceive:UITouch) -> Bool {
return true
}
Edit 2:
I copied your exact code and removed gestureRecognizerShould begin and it worked, I was able to remove some other code as well, not sure if you needed it in your project but here is what works with me:
class ViewController: UIViewController, UIGestureRecognizerDelegate, UIWebViewDelegate {
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
webView.delegate = self
DispatchQueue.main.async {
let req = URLRequest(url: URL(fileURLWithPath: Bundle.main.path(forResource: self.strTitle+"/index" , ofType: "html")!))
self.webView.loadRequest(req)
}
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action:#selector(self.goToback))
tapGestureRecognizer.delegate = self
tapGestureRecognizer.numberOfTapsRequired = 2
webView.isUserInteractionEnabled = true
webView.addGestureRecognizer(tapGestureRecognizer)
}
func gestureRecognizer(_: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith:UIGestureRecognizer) -> Bool {
return true
}
func goToback() {
print("On the Back")
}
Why are you using same gesture UIGestureRecognizer for webview and webview.scrollview.
Instead you need to create two separate UIGestureRecognizer for both like this...
// for webView
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action:#selector(self.goToback))
tapGestureRecognizer.delegate? = self
tapGestureRecognizer.numberOfTapsRequired = 2
webView.isUserInteractionEnabled = true
webView.addGestureRecognizer(tapGestureRecognizer)
// for webView.scrollView
let tapGestureRecognizer1 = UITapGestureRecognizer(target:self, action:#selector(self.goToback))
tapGestureRecognizer1.delegate? = self
tapGestureRecognizer1.numberOfTapsRequired = 2
webView.scrollView.addGestureRecognizer(tapGestureRecognizer1)
If you you assign same gesture recognizer to two object then one of the object doesn't have it...see log
I have a double tap gesture working in my app by doing the following:
I created a subclass of UIWebView that is the UIGestureRecognizerDelegate
class MyWebView: UIWebView, UIGestureRecognizerDelegate {
func gestureRecognizer(_: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith:UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_: UIGestureRecognizer, shouldReceive:UITouch) -> Bool {
return true
}
}
Then in my View Controller class I reference my subclass (make sure it is referenced in Interface Builder also)
#IBOutlet weak var webView: MyWebView!
And in viewDidLoad add the recognizer
let tapGestureRecognizer = UITapGestureRecognizer(target:self, action:#selector(self.goToback))
tapGestureRecognizer.delegate? = self.webView //set the delegate to the custom UIWebView
tapGestureRecognizer.numberOfTapsRequired = 2
webView.isUserInteractionEnabled = true
webView.addGestureRecognizer(tapGestureRecognizer)
Hope this helps, my original code was in Objective-C code so had to do a quick translation here so hope there are no syntax issues.
I have used this code and it works for me flawlessly
override func viewDidLoad() {
super.viewDidLoad()
let tapLockGesture = UITapGestureRecognizer(target: self, action: #selector(tapLock))
tapLockGesture.delegate = self
tapLockGesture.numberOfTapsRequired = 2
webView.addGestureRecognizer(tapLockGesture)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
make sure you conform to UIGestureRecognizerDelegate and UIScrollViewDelegate.
Related
I presented a view controller and I have a subview in it. I added a swipe gesture on the subview. The swipe gesture is not being called. Instead, the presented view controller is trying to dismiss itself i.e. going down. How do I override the swipe gesture to be recognised by the subview instead of the super view.
When I implemented swipe on a static view controller, it works as expected.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var viewSwipe: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let swipeUp = UISwipeGestureRecognizer(target: self, action: #selector(swipeUp(_:)))
swipeUp.direction = UISwipeGestureRecognizer.Direction.up
self.viewSwipe.addGestureRecognizer(swipeUp)
let swipedown = UISwipeGestureRecognizer(target: self, action: #selector(swipeDown(_:)))
swipedown.direction = UISwipeGestureRecognizer.Direction.down
self.viewSwipe.addGestureRecognizer(swipedown)
}
#objc func swipeUp(_ gesture: UISwipeGestureRecognizer) {
print("swiped up")
}
#objc func swipeDown(_ gesture: UISwipeGestureRecognizer) {
print("swiped down")
}
}
The red area needs to get swipe
Add Gesture only in view and you can also add any direction on gesture. Here i put Small example, you can refer this code for your problem.
Get 1 for Right Direction and 2 For Left Direction
Example :
import UIKit
class ViewController: UIViewController,UIGestureRecognizerDelegate{
#IBOutlet weak var viewGray: UIView!
#IBOutlet weak var viewRed: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.interactivePopGestureRecognizer?.delegate = self
let directions: [UISwipeGestureRecognizer.Direction] = [.right, .left]
for direction in directions {
let gesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(sender:)))
gesture.direction = direction
gesture.delegate = self
self.viewRed.addGestureRecognizer(gesture)
}
}
#objc func handleSwipe(sender: UISwipeGestureRecognizer) {
print(sender.direction)
}
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
Your code should work. If not there, is probably conflict with different gesture recognizer. To solve this conflict, you can set delegate on your swipeUp and swipeDown recognizers:
class ViewController: UIViewController, UIGestureRecognizerDelegate
swipeUp.delegate = self
swipeDown.delegate = self
and try to investigate what is happening in this delegate funcs (with breakpoints or with print, it is up to you):
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
If none of this delegate funcs is called, when you are trying to swipe. Then there is probably different (ie invisible) view over you content.
you code will work. Just try to present your view controller as a full screen in case iOS 13. Refer following code.
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc: UIViewController = storyboard.instantiateViewController(withIdentifier: "MyViewController") as! MyViewController
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)
When I am tapping on UIView but Gesture is not working.
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
viewForSHadow.isUserInteractionEnabled = true
viewForSHadow.addGestureRecognizer(tap)
// Do any additional setup after loading the view.
}
func handleTap(_sender: UITapGestureRecognizer) {
print("---------View Tapped--------")
viewForSHadow.isHidden = true
viewForAlert.isHidden = true
}
I just want to perform this action on UIView tap.
You may check in the debug view hierarchy if anything with alpha = 0 is overlapping your viewForSHadow.
You have to implement as follows:
class ViewController: UIViewController, UIGestureRecognizerDelegate {
...
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_sender:)))
viewForSHadow.isUserInteractionEnabled = true
viewForSHadow.addGestureRecognizer(tap)
}
#objc func handleTap(_sender: UITapGestureRecognizer) {
print("---------View Tapped--------")
viewForSHadow.isHidden = true
viewForAlert.isHidden = true
}
...
}
Your code is more than enough to have working UIGestureRecognizer, you should check some other stuff like, is there something else that can consume the user interaction. And also to check if you use
isUserInteractionEnabled = false
to some parent view of viewForSHadow.
You have to use UIGestureRecognizerDelegate
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleDismiss))
tapRecognizer.delegate = self
blackView.addGestureRecognizer(tapRecognizer)
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return true
}
#objc func handleDismiss() {
print("handleDismiss")
}
Reference
Don't forget to set the UIGestureRecognizerDelegate in your class
You need to mention numberOfTapsRequired property of the UITapGestureRecognizer.
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_sender:)))
tap.numberOfTapsRequired = 1
viewForSHadow.isUserInteractionEnabled = true
viewForSHadow.addGestureRecognizer(tap)
}
#objc func handleTapGesture(_sender: UITapGestureRecognizer) {
print("---------View Tapped--------")
// Why are you hiding this view **viewForSHadow**
viewForSHadow.isHidden = true
viewForAlert.isHidden = true
}
Also, make sure you are not doing viewForSHadow.isUserInteractionEnabled = false anywhere in the code.
First of all you code doesn't compile. The handleTap(_:) signature must be like:
#objc func handleTap(_ sender: UITapGestureRecognizer) {
print("---------View Tapped--------")
}
Secondly, you need to first try with the minimal code in a separate project. And by minimal code I mean what you've added in the question.
class ViewController: UIViewController {
#IBOutlet weak var viewForSHadow: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
viewForSHadow.addGestureRecognizer(tap)
}
#objc func handleTap(_ sender: UITapGestureRecognizer) {
print("---------View Tapped--------")
}
}
Try with just the above code and see if you can get it working. The above code is working well at my end without any delegate or numberOfTaps.
So I am trying to create an interface in which there are essentially 3 levels of views possible.
The UIView in charge of deciding the meta-data of new cells
The cells themselves
The background UIScrollView
Everything is a child of the UIScrollView however 1 is displayed on top of 2.
The goal is that all the cells are draggable and the scroll view is scrollable. However when the scroll view is tapped that should trigger the creation of a new node and thus the appearance of view 1 which will disappear upon the press of a submit button.
I have set up just about everything except I have one huge problem. If I tap on a cell I still get the creation view meaning that the TapGestureRecognizer isnt excluding subviews. So here is roughly what is set up:
class Node: UILabel {
var recognizer:UIPanGestureRecognizer?
init() {
self.isExclusiveTouch = true
self.isUserInteractionEnabled = true
recognizer = UIPanGestureRecognizer(target: self, action: #selector(drag))
self.addGestureRecognizer(recognizer!)
}
#objc
func drag(rec: UIPanGestureRecognizer) {
goToPoint(point: rec.location(in: self.superview!))
}
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
var shouldReceive = false
if let clickedView = touch.view {
if clickedView == self {
shouldReceive = true;
}
}
return shouldReceive
}
}
class NodeCreator: UIView {
var submit: UIButton = UIButton(type: UIButton.ButtonType.roundedRect)
init() {
super.init(frame: CGRect.zero)
submit.setTitle("Submit", for: .normal)
submit.translatesAutoresizingMaskIntoConstraints = false
submit.addTarget(self, action: #selector(press), for: .touchUpInside)
self.addSubview(submit)
}
#objc func press() {
if let d = delegate {
if let t = inputField.text {
d.created(title: t, pt: storeP)
self.removeFromSuperview()
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class TaskView: UIScrollView, CreationDelecate{
func created(title: String, pt: CGPoint) {
let temp = Node()
view.addNode(temp, at: pt)
}
var tapRecognizer:UITapGestureRecognizer?
var nodeSet:[Node] = []
func addNode(_ n1: Node, at: CGPoint = CGPoint.zero) {
nodeSet.append(n1)
n1.goToPoint(point: at)
self.addSubview(n1)
self.bringSubviewToFront(n1)
self.isScrollEnabled = true
self.isUserInteractionEnabled = true
self.isExclusiveTouch = false
self.canCancelContentTouches = false
if (tapRecognizer == nil) {
tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(tap))
self.addGestureRecognizer(tapRecognizer!)
}
}
#objc func tap(rec: UITapGestureRecognizer) {
let pos = rec.location(in: self)
let creator = NodeCreator(pt: pos)
creator.delegate = self
self.addSubview(creator)
}
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
var shouldReceive = false
if let clickedView = touch.view {
if clickedView == self {
shouldReceive = true;
}
}
return shouldReceive
}
}
Why is the TaskView still receiving tap gestures when the Nodes are all exclusive touch.
Code does not work because you did not assigned delegate for gesture recognizers. Delegate functions are implemented in Node and TaskView classes. But you did not declared classes as delegates i.e.
class TaskView: UIScrollView, CreationDelecate, UIGestureRecognizerDelegate
Than assign delegate to gesture recognizer:
recognizer.delegate = self as! UIGestureRecognizerDelegate
Should work
I want to write a custom code on double tap on UITextField and block the default text editing and popup of the keyboard. I've tried the following and nothing has worked for me so far. Kindly help me solve this problem.
let gestureArray = NamTxtBoxVal.gestureRecognizers
var tapGesture = UITapGestureRecognizer()
for idxVar in gestureArray!
{
if let tapVar = idxVar as? UITapGestureRecognizer
{
if tapVar.numberOfTapsRequired == 2
{
tapGesture = tapVar
NamTxtBoxVal.removeGestureRecognizer(tapGesture)
}
}
}
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(namFnc(_:)))
doubleTap.numberOfTapsRequired = 2
doubleTap.delegate = self
tapGesture.requireGestureRecognizerToFail(doubleTap)
NamTxtBoxVal.addGestureRecognizer(doubleTap)
I've also tried:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool
{
return false
}
The only way I know of for you to do this is to
put a UIView behind the UITextField
set the textField's userInteractionEnabled = false
add the double tap gesture to the UIView
This will allow you to register a double tap on the TextField area and not popup any keyboard or enter editing mode.
Not sure what you plan on doing with the textField after double tap but you should be able to handle most stuff programmatically at this point.
Code for this is:
class ViewController: UIViewController {
#IBOutlet weak var myViewBehindMyTextField: UIView!
#IBOutlet weak var myTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.myTextFieldTapped(_:)))
tapGesture.numberOfTapsRequired = 2
myViewBehindMyTextField.addGestureRecognizer(tapGesture)
}
func myTextFieldTapped(sender: UITapGestureRecognizer) {
print("Double tapped on textField")
}
}
I am trying to exclude a subview from my TapGestureRecognizer.
It has been created in sotryboard, the delegate is connected to the controller.
In my ViewControler I have the GestureRecognizerDelegate protocol and I ve set my gesture.delegate = self.
Though the shouldReceiveTouch Gesturerecognizer function is not calling, any idea ?
Here is somne of the code :
class DetailedPostViewController: UIViewController, UITextViewDelegate, MKMapViewDelegate, UIGestureRecognizerDelegate {
let circularLike = CircularLike(frame: CGRectZero)
#IBOutlet var gesture: UITapGestureRecognizer!
override func viewDidLoad() {
super.viewDidLoad()
gesture.delegate = self
self.view.addSubview(circularLike)
circularLike.frame = self.view.bounds
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if touch.view!.isDescendantOfView(circularLike){
return false
}
return true
}
#IBAction func UserTap(sender: AnyObject) {
if fromUser {
dismissViewControllerAnimated(true, completion: nil)
} else {
performSegueWithIdentifier("userPage", sender: nil)
}
}
#IBAction func hideUnhide(sender: UIGestureRecognizer) {
if hide {
hide=false
unhideUi()
} else {
hide = true
hideUi()
}
}
}
ok, In fact, I just had to set my subview userTouchEnabled parameter to false, and it then triggered the ShouldRecieveTouch Function ...