I have a tap gesture recognizer that I use to dismiss keyboard and store some data from a textfield. On the same view I have a tableview with didSelectRowAtIndexPath. When I tap on a row my gesture recognizer gets called instead of uitableview method. I've searched stack overflow and found some obj c solutions that I tried to implement. The solution is to implement cancelsTouchesInView and set it to NO. Below is my tap gesture recognizer function that doesn't seem to work. Am I doing something wrong ?
func addTapGestureRecognizer(){
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(detailViewController.dismissKeyboard))
self.view.addGestureRecognizer(tap)
tap.cancelsTouchesInView = false;
}
This is my didSelectRowAtIndexPath
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("Stisnut Redak")
let selectedRowObject = arrayForSteps[indexPath.row]
if selectedRowObject.status == false {
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .None
} else {
tableView.cellForRowAtIndexPath(indexPath)?.accessoryType = .Checkmark
}
}
the way that I would implement this is to use the:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
delegate method from the UIGestureRecognizerDelegate protocol (you need to make sure you declare that the controller which has scope over the table view, keyboard and text field signs up to this protocol). This will intercept the tap gesture and ask the function to determine whether the tap should be handled by the tap gesture recognizer or some other object.
So what you could do in your implementation of this method is check the following:
if self.myTableViewController.tableView.frame.contains(touch.location(in: self.view)) {
return false // The touch will be handled by the table view instead
}
return true // The touch will be handled by the tap gesture recognizer, etc.
The nice thing about this is that you don't have to add any further code to handle the return false case as the touch will then be handled by the next appropriate responder automatically.
(Another way you could do this if you did not want to use the above would be to create an action that tests whether the keyboard is currently being presented within the viewController's view, and decide on the basis of that what action to take.)
Hope that helps!
You can do this.
Take a BOOL as an instance variable isKeyboardActive and set it to YES in textFieldDidBeginEditing and set it to NO in textFieldDidEndEditing. Also set it to NO in viewDidLoad.
Now implement this method -(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event and check if isKeyboardActive is YES. If so then implement [self.view endEditing:YES].
If this doesn't work or is not required then please explain your question further so that we can help it out.
Related
I have a UITextView to which I have attached a gesture recognizer as follows:
let testTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(textTextViewTapped(gestureRecognizer:)))
testTapGestureRecognizer.cancelsTouchesInView = false
testTapGestureRecognizer.delaysTouchesBegan = false
testTapGestureRecognizer.delaysTouchesEnded = false
if textTextView != nil {
textTextView!.addGestureRecognizer(testTapGestureRecognizer)
}
The selector mentioned above is as follows:
#objc func textTextViewTapped(gestureRecognizer: UIGestureRecognizer) {
print("testTextViewTapped called.")
}
Every time I tap the UITextView, I can see the message above printed on the console. However, the keyboard doesn't appear any more.
I found Apple's doc confusing here:
Here, it says that
A gesture recognizer doesn’t participate in the view’s responder
chain.
which I am interpreting as that any gestures are also sent to the view and up the chain, as is normal.
Later on the same page, it says,
If a gesture recognizer recognizes its gesture, the remaining touches
for the view are cancelled.
which means that if an attached gesture recognizer is able to understand a gesture as the one it is supposed to recognize, then it will prevent it from being delivered to the view to which it is attached. Further, it specifies 3 properties that should be able to stop the gesture recognizer from doing that. I have set all three of them to false in my code, as shown above.
What is actually happening here and how do I allow the UITextView to interpret the touches normally and also be able to use a gesture recognizer?
You could use the UIGestureRecognizerDelegate to make the UITapGestureRecognizer work along the regular UITextView behaviour:
class TestViewController: UIViewController {
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tap))
tapGestureRecognizer.delegate = self
textView.addGestureRecognizer(tapGestureRecognizer)
}
#objc private func tap() {
print("tap")
}
}
extension TestViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
The UITextView probably has its own private gesture recognizer to handle when a user taps on it. When this happens it makes the text view the first responder, which causes the keyboard to appear. Gesture recognizers can force other gesture recognizers to fail when they recognize their gesture. (See the docs) Perhaps this is what is happening when you add your tap gesture. It recognizes the tap and thus forces other gestures to fail, which prevents the text view from becoming the first responder.
The best solution is to follow the answer from this question (as was mentioned by #FrancescoDeliro in the comments) and use the delegate calls to know when editing is beginning/ending.
I am a beginner in Swift, and am trying to add a swipe gesture recognizer to my UIView. I have inserted a gradient CALayer to index 0 to have a gradient background.
My problem is:
Swipe gestures for right and left work fine, but for Down it doesn't work, why?
Set the delegate of swipe gestures that you are adding to the view.
let swipeGesture = UISwipeGestureRecognizer(target: self, action: Selector("handleSwipe:"))
swipeGesture.delegate = self
self.view.addGestureRecognizer(swipeGesture)
self.mySwipeGesture = swipeGesture
GestureRecognizerDelegate asks if two gesture recognizers should be allowed to recognize gestures simultaneously. Return true to allow both gestureRecognizer and otherGestureRecognizer to recognize their gestures simultaneously. The default implementation returns false—no two gestures can be recognized simultaneously. Implement the following delegate to achieve this.
extension ViewController : UIGestureRecognizerDelegate {
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
//Identify gesture recognizer and return true else false.
return gestureRecognizer.isEqual(self.mySwipeGesture) ? true : false
}
}
Swiping Up & Down are the default property of table view. I would suggest you to disable the scrolling of the table view whenever you want to do something on the overlay.
tableView.scrollEnabled = NO;
If you are performing dragging of a particular cell then long press on it and then start dragging.
This is how you can achieve this.
Hope this helps.
I have a uipageviewcontroller and the pages have an area on the screen where there is a uitableview. I want the user to only be able to swipe through pages outside of that uitableview.
I can't seem to find where these gesture recognizers are hiding. I am setting them up as delegates like this:
self.view.gestureRecognizers = self.pageViewController?.gestureRecognizers
for gesture in self.view.gestureRecognizers!{
// get the good one, i discover there are 2
if(gesture is UIPanGestureRecognizer)
{
println("ispan")
// replace delegate by yours (Do not forget to implement the gesture protocol)
(gesture as! UIPanGestureRecognizer).delegate = self
}
}
I am seeing ispan in the logs so it seems to find some uipangesturerecognizer but when I override the function like this:
func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
println("gesture should begin")
var point = gestureRecognizer.locationInView(self.view)
return true
}
it doesn't print out "gesture should begin" at all... I have the class set as a UIGestureRecognizerDelegate what am I doing wrong? I'm guessing I have the wrong gesture recognizers set as delegates how can I set the correct ones as delegates?
Could something like this work?
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if(touch.view == <your tableView>){
return false
}else{
return true
}
}
You might need to also test which gestureRecognizer it is (the one from the pageView or the one from the tableView).
I have a collection view of bars that can slide up and down. Each cell in the collection view is using a UIPanGestureRecognizer to control the blue bar sliding up and down. The collection view does not scroll here.
There is an "edit mode" which disables the pan gesture controlling the bars. The hope here is that in "edit mode", the collection view can then scroll left and right.
My attempt at doing this was to disabled the pan gesture in each of the cells. I also tried using the UIGestureRecognizerDelegate methods to try to disable touches and fail the cell's pan gesture in favor of the collection view's pan gesture. It seems that the collection view's pan gesture is not forwarded to any of the cell's gesture delegate calls.
These delegate calls got me the closest (delegate for cell's pan gesture):
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
return !self.editMode // bar doesn't need to pan in edit mode
}
With this implemented, I could pan the collection view if I started the pan in the white space between cells. Starting a pan on a cell would not do anything, though.
EDIT: I uploaded a sample project of the issue to github.
If I understand this correctly, I think the problem could be as follows:
Your UICollectionView will listen for pan gestures. So far so good.
In KBHBarSlider you add UIPanGestureRecognizer for you sliders. They have "priority" over the underlaying UICollectionView.
This actually makes sense as your BarSliders are above the CollectionView. My best take would be to let the BarSliders add the UIPanGestureRecognizer when needed, and have them completely removed when editing.
So first try and remove the gestureRecognizer you added in VOLBarSliderCell.
Then in KBHBarSlider add something like: (and make sure your init calls addPanRecognizer() instead of setup())
func addPanRecognizer() {
let panGesture = UIPanGestureRecognizer(target: self, action: "viewDidPan:")
self.gestureRecognizers = [panGesture]
}
func removePanRecognizer() {
self.gestureRecognizers = []
}
It is now possible to add and remove the gestures on demand, do so from your edit get/set overrides in VOLBarSliderCell:
var editing: Bool = false {
didSet {
if !self.editing {
self.barSlider.addPanRecognizer()
// Removed code, to keep this example short...
} else {
self.barSlider.removePanRecognizer()
// Removed code, to keep this example short...
}
}
}
Try adding this code at VOLBarSliderCell:29:
self.panGesture.enabled = !self.editing
And remove:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if self.editing && gestureRecognizer == self.panGesture {
return false
}
return true
}
To be fair your problem was not clear to me, so I gave a try as an answer anyway since I cannot comment yet to request clarifications.
I understood you wanna scroll your UICollectionView while you are in edit mode. So that's what the code above does. If you wish to disable scrolling altogether UICollectionView while NOT editing then you can use scrollEnabled = false on the UICollectionView.
Clarify a bit better what is the problem you are trying to solve. That way you might be able to bring precise answers.
You're on the right track. Keep a reference to your leftRight gestureRecognizer. Then in shouldReceiveTouch, check if editing and the gestureRecognizer is your leftRight recognizer
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if(self.editing && gestureRecognizer != self.leftRightRecognizer){
return 0;
}
return 1;
}
My app has a table view (with a scroll-into of course) and this view slides on and off with a gesture recognizer (like on the Facebook app).
If I use a button to slide [the table view onto the screen], it works fine but when I use a gesture recognizer, the table view can't be scrolled anymore.
Here is the code of gesture recognizer with the problem:
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
Somebody have an idea?
Your gesture is probably preventing the scroll view gesture from working because by default only 1 gesture can be recognising at a time. Try adding yourself as the delegate of your gesture and implementing:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
self.slidingViewController.panGesture.delegate = self;
also, add <UIGestureRecognizerDelegate> to the list of protocols you implement
I have used UIPangesture in my UItableview and to avoid this gesture I have used below delegate,
//This method helped me stopped up/down pangesture of UITableviewCell and allow only vertical scroll
override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
if let panGestureRecognizer = gestureRecognizer as? UIPanGestureRecognizer {
let translation = panGestureRecognizer.translationInView(superview)
if fabs(translation.x) > fabs(translation.y) {
return true
}
return false
}
return false
}
Here is the swift version:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
I had same issue of defining long press gesture on table view and not being able to scroll table when I long press on it.
Fixed by:
1- adding
UIGestureRecognizerDelegate
2- adding
gesture.delegate = self (after you defined the long press gesture)
3- adding this function:
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {return true}
If I get it right the view that you're adding the gesture recognizer to is the table view. By default the UIScrollView (and implicitly UITableView) class uses the pan gesture recognizer for the scroll and your gesture recognizer interferes with that. If you use another view as a container for the table view and you're adding the pan gesture recognizer to it should work.