overload gestureRecognizerShouldBegin with UIPanGestureRecognizer in swift - swift

I have a pan gesture and I need to check when the gesture should begin. In objective-C you can use
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
and it possible to overload with UIPanGestureRecognizer
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer
but in swift
override func gestureRecognizerShouldBegin(gestureRecognizer: UIPanGestureRecognizer) -> Bool {
it doesn't work and return
Overriding method with selector 'gestureRecognizerShouldBegin:' has incompatible type '(UIPanGestureRecognizer) -> Bool'
How can I overload this method in swift?

You don't need to overload the method, you can do this instead :
override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
if let panGesture = gestureRecognizer as? UIPanGestureRecognizer {
return ...
}
else {
return ...
}
}

Remove override, and don't forget to add the UIGestureRecognizerDelegate extension.
You can add it at the bottom of your class
class YourViewController: UIViewController {
// You class declaration
}
extension YourViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
//
return true
}
}

Related

Protocol methods in a class extension are not called under specific conditions

I encountered a weird behavior. The best way I can put it is … Not overridden protocol methods in a class extension are not called while the superclass already conforms to the protocol (via extension). However this happens only while it's build with the release build configuration.
class A: UIViewController {}
extension A: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("scrollViewDidScroll in superclass")
}
}
class B: A {
// A tableView (and its data source and delegate) is set here…
}
extension B: UITableViewDelegate {
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
super.scrollViewDidScroll(scrollView)
print("scrollViewDidScroll in subclass")
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
print("scrollViewDidEndDragging")
}
}
The output:
scrollViewDidScroll in superclass
scrollViewDidScroll in subclass
scrollViewDidEndDragging
however if I build it with the release build configuration, the output is
scrollViewDidScroll in superclass
scrollViewDidScroll in subclass
I can solve the problem if I don't use the extension for protocol conformance approach in the class B and just use the regular way instead (put the methods that implement a protocol into the class).
The question is … how come?
I too encountered the same behaviour.
Xcode version - 12.4
Swift version - 4.2
I also encounter that this behaviour is shown only when applicationDidBecomeActive method is called in the application.(Mostly when you have navigation to some other Screen like authentication etc)
Fix: You need to assign the ViewController as delegate again.
func applicationDidBecomeActive(_ application: UIApplication) {
fixesForCallBackInExt()
}
func fixesForCallBackInExt() {
guard let navController = window?.rootViewController as? UINavigationController else { return }
if let tabVC = navController.topViewController as? UITabBarController, let topVC = tabVC.selectedViewController, topVC.isKind(of: UIViewController.self) {
// Set Your Delegate = topVC
} else if let topVC = navController.topViewController, topVC.isKind(of: UIViewController.self) {
// Set Your Delegate = topVC
}
}

How to disable swipe back specifically on only one view controller

I know there have been similar questions asked, but none of them have worked for me.
I have this code to enable swiping back in my project
class InteractivePopRecognizer: NSObject {
// MARK: - Properties
fileprivate weak var navigationController: UINavigationController?
// MARK: - Init
init(controller: UINavigationController) {
self.navigationController = controller
super.init()
self.navigationController?.interactivePopGestureRecognizer?.delegate = self
}
}
extension InteractivePopRecognizer: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return (navigationController?.viewControllers.count ?? 0) > 1
}
// This is necessary because without it, subviews of your top controller can cancel out your gesture recognizer on the edge.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
I have this VC stack
HomescreenVC -> Login/SignupVC -> UserProfileVC
I do not want them to be able to swipe back from the UserProfileVC.
A better approach is to clear them from the stack when you show UserProfileVC
let profile = self.storyboard?.instantiateViewController(withIdentifier: "profileID") as! UserProfileVC
self.navigationController?.viewControllers = [profile]
Edit: Do this inside profileVC
self.navigationController?.viewControllers = [self]
//
self.view.alpha = 0
UIView.animate(withDuration: 0.5) {
self.view.alpha = 1
}
I think you can remove gesture Recognizer from there too why you are not trying that.
Try something like this:-
view.gestureRecognizers?.removeAll()

UITextFieldDelegate extension function does not triggers, why?

