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 ...
Related
I'm having a problem with the pull-down gesture that is not working. I'm presenting an UIPageViewController and each pages is a UIViewController containing an UITableView. when the transition style is set to pageCurl there is not problem with the pull-down gesture to dismiss but when the transition style is set to scroll, I can't dismiss the view. Only the UITableView is scrolling even when at the top
final class HistoryReceiptContainerViewController: UIPageViewController {
private let viewModel: HistoryReceiptContainerViewModel
init(viewModel: HistoryReceiptContainerViewModel) {
self.viewModel = viewModel
super.init(transitionStyle: .pageCurl, navigationOrientation: .horizontal, options: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setViewControllers([receiptViewController(for: HistoryReceiptViewModel(historyId: "1234", source: "", context: nil, contextId: nil))], direction: .forward, animated: true, completion: nil)
}
private func receiptViewController(for viewModel: HistoryReceiptViewModel) -> HistoryReceiptViewController {
let vc = HistoryReceiptViewController(viewModel: viewModel)
return vc
}
}
And the code to present the view
present(HistoryReceiptContainerViewController(viewModel: viewModel)
Do you have any solutions for that kind of problem ?
Thank you in advance!
https://imgur.com/a/rwkVA0Q
Here is an example implementation to fix dismissing a UIPageViewController with transitionStyle .scroll by adding a UIPanGestureRecognizer and some custom gesture recognizer handling. It will work when pushing the UIPageViewController on a navigation stack as well as when presenting it modally.
class DemoPageViewController: UIPageViewController, UIGestureRecognizerDelegate {
var presentedModally = false
init(presentedModally: Bool = false) {
super.init(transitionStyle: .scroll, //if you would use .pageCurl dismissing works
navigationOrientation: .horizontal,
options: nil)
self.presentedModally = presentedModally
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
setUpPanGestureForDismissingSelf()
}
// MARK: - UIGestureRecognizerDelegate
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
guard let navigationController = navigationController, navigationController.interactivePopGestureRecognizer == gestureRecognizer else {
return true
}
return navigationController.viewControllers.count > 1
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return !presentedModally
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return presentedModally
}
private func setUpPanGestureForDismissingSelf() {
guard presentedModally else { return }
let panGestureRecognizer = UIPanGestureRecognizer(target: self,
action: #selector(handlePanGesture(sender:)))
panGestureRecognizer.delegate = self
view.addGestureRecognizer(panGestureRecognizer)
}
#objc func handlePanGesture(sender: UIPanGestureRecognizer) {
let dragVelocity = sender.velocity(in: view)
if dragVelocity.y >= 1300 {
dismiss(animated: true)
}
}
}
I create this basic tap gesture function for dismissing view and I added a delegate to override tap when the user taps on another custom sheet (container).
#objc func handleTapGesture() {
dismiss(animated: true, completion: nil)
}
private func tapGestureToDissmis() {
let tap = UITapGestureRecognizer()
tap.addTarget(self, action: #selector(handleTapGesture))
tap.delegate = self
view.addGestureRecognizer(tap)
}
extension TextConfigurationVC : UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view?.isDescendant(of: container) == true {
return false
}
return true
}
My problem is that I want to implement this for 8 screens and I don't want to repeat my self. i create in my UIViewcontroler extension file this tap function and I don't now how to pass this view :
extension UIViewController : UIGestureRecognizerDelegate {
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view?.isDescendant(of: **<UIView>)** == true {
return false
}
return true
}
when I created the global container, each time it was called, it was overlapping each other
(tap x 6)
Simple way to handle individual tap action for views
extension UIView {
private struct OnClickHolder {
static var _closure:()->() = {}
}
private var onClickClosure: () -> () {
get { return OnClickHolder._closure }
set { OnClickHolder._closure = newValue }
}
func onTap(closure: #escaping ()->()) {
self.onClickClosure = closure
isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(onClickAction))
addGestureRecognizer(tap)
}
#objc private func onClickAction() {
onClickClosure()
}
}
Usage:
override func viewDidLoad() {
super.viewDidLoad()
let view = UIView(frame: .init(x: 0, y: 0, width: 80, height: 50))
view.backgroundColor = .red
view.onTap {
print("View Tapped")
}
}
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'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.