I placed a small UIView inside a UITableViewCell.
In case of a tapping on such UIView, I would like to open a popup.
In case of a tapping outside such UIView, I would like to perform what it's defined in the UITableView "didSelectRowAtIndexPath" function.
What it's happening at the moment is that when I click on the view both things happen: the popup is opened and the "didSelectRowAtIndexPath" function is trigged.
How can I make sure that when I click on that UIView the "didSelectRowAtIndexPath" function is not triggered?
current implementation:
I defined a UITapGestureRecognizer for the UIView inside my custom UITableViewCell class.
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(MyCustomTableViewCell.clickOnHelp(_:)))
myClickAreaUIView.addGestureRecognizer(tapGesture)
The best solution I suggest is to change the UIView to a UIButton and handle the touch with a touchUpInside on the button.
This way you will reach your objective, as UIButton automatically prevents touch event forwarding to superview
Try setting the inner view's exclusiveTouch property to true.
cell.tapView.exclusiveTouch = true
You should use this method from UIGestureRecognizerDelegate
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
Create a customClass for your UIView lets say CustomView of type UIView
Connect your UIView's #IBOutlet conforming to tableViewCell of type CustomView
In CustomView class call this function
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
super.pointInside(point, withEvent: event)
return CGRectContainsPoint(self.bounds, point)
}
This will tell you whether or not the location at which user tapped was contained inside that UIView bounds or not, if it returns true do pop up in your UIGestureRecognizer functions and follow https://stackoverflow.com/a/18606840/6297658 , if false do the opposite
I suggest to implement the following method of UIGestureRecognizerDelegate like below
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
print("in shouldReceiveTouch")
gestureRecognizer.delegate = self
if ([...]){ // touch location is in the UIView
print("touching UIView")
return false // prevent from forwarding touch to superview
} else {
println("touching elsewhere")
return true
}
}
just write this code inside cellForRowAtIndexPath() for did select prevent::
cell.selectionStyle = UITableViewCellSelectionStyle.None
Related
I have a problem where I have a UIScrollView and a Header (UIView) inside my main View and my Header is over my UIScrollView as such:
UIView.
|
|- UIScrollView.
|
|- Header. (UIView)
I want my header to be able to detect taps on it, but I also want my scroll view to be able to scroll when I drag over my Header which right now it is not possible because my Header is over it and is blocking the scroll.
To sum up, I want my Header to detect taps but forward scrolls to my UIScrollView.
To tackle this problem I tried multiple things, and here are some of them:
Adding a UIPanGestureRecognizer to my Header so it is able to detect dragging
Adding a UITapGestureRecognizer to my Header so it is able to detect tapping
Setting isUserInteractionEnabled = false when dragging begins so the gesture can be passed to the next UIResponder which in this case is my UIScrollView
Setting isUserInteractionEnabled = true once my dragging has finished so it can again detect tapping
This is the code snippet:
override func viewLoad() {
myScreenEdgePanGestureRecognizer = UIPanGestureRecognizer(target: self, action:#selector(handlePan))
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action:#selector(handleTap(_:)))
headerView.addGestureRecognizer(myScreenEdgePanGestureRecognizer)
headerView.addGestureRecognizer(tapGestureRecognizer)
}
#objc func handlePan(_ sender: UITapGestureRecognizer){
print("dragging")
if headerView.isUserInteractionEnabled{
headerView.isUserInteractionEnabled = false
}
if sender.state == .began {
} else if sender.state == .ended {
headerView.isUserInteractionEnabled = true
}
}
#objc func handleTap(_ sender: UITapGestureRecognizer){
print("tapped")
}
At this point I see how dragging and tapping are being detected just fine, but for some reason isUserInteractionEnabled = false seems to not be changing how the view is behaving. This code is acting as isUserInteractionEnabled is always true no mater what.
Things that I have also tried besides this:
overriding the hitTest function inside UIButton
overriding touchesBegan, touchesMoved, touchesEnded methods overriding next
setting the variable to return ScrollView as the next UIResponder
setting the isExclusiveTouch method in UIButton
changing the isUserInteractionEnabled in every way possible
I was struggling with this problem too, you should try to use methods of UIGestureRecognizerDelegate which allows you to handle simultaneous gestures.
Connect your gesture recognizers delegates e.g. tapGestureRecognizer.delegate = self
Make your ViewController conform this protocol e.g.
extension YourViewController: UIGestureRecognizerDelegate {}
Implement this function:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true }
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.
Good day, I have UIImageView inside UIView and I want to handle event, when user tap on UIView background, but not on UIImageView. How can I achieve it? I read about UITapGestureRecognizer, but it confuse me a little.
P.S. If u can plz answer in swift.
If you want to disable UITapGestureRecognizer on the child view of your main view then you have to enable User Interaction of your UIImageView like this
imgTest.userInteractionEnabled = true;
then you have to set the Delegate of your UITapGestureRecognizer to detect the UITouch of the view, set the delegate like this
let touch = (UITapGestureRecognizer(target: self, action: "tapDetected:"))
touch.delegate = self
self.view.addGestureRecognizer(touch)
Then you have to implement the Delegate method of UITapGestureRecognizer in which you will detect the view which is currently touched like this
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if touch.valueForKey("view")!.isKindOfClass(UIImageView) {
return false
} else {
return true
}
}
In the above code i have disable the tap gesture on UIImageView
Now, this method will work only for your self.view not for UIImageView
func tapDetected(sender: UITapGestureRecognizer) {
self.dismissViewControllerAnimated(false, completion: nil)
}
Add this line for your imageView because UIImageView default user interaction is disable. If you will enable UIImageView userinteraction then it will not work for your UIView and outside of UIImageView it will work
yourImage.userInteractionEnabled = YES;
Try this your tap clicked will work
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;
}