how to add gesture recognizer to ViewController and to its Children? - ios

I am making app like in Euro Sport. My rootviewController is pageViewController which has 3 VCs. Swiping left and right you can change the VC. Next, I added sidebarmenu. I want to add gesture recognizer to the whole rootViewController. This is how I am adding gesture:
self.exitPanGesture = UIPanGestureRecognizer()
self.exitPanGesture.addTarget(self, action:"handleOffstagePan:")
self.sourceViewController.view.addGestureRecognizer(self.exitPanGesture)
self.sourceViewController.navigationController?.view.addGestureRecognizer(self.exitPanGesture)
When I drag rootviewController tapping on navigation bar it works. But the other parts like segmentcontrol, and pageContent doesnt work.
I am setting sourceViewController here:
func resetToMainPage(index: Int!) {
/* Getting the page View controller */
mainPageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("MainPageViewController") as UIPageViewController
self.mainPageViewController.dataSource = self
self.mainPageViewController.delegate = self
pageContentViewController = self.viewControllerAtIndex(index)
// self.transtionManger.sourceViewController = pageContentViewController // adding swipe to the pageContentViewControlle in order to close menu
self.transtionManger.sourceViewController = mainPageViewController
self.mainPageViewController.setViewControllers([pageContentViewController!], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
self.mainPageViewController.view.frame = CGRectMake(0, 102, self.view.frame.width, self.view.frame.height)
self.addChildViewController(mainPageViewController)
self.view.addSubview(mainPageViewController.view)
self.mainPageViewController.didMoveToParentViewController(self)
}
So, My question is how to add gesture recognizer to the whole rootViewController, including navbar, segmentcontrol, pageviewcontrol?

A gesture recognizer can only be attached to one view. So the lines below mean that only navigationController?.view is getting the gesture added.
self.sourceViewController.view.addGestureRecognizer(self.exitPanGesture)
self.sourceViewController.navigationController?.view.addGestureRecognizer(self.exitPanGesture)
You can just duplicate the gesture recognizer for each view you want to have it on and then add them all that way.
self.exitPanGesture1 = UIPanGestureRecognizer()
self.exitPanGesture1.addTarget(self, action:"handleOffstagePan:")
self.sourceViewController.view.addGestureRecognizer(self.exitPanGesture1)
self.exitPanGesture2 = UIPanGestureRecognizer()
self.exitPanGesture2.addTarget(self, action:"handleOffstagePan:")
self.sourceViewController.navigationController?.view.addGestureRecognizer(self.exitPanGesture2)

guys, I solved my problem but I got another one. In my case , I created exitGestureRecognizer. I wanted to add this to whole viewcontroller views. So, here is my code:
for singleView in self.sourceViewController.view.subviews {
singleView.addGestureRecognizer(exitPanGesture)
singleView.navigationController??.view.addGestureRecognizer(exitPanGesture)
}
For some reason, recognizer on navBar.view doesnt work. If I write code in this way:
for singleView in self.sourceViewController.view.subviews {
singleView.addGestureRecognizer(exitPanGesture)
singleView.navigationController??.view.addGestureRecognizer(exitPanGesture)
}
self.sourceViewController.navigationController?.view.addGestureRecognizer(self.exitPanGesture)
Only nav bar gestures work and subview gestures dont work. If I change the order of this code. Nav bar gestures dont work but subviews gestures work. How can I add gestures to both views?

Related

Swipe to go back only works on edge of screen?

My swipe to go back feature works but only works on the edge of the screen. How can I have it work from anywhere on the screen?
It's actually quite easy to do on the UINavigationController subclass without any intervention into every UIViewController subclass pushed. Also respecting built-in swipe-from-edge state (so when it's disabled intentionally, the new gesture is disabled as well):
import UIKit
class NavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
setupFullWidthBackGesture()
}
private lazy var fullWidthBackGestureRecognizer = UIPanGestureRecognizer()
private func setupFullWidthBackGesture() {
// The trick here is to wire up our full-width `fullWidthBackGestureRecognizer` to execute the same handler as
// the system `interactivePopGestureRecognizer`. That's done by assigning the same "targets" (effectively
// object and selector) of the system one to our gesture recognizer.
guard
let interactivePopGestureRecognizer = interactivePopGestureRecognizer,
let targets = interactivePopGestureRecognizer.value(forKey: "targets")
else {
return
}
fullWidthBackGestureRecognizer.setValue(targets, forKey: "targets")
fullWidthBackGestureRecognizer.delegate = self
view.addGestureRecognizer(fullWidthBackGestureRecognizer)
}
}
extension NavigationController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
let isSystemSwipeToBackEnabled = interactivePopGestureRecognizer?.isEnabled == true
let isThereStackedViewControllers = viewControllers.count > 1
return isSystemSwipeToBackEnabled && isThereStackedViewControllers
}
}
Apple says here :
interactivePopGestureRecognizer
The gesture recognizer responsible for popping the top view controller
off the navigation stack. (read-only)
#property(nonatomic, readonly) UIGestureRecognizer
*interactivePopGestureRecognizer
The navigation controller installs this gesture recognizer on its view
and uses it to pop the topmost view controller off the navigation
stack. You can use this property to retrieve the gesture recognizer
and tie it to the behavior of other gesture recognizers in your user
interface. When tying your gesture recognizers together, make sure
they recognize their gestures simultaneously to ensure that your
gesture recognizers are given a chance to handle the event.
So SloppySwiper library customise the UIPanGestureRecognizer.
Check out the library SloppySwiper, which achieves this by using UIPanGestureRecognizer and by recreating the default animation.
SloppySwiper:- UINavigationController delegate that allows swipe back gesture to be started from anywhere on the screen like instagram.
Usage of this library can be found here.
Cocoapods:- pod "SloppySwiper"
I test this library on ios7 and above. It works like a charm.
Swipe to go back is default behavior of pushed/showed view controllers. It works from left edge of the screen (by default). If you want to swipe back from any part of the screen, you should add UISwipeGestureRecognizer to your view:
let swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "didSwipe:")
self.view.addGestureRecognizer(swipeGestureRecognizer)

