Is there an easy solution to enable the function where the user swipes to get back? Couldn't find an easy solution that works for me.
UIKit disables the default back gesture if you use any custom transitions. You can unset the navigationControllerDelegate to get back the back gesture. or implement your own back gesture through interactive transition.
Inside viewDidLoad() of the destination-ViewController following line does the trick:
navigationController?.interactivePopGestureRecognizer?.delegate = nil
Link
Related
I am using an imported button object, Floaty (https://github.com/kciter/Floaty) which is a subclass of the UIView class. In one of the view controllers where I am using this button (which includes a Text Field), the button fails to be selected when tapped if I include the following line to close the keyboard when tapped away from...
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard (_:)))
self.view.addGestureRecognizer(tapGesture)
The idea for how to close the keyboard came from this SO query: iOS - Dismiss keyboard when touching outside of UITextField
I thought part of the issue could be that the Floaty button needs to be in the front, so I added the following line of code, found from this SO query: Losing tap recognition after adding a UIScrollView under a UIButton
floaty.bringSubviewToFront(self.view)
I added this line to the end of my ViewDidLoad() function in the view where it is used.
(Important maybe to note is that all of the aforementioned code is in the ViewDidLoad function for my view controller)
Is there another way to handle closing a keyboard when tapping away from a text field? Or is there something else that I should do to the Floaty (UIView) object to handle tap / touch events?
You can try use Hit Testing. With hit testing, you can assign a view that handles touch. This great article explains how to apply hit testing http://smnh.me/hit-testing-in-ios/
I have a UIPageViewController where one page is a Settings screen. On that screen, there is a UISwitch to toggle a setting on or off.
While I've noticed many people tap a UISwitch to toggle it like I do, I've observed that some users slide a UISwitch to toggle it.
Attempting to slide the UISwitch can cause problems when it's on a UIViewController that's part of a UIPageViewController, since sliding the switch can start sliding the UIPageViewController as if the user wants to change pages.
This behavior that feels very broken and inconsistent. It seems that if the user touches the switch, but hesitates briefly before sliding, the touches are registered by the UISwitch and it works as the user expects. But, if the user touches the UISwitch and immediately starts sliding, the UIPageViewController gets the touches instead. There seems to be a very fine line (hesitation threshold) between the UISwitch getting the touches or not.
How would you solve this problem?
Here's an example that starts with simple taps to toggle the UISwitch, and then shows some of the different ways to try to slide the UISwitch that lead to inconsistent results:
Possible Solutions & Issues
One way I've considered resolving this is to detect touches anywhere on the Settings UIViewController, and if the touches begin somewhere in the frame of the UISwitch, prevent the UIPageViewController from sliding.
My worry is that simply disabling sliding for the UIPageViewController would not guarantee the touches are passed to the UISwitch. That would mean a user might try to slide the UISwitch and the UIPageViewController would not slide, but the UISwitch would also not respond to the touches, making it still seem broken and inconsistent.
To start exploring this possible solution, I've tried detecting touches by overriding the touchesBegan method like this:
override func touchesBegan(_ touches: Set<UITouch>,
with event: UIEvent?) {
print("touchesBegan")
super.touchesBegan(touches,
with: event)
}
I've tried detecting touches this way on both the UIPageViewController and the UIViewController of the specific Settings page, but neither gets called. I've also tried setting view.isUserInteractionEnabled = true on the views, and different combinations of true or false on the different UIViewControllers, but still can't seem to detect touches.
I've also considered just ditching the whole UIPageViewController paradigm for this app and making the Settings screen a modal instead, since that would resolve the need for this kind of complicated solution, but there are other advantages to the UIPageViewController paradigm in the app, so I want to explore whether it'd be possible to keep. Ultimately, that seems like it would be a better idea than weird workarounds, but I wanted to post this anyway in case someone else experiences the same issues or anyone has other possible solutions.
UPDATE: SOLUTION
I spent a ton of time experimenting based on the answers that were submitted, while also learning a lot about how iOS works, so thank you to everyone who answered.
Ultimately, Leon's answer got me going down the path to a simple solution that worked with a UIPanGestureRecognizer, which is very similar to what I was trying with a UISwipeGestureRecognizer based on Carl's answer, but ultimately didn't produce the same results.
I ended up subclassing UISwitch and adding a UIPanGestureRecognizer that does nothing. This makes it behave exactly as I want: any time a user starts sliding the switch the UIPageViewController does not pan.
class NoPanSwitch: UISwitch {
override init(frame: CGRect) {
super.init(frame: frame)
let recognizer = UIPanGestureRecognizer(target: self,
action: nil)
addGestureRecognizer(recognizer)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Page view controller has has gestureRecognizers property for gesture recognisers controlling interactions within page. I would try adding a pan gesture recogniser to the UISwitch and adding it to page controller. The effect I would expect is that the pan gesture recogniser of the page controller will require the gesture recognisers in the array to fail before recognising page panning so the scrolling doesn't happen when you flick the switch. If it doesn't work out like that, you might want to swap Page controller for a scroll view with a stack or collection view with your controllers inside. Then you'll have full control over school view's panGEstureRecognizer to achieve the above behaviour.
Can you subclass the UISwitches you are using? This sounds more like a job for that class, overriding gestureRecognizerShouldBegin() to return NO for a swipe gesture recognizer moving horizontally. The documentation for that method mentions that UISlider uses that method for the same purpose; UISwitch should probably do the same.
Another option might be to create a custom UIGestureRecognizer which via delegate or the UIGestureRecognizerSubclass methods forces any other UISwipeGestureRecognizer to fail if it starts over a UISwitch, and is itself set with cancelsTouchesInView to false (and not do anything when it "succeeds").
Just try this one and see whether it's working as you want or not.
Detect the touches on UISwitch when UISwitch touchBegan set delegate and datasource of UIPageViewController to `nil.
On touchesEnd set again set datasource and delegate for UIPageViewController.
In this way you will be able to interact with your UISwitch as you want.
I want to block the tap gesture (perfectly just the left border one, but both will be also fine, I will just add button for it) which is responded for changing pages in UIPageViewController.
I already tried this solution in 'viewDidLoad' method:
for recognizer in gestureRecognizers {
if recognizer is UITapGestureRecognizer {
recognizer.isEnabled = false
}
}
But it worked only for the case when TransitionStyle is set for Page Curl, in my case I need to use Scroll TransitionStyle.
Ps. I also found a comment in UIPageViewController implementation that gestureRecognizers are populated only if transition style is UIPageViewControllerTransitionStylePageCurl so there will be needed some bigger "hack", hope you can help me with it.
Pps. Yes I found this - UIPageViewController returns no Gesture Recognizers in iOS 6 . solution but it's pretty old and in objC and I would be glad to use Swift here.
Ppps. Setting dataSource on nil won't work - I need swipe gesture.
You need to implement the UIGestureDelegate and then to declane gesture when such are detected.
I think here you can see good example how to do it
I was using SideMenu library from this github repo.
https://github.com/jonkykong/SideMenu
And after going high and low, I can't pinned down how to disable swipe on a navigation bar of viewcontroller.
I will use example included in library as example. At MainViewController.swift file, SideMenu is initiated and as far as I know, there are only 2 methods/functions related to gesture. And none of them are related to disable reacting to gesture. And I added this line at setUp method in the file.
SideMenuManager.default.menuPushStyle = .preserveAndHideBackButton
I have tried using childrenController in these methods but as soon as I did that, I can't use swipe/gesture on the directed view after touching on items of left sidemenu/drawer.
Is there anyway I could disable gesture on navigation bar using this library? And kindly let me know if I need to edit question, as this is my first question in stackoverflow.
As checked with demo example from above github project,
SideMenuManager.default.menuAddPanGestureToPresent(toView: self.navigationController!.navigationBar)
This line is in class 'MainViewController' in method 'setupSideMenu' which is responsible for adding pan gesture to navigation bar, which causing swipe gesture work for navigation bar. If you don't want this feature, you can simply comment the line and it will work.
Hope it helps...
Not sure about your scenario, but in my case when user logs out and reach on login screen i wanted to disable gesture from left to right.
At th etime user clicks on logout i set below property to 0.0 and that's it.
SideMenuManager.default.menuWidth = 0.0
Now again reset it to 85% after user re-login.
I just figured out how to do this myself for a UITabBarController. The approach will be similar for a UINavigationController. I created the following function to enable/disable the swipe gestures:
/// Keeps references to the gestures created by the SideMenu manager.
private var sideMenuGestures : [UIGestureRecognizer] = []
/// Enables or disables the swipe gestures used to show a SideMenu on the given side (default: left).
func enableSideMenuGestures(_ enable: Bool, forMenu: UIRectEdge = .left) {
if enable {
if sideMenuGestures.count == 0 {
sideMenuGestures.append(SideMenuManager.default.menuAddPanGestureToPresent(toView: self.tabBarController!.view))
sideMenuGestures.append(contentsOf: SideMenuManager.default.menuAddScreenEdgePanGesturesToPresent(toView: self.tabBarController!.view, forMenu: forMenu))
}
} else {
self.tabBarController?.view.gestureRecognizers?.removeAll(where: { sideMenuGestures.contains($0) })
sideMenuGestures.removeAll()
}
}
In your case, simply replace self.tabBarController with self.navigationController and it should work the same. (The self. prefix is not required but I like to include it as a convention to show it's an inherited field rather than one I've declared.)
This works because SideMenuManager handily returns references to all the gestures it creates; otherwise you'd have to clear all gestures from the navigation controller, which would be a bad idea as explained in this answer.
Usage is then simply enableSideMenuGestures(true) in the action handler for a button which displays the menu (for example) and enableSideMenuGestures(false) when you want to disable the gestures again.
I have a textView to input some text or emotion. so I make another button to change the keyboard to custom emotion view, and I also use tap GestureRecognizer for when I want to change back to keyboard.
I found every time I need to touch the button twice, the tap GestureRecognizer can work well. I thought and search long time but no result, finally I fix it. I think some one will meet the same question, so I shared it below.
because my add tap gesture recognition 's code is in init code, but when I tap textview, keyboard was pop up, the textview's position was changed too, so the init tap gesture recognition code isn't work now.
I fix it with add tap gesture recognition delay 0.3 s.