SWRevealViewController conflict with UISlider - swift

I currently facing an issue trying to delegate SWRevealViewController panGestureRecognizer method in one of my view.
When i slide my UISlider, the panGesture interfer and open the sidemenu instead to move my slider.
i tried to delegate the panGesture and it works well, but if i quit my view and go to an other, the pangesture is not functionnal anymore, and i can't reveal my sidemenu from my second view.
My code :
class Search : UIViewController, UIGestureRecognizerDelegate{
#IBOutlet weak var sliderprice: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
self.revealViewController().panGestureRecognizer().delegate = self
}
override func viewDidAppear(animated: Bool) {
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if (touch.view == self.sliderprice){
return false
}
else{
return true
}
}
}

While this is an old question, I'll answer it anyways, maybe for someone coming from Google search it will be helpful.
After many hours of research and thinking, finally I was able to come up with multiple solutions to this problem.
The Lazy
This solution is not recommended, but working, so I decided to list it.
Before setting self as self.revealViewController().panGestureRecognizer()'s delegate, store the original self.revealViewController().panGestureRecognizer().delegate to a property, and when you leave the screen at viewWillDisappear(), set self.revealViewController().panGestureRecognizer().delegate back to the one you stored in viewDidLoad(). So in the end it gets back its original delegate. Tampering with delegates like this is never really recommended, but I said, it works.
The Nicer One
I consider this still not the best solution, but we are getting there. Find a class, a controller that you use in the whole application and gets called when you start the app. Here set the SWRevealViewController's panGestureDelegate to this class, and overwrite the gestureRecognizerShouldBeginmethod appropriately (see below).
The Best One - (in my opinion)
Now this is the best and most clear solution.
For the time being (May, 2018) the last commit to SWRevealViewController was in 2015.
Fork the original project ( https://github.com/John-Lluch/SWRevealViewController ) or simply copy the two necessary files (SWRevealViewController.m and SWRevealViewController.h) and place them into a separate folder to handle 3rd party libraries. Then you can remove SWRevealViewController from your Podfile. Don't forget to fix your imports for SWRevealViewController where you have it.
Now you are free to modify the files. What I suggest is the following.
Go to SWRevealViewController.m and implement the following method:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
{
return ![touch.view isKindOfClass:UISlider.class];
}
If the touched view is a UISlider (or a custom class that inherits from UISlider) the gesture won't begin, meaning the pan gesture will no longer be conflicted with the UISlider's pan gesture.
I seriously hope I could help anyone out there as this problem was a pain in my back for quite some time now.

Related

Prevent VoiceOver From Reading UIButton as "Possible Text"

I am writing a very simple game that is designed specifically for blind users, but may also be used by sighted users. It uses many buttons as elements, however, blind users interact with these buttons through custom gestures (pan, tap, etc), so standard voiceover interaction is not appropriate.
The issue lies in the fact that there are no accessibility objects on the screen at all, so whenever the game loads, voiceover starts reading the labels on buttons (e.g. "Possible text: back, menu...). These buttons are read regardless of the fact that they are not enabled. I also can't remove most of them from the view for blind users.
I have tried turning off accessibility for the elements, unchecking "button" from accessibility traits, everything has allows direct interaction selected, I have tried .accessibilityElementsHidden, all the suggestions from How do you exclude a UIButton from VoiceOver? and nothing seems to work.
My current solution has a clear UILabel with no text in it, this is set to the only item in the .accessibilityElements array, and then for good measure I post an accessibility screen changed notification with that label as the object so it becomes focused, then I wait a second in a dispatch queue async after call, remove the label entirely, and set focus back to the main view so the user can interact.
Here is an example of my current solution:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.accessibilityElements = [lblVoiceOver!]
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIAccessibility.post(notification: .screenChanged, argument: lblVoiceOver)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.lblVoiceOver.removeFromSuperview()
UIAccessibility.post(notification: .screenChanged, argument: self.view)
}
}
This is a silly hack, at best, and I would love to implement a real solution that simply prevents the "Possible text" from being read by voiceover. I believe the possible text feature was added in iOS 11, to help apps that are not written with accessibility in mind to be more accessibility friendly, but so far I haven't found a way to turn this off.
The issue lies in the fact that there are no accessibility objects on the screen at all.
If you want to reach this purpose, just write self.view.accessibilityElementsHidden = true in your view controller that will contain no accessible element for VoiceOver anymore: this will indicate that the content of your container isn't accessible.
blind users interact with these buttons through custom gestures (pan, tap, etc), so standard voiceover interaction is not appropriate [...] I would love to implement a real solution that simply prevents the "Possible text" from being read by voiceover.
... following the preceding rationale, you should prevent VoiceOver from analyzing and reading anything in your view.
Now, dealing just with your buttons, I created a blank project with a simple code for the view controller hereafter (Swift 5.0, iOS 12):
import UIKit
class NonAccessibleButtonVC: UIViewController {
#IBOutlet weak var aboveLabel: UILabel!
#IBOutlet weak var belowLabel: UILabel!
#IBOutlet weak var myButton: UIButton!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
myButton.accessibilityElementsHidden = true
}
}
... and you get the following result on your device:
The button isn't taken into account as an accessible element and if it doesn't work in your code, it means that anything else prevents this correct operation. Add maybe the setAccessibleElement button property value to false according to your context ?
However, another solution could be defining the display of the desired accessibility objects in your view thanks to its accessibilityElements property taking away the buttons for instance (see Example 2 of this link): that will definitely work in addition to ordering all your elements.