How to keep two gestureRecognizers in one view and switch between them?

I am having trouble my UIPageViewController. What I ve done sofar:
1. On pageViewController you can swipe left, right in order to change VC
2. added lateral menu. When adding lateral menu, I created pangesture recognizer and added it to pageContentVC (so I can drag pageContentVC in order to close sidebar menu)
var menuViewController: UIViewController! {
didSet {
self.exitPanGesture = UIPanGestureRecognizer()
self.exitPanGesture.addTarget(self, action:"handleOffstagePan:")
// self.exitPanGesture.view?.userInteractionEnabled = false
self.sourceViewController.view.addGestureRecognizer(self.exitPanGesture)
}
}
Here I am setting pageContentVC to sourceVC of transitionManager:
func resetToMainPage(index: Int!) {
/* Getting the page View controller */
mainPageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("MainPageViewController") as UIPageViewController
self.mainPageViewController.dataSource = self
self.mainPageViewController.delegate = self
let pageContentViewController = self.viewControllerAtIndex(index)
self.transtionManger.sourceViewController = pageContentViewController // adding swipe to the pageContentViewControlle in order to close menu
self.mainPageViewController.setViewControllers([pageContentViewController!], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
self.mainPageViewController.view.frame = CGRectMake(0, 102, self.view.frame.width, self.view.frame.height)
self.addChildViewController(mainPageViewController)
self.view.addSubview(mainPageViewController.view)
self.mainPageViewController.didMoveToParentViewController(self)
}
After closing menu I disabled pangesture recognizer.
` var presentingP:Bool!{
didSet{
if presentingP == true {
// enable the gesture recognizer only when the view of menucontroller is presented
self.exitPanGesture.view?.userInteractionEnabled = true
}else{
// disable gesture recognizer when menu is not presented
self.exitPanGesture.view?.userInteractionEnabled = false
isMenuVisible = false
}
}
}
` Sofar, I can swipe left, and right in order to change VC.
The problem is that I can't scroll tableView on my VC. After I disabled pageContentVC gesture, everything become disable except swiping right and left (to change VC). Question: How to keep two gestureRecognizers in one view and switch between them?
You can use the enabled property of UIGestureRecognizer to disable the one you don't want to use and enable the other one.
UIGestureRecognizer documentation

iOS UINavigationController barHideOnTapGestureRecognizer and UIButton interference

I have developed an app that makes use of the iOS8 feature to show or hide the navigation bar on a tap of the view.
However, the main view contains a UIButton which also act upon taps. The problem is that both 'objects' are receiving the tap and if I tap the button, the navigation bar toggles its visibility.
I can get to the barHideOnTapGestureRecognizer via the navigation controller but not really sure what can be done with it to stop it responding if a button is tapped.
Is there a way (apart from switching off or changing to 'Swipe to Hide') to subdue the navigation bar's appearance/disappearance when a button is pressed?
Don't use the standard barHideOnTapGestureRecognizer. Fortunately, it's not hard to roll your own:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let gestureRecognizer = UITapGestureRecognizer(target: self, action: "toggleBarsOnTap:")
self.view.addGestureRecognizer(gestureRecognizer)
}
func toggleBarsOnTap(sender: AnyObject?) {
let hidden = !self.navigationBarHidden
self.setNavigationBarHidden(hidden, animated: true)
self.setToolbarHidden(hidden, animated: true)
}
Taps on the view will show/hide the bars, and taps on controls (subviews of the view) will not.
[self.navigationController setNavigationBarHidden:YES];

