Parent Controller blocking child table view UITableViewRowAction swift - ios

I am using library call Page-Menu (https://github.com/PageMenu/PageMenu) which allow me to add child view controllers in view controller. i can scroll between this view controllers.In one of the child view controller I'm using UITableView.i am using UITableViewRowAction to delete cell but. Don't know why but table not detecting swipe gesture. if i disable Page-menu scrolling then table-view UITableViewRowAction working. i searched lots about this in google. i didn't find any solution. Thanks

Having played a bit with the library, I found the issue is to do with disabling scroll view gesture recogniser in the CAPSPageMenu view controller.
Follow these steps to enable table view editing in one of the child view controllers.
Subclass a UIScrollView like this for the PageMenu target:
class CustomScrollView : UIScrollView, UIGestureRecognizerDelegate {
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
gestureRecognizer.cancelsTouchesInView = false
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return ((self.delegate as! CAPSPageMenu).gestureDelegate?.gestureRecognizerShouldRecognizeSimultaneouslyWith(gestureRecognizer, otherGestureRecognizer: otherGestureRecognizer))!
}
}
Then in CAPSPageMenu, replace:
let controllerScrollView = UIScrollView()
with
let controllerScrollView = CustomScrollView()
Declare a protocol in the same file:
#objc public protocol CustomGestureDelegate {
func gestureRecognizerShouldRecognizeSimultaneouslyWith(_ gestureRecognizer: UIGestureRecognizer, otherGestureRecognizer: UIGestureRecognizer) -> Bool
}
And add this property to CAPSPageMenu:
open weak var gestureDelegate: CustomGestureDelegate?
Here I have made controllerArray as a property in ViewController so we can filter out the view controllers with a table view later.
var controllerArray : [UIViewController] = []
Then make ViewController conform to CustomGestureDelegate with ViewController: UIViewController, CustomGestureDelegate, and set this in viewDidLoad:
pageMenu!.gestureDelegate = self
Implement this function in ViewController (your container view controller):
func gestureRecognizerShouldRecognizeSimultaneouslyWith(_ gestureRecognizer: UIGestureRecognizer, otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if pageMenu?.currentPageIndex == 0 {
return (controllerArray[0] as! CustomGestureDelegate).gestureRecognizerShouldRecognizeSimultaneouslyWith(gestureRecognizer, otherGestureRecognizer: otherGestureRecognizer)
}
return true
}
You can add similar logic for other child view controllers with table views. Here I assumed only the initial view controller had a table view.
Make your child view controllers conform to CustomGestureDelegate by adding the following implementation:
class TestTableViewController: UITableViewController, CustomGestureDelegate {
func gestureRecognizerShouldRecognizeSimultaneouslyWith(_ gestureRecognizer: UIGestureRecognizer, otherGestureRecognizer: UIGestureRecognizer) -> Bool {
let location = gestureRecognizer.location(in: self.view)
let path = self.tableView.indexPathForRow(at: location)
if path != nil {
gestureRecognizer.isEnabled = false
}
gestureRecognizer.isEnabled = true
return true
}
}
I hope this helped you.
UPDATE:
Check out https://github.com/LemonSpike/PageMenu to see a demo of this for PageMenuDemoStoryboard target.

Related

Swipe gesture is not being called on a subview inside a present view controller

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)

UIGestureRecognizer recognize order problem

I have a red view and a green view (green view is a subview of redview),and I add a gestureRecognizer to each of them.
And both grestureRecognizer's shouldRecognizeSimultaneouslyWith return YES.
But when I tap the green view , the redview's gesture delegate method shouldRecognizeSimultaneouslyWith is called first .
Because i think the hit-test view should be the green view . so the green view 's shouldRecognizeSimultaneouslyWith should be the first .
How do the iOS to decide which grestureRecognizer should be recogonized first ?
Post the code and print logs:
class GreenView: UIView,UIGestureRecognizerDelegate{
override func awakeFromNib() {
let gesture = UITapGestureRecognizer(target: self, action: #selector(greenTap))
gesture.delegate = self
self.addGestureRecognizer(gesture)
}
#objc func greenTap(){
print("greenTap")
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
print("greenTap shouldRecognizeSimultaneouslyWith")
return true
}
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
print("green gestureRecognizerShouldBegin")
return super.gestureRecognizerShouldBegin(gestureRecognizer)
}
}
class RedView: UIView,UIGestureRecognizerDelegate{
override func awakeFromNib() {
let gesture = UITapGestureRecognizer(target: self, action: #selector(redTap))
gesture.delegate = self
self.addGestureRecognizer(gesture)
}
#objc func redTap(){
print("redTap")
}
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
print("redTap gestureRecognizerShouldBegin")
return super.gestureRecognizerShouldBegin(gestureRecognizer)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
print("redTap shouldRecognizeSimultaneouslyWith")
return true
}
}
and the prints are :
green gestureRecognizerShouldBegin
redTap gestureRecognizerShouldBegin
green gestureRecognizerShouldBegin
redTap shouldRecognizeSimultaneouslyWith
greenTap shouldRecognizeSimultaneouslyWith
redTap
greenTap
and we can see that the redview(superview) 's shouldRecognizeSimultaneouslyWith and action method is called before the greenview(subview)'s methods
Don't add the green view as subview of red view. Just add the green view outside red view above it and center the constraints to red view

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.

How to implement a Longpress Gesture on a UITextView

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

Resources