How to detect swiping or selecting event in XLPagerTabStrip swift

I am going to detect the swiping or selecting tab event in XLPagerTabStrip library in Swift 4.
I only need to get the tab index whenever selected tab is changed.
So I have checked library in Github but not found any good solutions.
thanks.
So I didn't find a solution from the library itself but, I used viewWillDisappear and for every time you swipe away of the view this function will be called.
Should be written as follows in your child view controller:
override func viewWillDisappear(_ animated: Bool) {
//Do your thing here
}
You can read more about this callback function here.
You can simply override updateContent
override func updateContent() {
super.updateContent()
// your code ....
}

How to catching doubleClick events from NSOutlineView in ViewController?

I am trying to catch doubleClick events from my NSOutlineView to be passed to my ViewController. My idea is to catch doubleClick events and to get the selected row from my OutlineView
What I did so far was subclassing the NSOutlineView in order to overwrite mouseDown
override func mouseDown(with event: NSEvent) {
super.mouseDown(with: event)
if event.clickCount >= 2 {
...
}
}
That works well however I don't know how to pass this event to my ViewController. The ViewController is already implementing the NSOutlineViewDelegate protocol.
I guess that the solution is not far away but somehow I am stuck.
UPDATED
Although you can set up NSGestureRecognizer for single click and NSClickGestureRecognizer for double clicks in OSX, You should probably be using the doubleAction property of the NSOutlineView directly.
Here's an example of how to set it up
This comes from a another of the Wenderlich tutorials, and there is a good discussion on SO already

Shake Gesture not Working

I have a project set up using SWReveal for the slide out menu.
When I place a motion ended override function it never seems to be called even though everything else in the class loads (indicated with the 'loading'.
I have had a good play around and found that if I change the segue to the page to a 'show' segue the code functions as I would expect it to.
However when I use a custom segue with the SWReveal as the class the motion ended does not work.
I have attached a screen shot to help describe the issue.
Any help much appreciated.
You need to override canBecomeFirstResponder method.
override func canBecomeFirstResponder() -> Bool {
return true
}

Start the animation of something when app starts

I am trying to learn Swift. I currently have a button that plays an animation but I want to have the animation start immediately after the app loads. How would I go about doing that?
You need to put your animation on the viewDidApear method, while will be called after your viewDidLoad method.
For example
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
UIView.animateWithDuration(2, animations: {
//do what ever here
})
}
Your question is not very specific so I'm not sure if this answers your question.
When your iOS application starts, there is usually a View Controller that gets rendered on the screen as your main entry point. This can be changed in in your AppDelegate.swift
Now, let's assume that in your application, a view controller named WelcomeViewController is the first page and you have your button along with the rest of your logic in it. Every view controller has certain functions that you can override and use. For example:
viewDidLoad
viewDidAppear
viewWillAppear
viewWillDisappear
the names are self-explanatory so I'm sure you can guess what they do. In your case, just override viewDidAppear and start your animation in there.
override func viewDidAppear(animated: Bool) {
...
}