How to present another view controller by tapping on a label inside a tableview cell

I want to create a segue to from an object (label) inside a tableview cell to another view controller.
example: imagine the instagram feed tableview - tapping the number of likes under an image will push a second view with the list of people who liked an image.
How can i do that?
I know it's possible when tapping a cell but i need a solution for when tapping a label inside the cell...
thanks!
You have to make the following steps :
Define userInteractionEnabled = true for the label using Interface Builder or in code, as you wish, (VERY IMPORTANT!!) without this the tap is not enabled.
Define an action to the Tap Gesture Recognizer to handle the tap inside the label.
To present the another ViewController you can do one of the following two things :
Make a modal segue from the Tap Gesture Recognizer to the another ViewController using the Interface Builder or make an action for the Tap Gesture Recognizer and then present the another ViewController manually using the presentViewController function.
I thought the first is more easy to present the another ViewController, Is up to you.
I hope this help you.
You can simply use a button and set its title instead of using a label. If you have to use a UILabel for some reason, then you can do so by setting its userInteractionEnabled property to true and then adding a UITapGestureRecognizer to it.
You can use UITapGestureRecognizer.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var lblTxt: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let recognizer = UITapGestureRecognizer(target: self, action:Selector("handleTap:"))
recognizer.numberOfTapsRequired = 1;
lblTxt.addGestureRecognizer(recognizer)
lblTxt.userInteractionEnabled = true;
}
func handleTap(recognizer: UITapGestureRecognizer){
println("tapped!")
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let secondViewController = storyBoard.instantiateViewControllerWithIdentifier("secondView") as SecondViewController
self.presentViewController(secondViewController, animated:true, completion:nil)
}

MMDrawerController can't close drawer

I'm trying to use MMDrawerController in my application, but I can't get the side drawer to close with gestures on the center view. I've used MMDrawerController somewhere else in my app with great success, but can't figure out why its not working in this case.
The top-level View Controller is a UINavigationController, whose default view controller is the MasterViewController (source below). This class extends MMDrawerController and configures the view for what I want (center and right, close gestures, max width). The center view has a button that opens the drawer. Once the drawer is open I can't close it with gestures on the center view. I added a button to the drawer and it can close the drawer programmatically, but I need to be able to tab/pan on the center view.
class MasterViewController: MMDrawerController {
override func viewDidLoad() {
let centerView = storyboard!.instantiateViewControllerWithIdentifier("CenterControllerName") as? CenterControllerType
super.setCenterViewController(centerView, withCloseAnimation: false, completion: nil)
let drawer = storyboard!.instantiateViewControllerWithIdentifier("Drawer") as? DrawerType
super.rightDrawerViewController = drawer
super.setMaximumRightDrawerWidth(200, animated: true, completion: nil)
super.closeDrawerGestureModeMask = MMCloseDrawerGestureMode.PanningCenterView | MMCloseDrawerGestureMode.TapCenterView
}
}
The function to open the drawer:
#IBAction func drawerButtonPressed(sender: AnyObject) {
drawer?.openDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
I was able to work around this by placing a ContainerView as the only object in my view and then configuring the MMDrawerContainer from my ViewController. It doesn't seem like the right answer but everything looks and functions right from the user's perspective.
Ok so I run into the same issue, and I am almost sure that it comes from this:
Ok, the VC1 (with the star) is our application Home, the VC2 is the left drawer, and the VC3 is the DrawerController.
During a long time I tried to put gesture functions in the VC1 and VC2, but only opening in the VC1, and closing in the VC2 would work.
The functions I used in VC1:
let rightSwipe = UISwipeGestureRecognizer(target: self, action: Selector("showDrawer:"))
rightSwipe.direction = .Right
view.addGestureRecognizer(rightSwipe)
...
func showDrawer(sender: UISwipeGestureRecognizer) {
if let m = mmdc {
if !m.isLeftDrawerOpen() {
m.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil)
}
}
}
And the same one in VC2 but with the LeftSwipe and closeDrawer.
The solution is tu put both of these functions and gestures recognizer in the VC3 (DrawerController).
The problem is coming from the fact that your gestures are defined for a given VC, but when you open the drawer, it changes the current VC by a new one, and the previous VC is just displayed but can't be interactif with. By putting things in the parentVC of VC1/VC2 it solves the problem.

Resources