How to implement a Longpress Gesture on a UITextView - ios

Is it possible to implement a long press gesture on a UITextView? Basically, if the user tap once on a textview I want him/her to be able to edit the text. However, if he/she tap and hold on the textview (let's say for two seconds) an action will be performed? If the answer is yes please give me direction on how this could be achieved?
Follows is the solution for my problem as per kchromik's asnwer:
(1) First step is define the following extension before the ViewController Class begins:
extension ViewController: UIGestureRecognizerDelegate { func gestureRecognizer (_ gestureRecognizer: UIGestureRecognizer, shouldRecognizerSimultaneouslyWithotherGestureRecognizer: UIGestureRecognizer) -> Bool { return true}}
(2) Second step is to link the UITextView Outlet from the Main Storyboard to the swift code file:
#IBOutlet weak var testTextView: UITextView!
(3) Third step is to drag and drop a GestureRecognizer from the Object Library on top of the UITextView you want to implement a Longpress Gesture Recognizer on.
(4) The Fourth step is add the below code under viewDidLoad () {
let uilpgr = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.longpress(gestureRecognizer:)))
uilpgr.minimumPressDuration = 2
testTextView.addGestureRecognizer(uilpgr)
uiplgr.delegate = self
}
(5) The last step is to define the function to be used with the Longpress Gesture Recognizer defined earlier on:
func longpress(gestureRecognizer: UIGestureRecognizer) {
print("Long tap") // Execute what you want to do
}

By default a UILabel has user interactions disabled. Try testLabel.isUserInteractionEnabled = true in your viewDidLoad or enable it in the storyboard:
UPDATE
If you UIView has it's own gesture recogniser, you can implement following delegate:
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
And you don't forget to set uilpgr.delegate = self

Related

Triggering UITapGestureRecognizers from overlapping Views

I have one main view and 4 subviews of the mainview they all have their UITapGestureRecognizer, when I tap on one subview how can it be triggered both views. Example below,
if I tap to subview 1 desired log would be:
subview1 is clicked
MainView is clicked
My Code
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let mainGesture = UITapGestureRecognizer(target: self, action: #selector(mainGestureActivated))
self.view.addGestureRecognizer(mainGesture)
let subGesture = UITapGestureRecognizer(target: self, action: #selector(subViewGestureActivated))
self.subview1.addGestureRecognizer(subGesture)
}
#objc func mainGestureActivated(){
print("MainView Clicked")
}
#objc func subViewGestureActivated(){
print("Subview Clicked")
}
it prints only subview clicked! Is it possible to trigger both gestureRecognizers since main is encapsulating other.
First you should conform to UIGestureRecognizerDelegate in your VC, and then implement the delegate func of shouldRecognizeSimultaneouslyWith. Inside the function, you should detect if the gestureRecognizer, and the otherGestureRecognizer are the wants you want, and if so, you should allow them to work simultaneously,
Conform to delegate, and Declare gesture recognizers outside of viewDidLoad (because you need to access them in the delegate method later.)
var mainGestureRecognizer = UITapGestureRecognizer()
var subGestureRecognizer = UITapGestureRecognizer()
Initialize your recognizers, and set your VC as their delegate:
mainGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(mainGestureActivated))
subGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(subViewGestureActivated))
mainGestureRecognizer.delegate = self
subGestureRecognizer.delegate = self
Implement the delegate function mentioned above to allow simultaneous recognition for subView and mainView:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer == subGestureRecognizer && otherGestureRecognizer == mainGestureRecognizer {
return true
}
return false
}
And if you want it to work for 4 different subviews, then you should check with else if statements inside the delegate method.

CollectionView Cells Need Two Taps Because of GestureRecognizer

I have added GestureRecognizer for hide keyboard when user click to anywhere in view without textview. Its working well but CollectionView Cells need two tap for work, when I delete GestureRecognizer its working well but I need both of them. I have searched this in couple hours and tried so many solutions but anything is not worked.
GestureRecognizer for hide keyboard when user click to anywhere in view without textview;
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
tapRecognizer.cancelsTouchesInView = false
view.addGestureRecognizer(tapRecognizer)
#objc func handleTap() {
textBody.endEditing(true)
}
You can try this method:
public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true) // or textBody.endEditing(true)
}
To recognize both gestures simultaneously use below code by removing cancelsTouchesInView line. You may need to add another tap gesture for your collectionView according to your need. Or use scrollViewDidScroll event of collectionView to hide the keyboard.
//tapRecognizer.cancelsTouchesInView = false
tapRecognizer.delegate = self
view.addGestureRecognizer(tapRecognizer)
extension MyViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}

didSelectItemAt not called when using UIPanGestureRecognizer in View

I am using both a tap and pan gesture in my View. The view has a UICollectionView where I am trying to call didSelectItemAthowever the method is not called.
I have tried the following, but with no luck.
override func viewDidLoad() {
panGesture.delegate = self
tapGesture.delegate = self
}
extension AddNotebookViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
Does anybody have any idea what the issue may be ?
The problem, as you've already guessed, is that the background view's gesture recognizer swallows the tap that would select the collection view cell. To solve the problem, implement this gesture recognizer delegate method in your view controller:
func gestureRecognizerShouldBegin(_ gr: UIGestureRecognizer) -> Bool {
let p = gr.location(in: self.view)
let v = self.view.hitTest(p, with: nil)
return v == gr.view
}
The result is that if the gesture is in the collection view, the background view's gesture recognizer won't begin and normal selection will be able to take place.

