Use contains() to check if back button pressed in Swift - swift

I need to check if the back button is pressed in Swift. The action does not fire programmatically, so the approach appears to be to check if self is in the viewcontroller stack. I think the solution is here:
How to check for self with contains() in Swift?
However for each of the answers Swift tells me that contains is an unresolved identifier. I understand that is it in the Swift standard library so I assume I don't know how to use it.
Therefore what is the proper form of the following function?
override func viewDidDisappear(_ animated: Bool) {
if let viewControllers = self.navigationController?.viewControllers as? [UIViewController] {
if (contains(viewControllers, self)) {
print("Back button not pressed")
} else {
print ("Back button pressed")
}
}
}

Similar to RAJAMOHAN-S I look at where I'm transitioning from. However, the function
override func willMove(toParentViewController parent: UIViewController?)
provides a nice way of doing that; I combined with a simple bool variable to differentiate if the back button was really used or whether another programmatic method meant a transition event.
override func willMove(toParentViewController parent: UIViewController?) {
super.willMove(toParentViewController: parent)
if parent == nil {
// The back button was pressed, can be acted on
}
}
else{
print ("Coming here from a child view")
}
}

Related

Check if Popped UIViewController gets dismissed by swipe

I want to check whenever the user swipes a popped viewController away. So for example when in whatsApp a user exits the current chat by swiping from the edge. How is that possible in Swift?
I don't want to use viewDidDisappear, because this method also gets called when another viewController is presented over the current viewController.
As I wrote in comment, a simple workaround would be in viewDidDisappear, check if the navigationController is nil.
class MyVc: UIViewController {
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
if navigationController == nil {
print("view controller has been popped")
}
}
}
Of course, this solution works only if the view controller is embedded into a navigation controller, otherwise the if statement will always be true.
This "swipe" is handled by the interactivePopGestureRecognizer of the UINavigationController. It is possible to set the delegate of this gesture recognizer to your UIViewController as follows:
navigationController?.interactivePopGestureRecognizer?.delegate = self
Then, you can implement the UIGestureRecognizerDelegate in your UIViewController. This would look like this:
extension YourViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
guard gestureRecognizer.isEqual(self.navigationController?.interactivePopGestureRecognizer) else { return true }
print("The interactive pop gesture recognizer is being called.")
return true
}
}
I haven't tested the code, but this should print every time the interactivePopGestureRecognizer is used.
For more information, refer to the documentation of the interactivePopGestureRecognizer and UIGestureRecognizerDelegate.

How to get Stripe's STPPaymentCardTextField Data programmatically?

I've successfully set my first view controller to STPAddCardViewController. I now need to get the user information in the STPPaymentCardTextField. Problem is, I'm used to using the storyboard to make outlets. How do I detect the STPPaymentCardTextField programmatically?
I've tried:
class ViewController: STPAddCardViewController, STPPaymentCardTextFieldDelegate {
let paymentCardTextField = STPPaymentCardTextField()
func paymentCardTextFieldDidChange(_ textField: STPPaymentCardTextField) {
print(paymentCardTextField.cardNumber)
//ERROR: printing nil in the console
}
}
But I'm getting nil as an output. Any help?
You should use either STPAddCardViewController, or STPPaymentCardTextField, not both. The SDK's ViewControllers are not designed to be extended. The intended use is:
class MyVC : STPAddCardViewControllerDelegate {
override func viewDidLoad() {
…
let addCardView = STPAddCardViewController()
addCardView.delegate = self
// Start the addCardView
self.navigationController.pushViewController(addCardView, animated: true)
}
…
func addCardViewController(_ addCardViewController: STPAddCardViewController, didCreatePaymentMethod paymentMethod: STPPaymentMethod, completion: #escaping STPErrorBlock) {
// TODO: do something with paymentMethod
// Always call completion() to dismiss the view
completion()
}
func addCardViewControllerDidCancel(_ addCardViewController: STPAddCardViewController) {
// TODO: handle cancel
}
}
But rather than my partial example I'd recommend reading these docs and trying out this example iOS code. Best wishes!

open other view with segue only works by pressing button

