open other view with segue only works by pressing button - swift

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

Related

iOS 13 Modals - Calling swipe dismissal programmatically

I'd like to detect a modal dismissal in the view controller that's presenting the modal.
This method works amazing for detecting the new iOS 13 swipe dismissal on the new card modals:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MyIdentifier" {
segue.destination.presentationController?.delegate = self
}
}
extension MyController: UIAdaptivePresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
//clean up UI (de-selecting stuff) once modal has been dismissed
}
}
However, presentationControllerDidDismiss is NOT called if the modal dismisses itself programmatically through an action:
#IBAction func btnDismissTap(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
Is this a bug or is there a way I can programmatically call whatever the "swipe" dismiss is so I can detect all dismissals the same way? Currently I'm writing extra "dismiss" delegate methods into my modals as a work around and it seems unnecessary.
However, presentationControllerDidDismiss is NOT called if the modal dismisses itself programmatically through an action
self.dismiss(animated: true, completion: nil)
It doesn’t need to be called, because you dismissed the modal yourself, in code. You cannot not know that the modal was dismissed. You don’t need to receive a dismissal signal, because you gave the dismissal signal in the first place.
You typically don’t get a delegate method call reporting something your own code did. Delegate methods report user actions. It would be crazy if everything you yourself did in code came back as a delegate method call.
Mojtaba Hosseini, answer is something I was looking for.
Currently, I need to write a delegate function to let the presenting view know that the user dismissed the modal PLUS do the presentationControllerDidDismiss handler for swipe dismissals:
#IBAction func btnDismissTap(_ sender: Any) {
self.dismiss(animated: true, completion: {
self.delegate?.myModalViewDidDismiss()
})
}
I wanted to handle both of these the same way and Mojtaba's answer works for me. However, presentationControllerDidDismiss does not get invoked if you call it inside of the self.dismiss completion block, you need to call it before.
I adapted my code to use "presentationControllerWillDismiss" (for clarity) and simply called the delegate before I dismiss programmatically in my modals and it works great.
#IBAction func btnDismissTap(_ sender: Any) {
if let pvc = self.presentationController {
pvc.delegate?.presentationControllerWillDismiss?(pvc)
}
self.dismiss(animated: true, completion: nil)
}
Now, I no longer need to create delegate functions to handle modal dismissals in code and my swipe handler takes care of all scenarios.
FYI, what I'm "handling" is doing some UI clean up (de-selections, etc) on the presenting UI once the modal is dismissed.
As #matt mentioned, there is no need to inform the one who dismissed a view by delegate. Because it is already known. BUT if you need that delegate method to be called, you should call it manually your self after dismissing the view:
#IBAction func btnDismissTap(_ sender: Any) {
self.dismiss(animated: true) {
presentationController?.delegate?.presentationControllerDidDismiss?(presentationController!)
}
}

How reload view controller like first time in Swift?

How reload viewcontraller like first time in Swift 4?
Details: when run app then enter viewcontraller then exit from it and I want back to same viewcontraller I want download viewcontraller from new same first time.
//code close viewcontraller :
self.dismiss(animated: true, completion: {})
//code on enter or back to viewcontraller :
DispatchQueue.main.async {
self.performSegue(withIdentifier: "sbmpo2", sender: nil)
}
Assign data to the ViewController or making API calls inside viewWillAppear that will call everytime when you came to that viewcontroller. Only you have to do to call data population functions/API call inside viewWillAppear
override func viewWillAppear(_: Bool) {
super.viewWillAppear(true)
//call your data populating/API calls from here
}
Hope this will help you
Take a look at the following methods of UIViewController:
viewWillAppear(_:)
viewDidAppear(_:)
viewWillDisappear(_:)
viewDidDisappear(_:)
The first two are called each time the view is shown. The last two are called each time the view goes away.
You can override these methods in your ViewController to setup your view as you wish.
self.dismiss(animated: true) {
//UIViewController is dismissed
DispatchQueue.main.async
self.performSegue(withIdentifier: "sbmpo2", sender: nil)
}
}
Hope this will help you

How to perform the Segue only after authorization with Touch ID