I have SequencedTextFields protocol, which contains sequence of text fields. When user taps Return button on keyboard, current text field should resign first responder and next text field in the sequence should become first responder. And it works good, when I'm using direct implementation of UITextFieldDelegate protocol for view controller:
extension MyViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
nextInSequence(after: textField)?.becomeFirstResponder()
return true
}
}
But, when I'm trying to use default implementation, it does not triggers ever:
extension UITextFieldDelegate where Self: SequencedTextFields {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
nextInSequence(after: textField)?.becomeFirstResponder()
return true
}
}
What could be the reason? Or I've missed something?
UPDATE:
My view controller defining:
final class MyViewController: UIViewController, UITextFieldDelegate, SequencedTextFields
Setting up the delegate for the text fields:
The reason why your implementation is not working is because you are trying to extend an interface (UITextFieldDelegate) instead of a class, that's why it works when you use UIViewController instead.
What you can do is to create a custom class SequencedTextField that extends UITextField. Add a custom delegate (that I called sequencedDelegate that represents the class that implements your SequencedTextFields protocol.
Extend SequencedTextField to implement UITextFieldDelegate with your default implementation.
On MyViewController, set up your SequencedTextField delegate with the viewController itself.
At the end it should look something like this:
protocol SequencedTextFields: class {
func nextInSequence(after: UITextField) -> UITextField?
}
class SequencedTextField: UITextField {
weak var sequencedDelegate: SequencedTextFields?
}
extension SequencedTextField: UITextFieldDelegate {
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
sequencedDelegate?.nextInSequence(after: textField)?.becomeFirstResponder()
return true
}
}
class MyViewController : UIViewController, SequencedTextFields {
var textField = SequencedTextField()
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = textField
textField.sequencedDelegate = self
}
func nextInSequence(after: UITextField) -> UITextField? {
// your view controllers nextInSequence implementation here
}
}
What is wrong with your code?
The first extension is the extension of your view controller MyViewController, that's why it is working. But the second one is the extension of UITextFieldDelegate. Those are completely two different things.
Not clear what you want to achieve.
This is how you can use your custom protocol
If you want to make one CustomProtocol for your special UITextField you can do like this.
CustomTextFieldProtocol
Declear the protocol
protocol SequencedTextFields: UITextFieldDelegate {
func checkSomeThing(_ textField: UITextField) -> Bool
}
Set the delegate
self.textField.delegate = self
ViewController Extension to implement your Delegate methods
extension ViewController: SequencedTextFields {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
nextInSequence(after: textField)?.becomeFirstResponder()
return true
}
func checkSomeThing(_ textField: UITextField) -> Bool {
print("Check here")
return true
}
}

Simultaneous gesture recognition for specific gestures

I'm trying to enable simultaneous gesture recognition but only for the UIPinchGestureRecognizer and UIRotationGestureRecognizer gestures. I don't want it to work for any other gestures. If I set the following property to true it allows all gestures to be recognized simultaneously, how can I limit it to just rotating and scaling?
func gestureRecognizer(UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
Make sure your class implements UIGestureRecognizerDelegate
class YourViewController: UIViewController, UIGestureRecognizerDelegate ...
Set the gesture's delegate to self
yourGesture.delegate = self
Add delegate function to your class
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if (gestureRecognizer is UIPanGestureRecognizer || gestureRecognizer is UIRotationGestureRecognizer) {
return true
} else {
return false
}
}
any 2 cents for swift 5.1
// suppose You need to prefer pinch to pan:
//UIGestureRecognizerDelegate
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith shouldRecognizeSimultaneouslyWithGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer is UIPinchGestureRecognizer {
return true
}
return false
}

UITableView as UIGestureRecognizerDelegate

I would like to extend UITableView by adding a custom UIPanGestureRecognizer:
extension UITableView {
func addCustomPanGestureRecognizer() {
let panGestureRecognizer = UIPanGestureRecognizer()
panGestureRecognizer.delegate = self
addGestureRecognizer(panGestureRecognizer)
// some additional setup
// ...
}
}
Since custom gesture recognizer interferes with scrolling, I tried to implement UIGestureRecognizerDelegate with an optional requirement:
extension UITableView: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return otherGestureRecognizer == self.panGestureRecognizer
}
}
...but it's not getting called at all, despite delegating to tableView (as shown above).
Also, Xcode displays a warning:
Instance method 'gestureRecognizer(:shouldRecognizeSimultaneouslyWith:)' nearly matches optional requirement 'gestureRecognizer(:shouldRecognizeSimultaneouslyWith:)' of protocol 'UIGestureRecognizerDelegate'
To get rid of the warning, I tried adding #objc annotation before method definition, but with no luck.
Turns out, Xcode needed some additional help with finding an appropriate Objective-C method definition. To achieve this I had to provide method signature after #objc annotation:
extension UITableView: UIGestureRecognizerDelegate {
#objc(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return otherGestureRecognizer == self.panGestureRecognizer
}
}
The warning is still displayed, but the method is being called properly.