I want to open an other view controller, after checking if it is the first run of the app.
It works when I press a button but not when I call the method openMap
class TutorialController: UIViewController {
override func viewDidLoad() {
//check if the app opens for the first time
if(UserDefaults.standard.bool(forKey: "HasLaunchedOnce"))
{
// app already launched
print("not first launch")
openMap()
}
else
{
// This is the first launch ever
UserDefaults.standard.set(true, forKey: "HasLaunchedOnce")
UserDefaults.standard.synchronize()
print("first launch")
openTutorial()
}
}
func openTutorial(){
}
#IBAction func openMap(){
print("openmap opened")
performSegue(withIdentifier: "openMap", sender: nil)
}
}
I assume, you've connected your button to #IBAction func openMap()
if so, you should not call openMap() action inside your viewDidLoad, but use the same code performSegue(withIdentifier: "openMap", sender: nil) instead in your viewDidAppear:
if(UserDefaults.standard.bool(forKey: "HasLaunchedOnce"))
{
// app already launched
print("not first launch")
performSegue(withIdentifier: "openMap", sender: nil)
}
...
If it doesn't work, you've probably made a mistake with creation of your segue and have connected Button to the destination ViewController directly in your storyboard instead of connecting two controllers:
If so, just remove the old segue, and re-crete it in the way as it is on the image above and assign the same segue id "openMap"
EDITED:
Please, move performing of your segue to the viewDidAppear instead of viewDidLoad, because viewDidLoad is called when the ViewController object is created and it's not yet attached to the window.
Ok, from what I understand is that you want to perform a segue "openMap" when it HasLaunchedOnce. Well what you're doing wrong is that you're calling an #IBAction func. This is my suggestion
if you still want to have that button
create a function and name if whatever you want. Inside this function perform this segue. Link this function to the if else statement and the button.
eg:
//if else statement
if(UserDefaults.standard.bool(forKey: "HasLaunchedOnce"))
{
// app already launched
print("not first launch")
anotherFunction()
}
//#ibaction (scrap this if you don't want the button)
#IBAction func openMap()
{
print("openmap opened")
anotherFunction()
}
//another function
func anotherFunction()
{
performSegue(withIdentifier: "openMap", sender: nil)
}
hope this helps

Hide FabButton when floatingViewController appears. Cosmicmind/Material

I want to hide a FabButton everytime a floatingViewController appears and show it again when I close the view.
I tried hiding the FabButton itself with
self.menuButton.hidden = true
but there isn't a callback function when I close the floatingViewController so I dont have a way to un hide it.
I also tried setting the zPosition manually but the button is unaffected
Is there a better approach to this?
The NavigationBarViewController has delegation methods for detecting the state of the floatingViewController. Set the delegate object,
navigationBarViewController?.delegate = self
Then try out the delegate methods:
extension AppNavigationBarViewController: NavigationBarViewControllerDelegate {
/// Delegation method that executes when the floatingViewController will open.
func navigationBarViewControllerWillOpenFloatingViewController(navigationBarViewController: NavigationBarViewController) {
print("Will Open")
}
/// Delegation method that executes when the floatingViewController will close.
func navigationBarViewControllerWillCloseFloatingViewController(navigationBarViewController: NavigationBarViewController) {
print("Will Close")
}
/// Delegation method that executes when the floatingViewController did open.
func navigationBarViewControllerDidOpenFloatingViewController(navigationBarViewController: NavigationBarViewController) {
print("Did Open")
}
/// Delegation method that executes when the floatingViewController did close.
func navigationBarViewControllerDidCloseFloatingViewController(navigationBarViewController: NavigationBarViewController) {
print("Did Close")
}
}
You should be able to use these methods to achieve the desired effect.
add this method to the FloatingViewController class
override func viewWillDisappear(animated: Bool) {
let vc = ...assign to the viewcontroller that holds tour menu button
vc.menuButton.hidden = false
}

How to check for self with contains() in Swift?

I'm trying to check if the back button on my view controller was pressed but I'm having a hard time detecting this in Swift.
With this code:
if (contains(self.navigationController?.viewControllers, self)) {
println("Back button not pressed")
} else {
self.updateSearchQueryModel()
}
I am getting the error:
Could not find an overload for contains that accepts the supplied arguments.
I did get the result that I wanted in another fashion but I am still confused as to why this error is happening.
Why is this happening? Can I not check if self exists in an array?
Source of original code in Objective C that I couldn't translate to Swift:
Setting action for back button in navigation controller
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
// back button was pressed. We know this is true because self is no longer
// in the navigation stack.
}
[super viewWillDisappear:animated];
}
Please do not tell me how to detect that the back button was pressed. I already figured that out here.
Source of solution to objective: Detecting when the 'back' button is pressed on a navbar
If you look at the declaration of the viewControllers property, you notice that it's [AnyObject]! and not [UIViewController]!.
The contains function requires that the sequence element implements the Equatable protocol, which AnyObject doesn't.
The solution is to make an explicit downcast of that array, using optional binding:
if let viewControllers = self.navigationController?.viewControllers as? [UIViewController] {
if (contains(viewControllers, self)) {
println("Back button not pressed")
} else {
self.updateSearchQueryModel()
}
}
I'm new in Swift but try this:
if let controllers = self.navigationController?.viewControllers as? [UIViewController] {
if contains(controllers, self) {
DLog("!")
}
}
You're getting error because you're passing optional as contains()'s first argument