Maybe someone can help, just want to use Segue for Login button to made a transfer to second ViewController only after authorization with Touch ID but application still performing the Segue after user pressing on the button.
import UIKit
import LocalAuthentication
class LoginWindowViewController: UIViewController {
#IBAction func loginButton(_ sender: Any) {
let context: LAContext = LAContext()
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil){
context.evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: "For login you need to use your TouchID", reply: {(wasSuccessful, error) in if wasSuccessful {
DispatchQueue.main.async {
self.shouldPerformSegue(withIdentifier: "LoginComplete", sender: self.navigationController)
}
}else{
print ("Bad TouchID")
}
})
}
}
Thanks
Try implementing this function and checking from where it's being called:
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
}
Remember that as you're calling the segue from your code, if you have already added a segue action from your button to the next ViewController in the storyboard you should remove it and only create a connection between your LoginViewController to the one you want to call
As the documentation states, if you do not override the shouldPerformSegue method, the default implementation returns true for all segues.
The shouldPerformSegue(withIdentifier:sender:) method should determine whether a segue with the specified identifier will or will not be called depending on the return value of your implementation.
Since you already check whether the TouchID authorization was successful, you don't actually need to call shouldPerformSegue, but rather you need to call performSegue.
context.evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: "For login you need to use your TouchID", reply: {(wasSuccessful, error) in
if wasSuccessful {
DispatchQueue.main.async {
self.performSegue(withIdentifier: "LoginComplete", sender: self.navigationController)
}
}else{
print ("Bad TouchID")
}
})

Perform UnwindSegue correctly

I am trying to create an Unwind segue that brings from the DetailViewcontroller to the SecondViewcontroller.
I have created an IBAction like this one that can be intercepted by Segues :
#IBAction func segueToSecondVC(sender: UIStoryboardSegue) {
print("segue intercepted by IBAction segueToSecondVC")
}
An placed it in the receiver ViewController (secondViewController).
Then I moved to the Detailviewcontroller and clicked to the third figure on the top of the viewcontroller (exit),clicked to the segueToSecondVC IBAction that appeared in the list of events and dragged it into the button "TURN BACK"(placed in the Detailviewcontroller) in order to associate the button to the event.
In this way the button should be connected with the event and should turn back to the secondVC with an automatic Hidden Segue.
But it doesn't work, the simulator crash and I am redirected to xCode editor with an error in the Appdelegate that says "Thread 1: signal SIGABRT"
I don't know why it doesn't work, anyone could help me?
you can use Unwind like this:
#IBAction func prepareForUnwind(segue: UIStoryboardSegue){}
#IBAction func closePressed(_ sender: Any) {
performSegue(withIdentifier: "unwindToAnotherVC", sender: nil)
}
good rule, is to add all your segues names to constants file:
Create swift file
import Foundation
// Segues
let TO_LOGIN = "toLogin"
let TO_CREATE_ACCOUNT = "toCreateAccount"
let UNWIND = "unwindToChannel"
and then use UNWIND in
#IBAction func closePressed(_ sender: Any) {
performSegue(withIdentifier: UNWIND, sender: nil)
}

Automatically Reload TableViewController On Rewind

I am working on an app where it starts out at a tableViewController which loads and displays data stored in a Realm database. I have it so I can create a new entry in my Realm database in a separate scene and the save button unwind segues back to the initial tableView.
I current have it so the tableViewController will reload the tableView on pull down (something I Learned here, second answer down) but I would be better if the tableView would reload its self automatically upon unwind, displaying all the data in my database, including my new entry. Could someone please direct me to a tutorial that will teach me how this is done.
Additional info: My app is embedded in a navigation controller. The save button is located the bottom tool bar.
You can use NSNotification for that.
First of all in your tableViewController add this in your viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "refreshTable:", name: "refresh", object: nil)
}
And this will call one method from your class:
func refreshTable(notification: NSNotification) {
println("Received Notification")
tableView.reloadData() //reload your tableview here
}
So add this method too.
Now in your next view controller where you add new data into data base add this in you unWindSegue function:
NSNotificationCenter.defaultCenter().postNotificationName("refresh", object: nil, userInfo: nil)
Hope it will help
Try reloading your tabledata in viewWillAppear in initial (tableview)controller.
override func viewWillAppear(animated: Bool) {
self.tableView.reloadData()
}
Or call again the function through which you are loading your data from Realm like
override func viewWillAppear(animated: Bool) {
getMyData() //or whatever your function name is
}
If you are using storyboard unwind segue, try using
func unwindSegue(segue:UIStoryboardSegue) {
if segue.identifier == "identifier" {
getData()
self.tableView.reloaddata()
}
}