How to disable CollectionView's vertical gesture recognizer?

I am using LNPopupController framework for presenting View Controllers, the presented VC can be dismissed by dragging it down, however I have added a collection view in this VC and dragging finger in its area doesn't drag the VC down. I want this CollectionView to scroll horizontally, I tried subclassing UICollectionView and use the
gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool
method but it doesn't make any difference.
Here is my code:
class MyCollectionView: UICollectionView, UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
in viewDidLoad()
func setupCollection() {
cellSize = CGSize(width: self.view.frame.width, height: self.view.frame.width)
collectionView.delegate = self
collectionView.dataSource = self
if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
layout.scrollDirection = .horizontal
}
//collectionView.isScrollEnabled = false
collectionView.isPagingEnabled = true
collectionView.alwaysBounceVertical = false
}
when scrolling is disabled everything works perfectly, I can close the view by dragging the CollectionView's area.
First, I would implement UIGestureRecognizerDelegate in the view controller that contains the collection view:
extension MyViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
Then you also need to set this delegate to the proper gesture recognizer - in your code I do not see any such line that would assign the delegate to none of the gesture recognizers. I would assign it to the gesture recognizer that is responsible for dismissing the viewController:
self.popupContentView.popupInteractionGestureRecognizer.delegate = self
Call this line of code in viewDidLoad of the MyViewController (I use this name since you did not mention the name of your controller). Here I assume that popupContentView.popupInteractionGestureRecognizer is the pan gesture recognizer that dismisses the view controller (I checked quickly LNPopupController).
Author of LNPopupController here.
The other answer is correct. You need to implement the gesture recognizer's delegate and exclude it from interaction (or do even more complex stuff).
To do this, set the controller.popupContentView.popupInteractionGestureRecognizer.delegate property. Make sure controller is the actual controller you call presentPopupBarWithContentViewController:animated:completion: on.

GestureRecognizer taking all Touch Input

I have this Setup in my Storyboard.
In my first ViewController Scene I have a MapView from MapBox. In there I have put a TextField (AddressTextField). On that TextField when touching the view, i'm running self.addressTextField.resignFirstResponder(), but after that neither the mapview, nor any other element in there or in the Embedded Segues react on a touch or click. Probably this is because I didn't completely understand the system of the First Responder. I'm thankful for every help.
Edit 1:
I think I know what's going on now, but I don't know how to fix it. When I add the Gesture Recognizer to the View (or to the mapView, that doesn't matter), the other UIViews and the MapView do not recognize my Tap-Gestures anymore. When I am not adding the Recognizer everything works fine. It seems as if the Gesture Recognizer is recognizing every tap I make on either the UIViews or the MapView and therefore other gestures are not recognized.
Edit 2:
I just added a print() to dismissKeyboard(). As soon as any Touch Event gets recognized on the MapView or the other UIViews, dismissKeyboard() gets called. So I think my thought of Edit 1 was correct. Does anyone know how I can solve this, so that it's not only dismissKeyboard() that gets called ?
Some Code:
func dismissKeyboard(){
self.addressTextField.resignFirstResponder()
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
dismissKeyboard()
return true
}
//Class (only partially)
class ViewController: UIViewController, MGLMapViewDelegate, CLLocationManagerDelegate, UITextFieldDelegate {
override func viewDidLoad(){
mapView.delegate = self
addressTextField.delegate = self
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
self.mapView.addGestureRecognizer(tap)
}
}
Others are just #IBActions linked to the Buttons, or other elements.
try this:
func dismissKeyboard(){
view.endEditing(true)
}
hope it helps!
After I knew the real issue I was able to solve the problem. I declared a var keyboardEnabled. Then I added these lines to my class.
class ViewController: UIViewController, UIGestureRecognizerDelegate {
var keyboardEnabled = false
override func viewDidLoad(){
super.viewDidLoad()
//Looks for single tap
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
self.mapView.addGestureRecognizer(tap)
}
/* Setting keyboardEnabled */
//Editing Target did end
#IBAction func editingTargetDidEnd(_ sender: Any) {
keyboardEnabled = false
}
//Editing TextField Started
#IBAction func editingAdressBegin(_ sender: Any) {
keyboardEnabled = true
}
//Call this function when the tap is recognized.
func dismissKeyboard() {
self.mapView.endEditing(true)
keyboardEnabled = false
}
//Implementing the delegate method, so that I can add a statement
//decide when the gesture should be recognized or not
//Delegate Method of UITapGestureRecognizer
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return keyboardEnabled
}
}
With this solution keyboardEnabled takes care of deciding wether my UIGestureRecognizer should react or not. If the Recognizer doesn't react, the Gesture is simply passed on to the UIViews or other Elements that are in my MapView.
Thanks for all your answers!

Resources