Swift opening a new view controller programmatically - ios

I'm working on an workout app. I finished everything now I'm stuck at the details.
So when my workout finishes it needs to open a new view controller which will tell the user that he finished the workout.
I have tried to do it with this code:
var storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
//var vc: UINavigationController = storyboard.instantiateViewControllerWithIdentifier("newViewController") as! UINavigationController
var vc: EndOfWorkout = storyboard.instantiateViewControllerWithIdentifier("newView") as! EndOfWorkout
self.presentViewController(vc, animated: true, completion: nil)
But it opens sometimes and sometimes not. Also when it opens it closes after a short period of time.
I need also at the end that the user is able to go back to the main menu but none of the examples that I tried are working.

Your vc variable might be destroyed by ARC, try setting this instance in a property of your controller.
Something like that :
class MainViewController: UIViewController{
private var vc: EndOfWorkout?
override func viewDidLoad() {
...
vc = storyboard.instantiateViewControllerWithIdentifier("newView") as! EndOfWorkout
}
}

Related

How to present a view controller from appdelegate xcode 11 swift 5

I have been searching all day on how to present a view controller from within the appdelegate. It appears that in xcode 11 the window property was moved to the scenedelegate which has been confusing me. I want to present a view controller from within the appdelegate from the didReceiveRemoteNotification function so when the user receives a notification it takes them to a separate view controller with information. I have tried to do:
self.window?.rootViewController?.present(LoginViewController(), animated: false, completion: nil)
within the appdelegate which used to work in a previous application of mine but it does not seem to work anymore. Any help would be much appreciated.
I was able to solve this issue by using shared windows to get the window from scenedelegate to present the view controller on.
UIApplication.shared.windows.first?.rootViewController?.present(vc, animated: false, completion: nil)
Best approach to present view controller through app delegate is without falling for hierarchy like below:
if let vc = UIStoryboard(name: "YOURSTORYBOARD", bundle: nil).instantiateViewController(withIdentifier: "YOURVIEWCONTROLLER") as? YOURVIEWCONTROLLER {
if let window = self.window, let rootViewController = window.rootViewController {
var currentController = rootViewController
while let presentController = currentController.presentedViewController {
currentController = presentController
}
currentController.present(vc, animated: true, completion: nil)
}
}

passing data from 2 view controllers without segue

I have a mainviewcontroller and a popup view controller which opens without a segue.
the popup viewcontroller recive data from Firebase, and then i need to append this data to an array in the mainviewcontroller.
How can i do that?
(i tried to create a property of the popupviewcontroller in the mainviewcontroller, but in crashes the app)
this is the code that opens the popup:
#IBAction func showPopUp(_ sender: Any) {
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "sbPopUp") as! PopUpViewController
self.addChild(popOverVC)
popOverVC.view.frame = self.view.frame
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParent: self)
You need to connect the classes so that the popup class knows what to do with the data once it has been received. Here's a sample structure that works in a playground that you should be able to apply to your real classes.
class MainClass {
func showPopUp() {
let popOverVC = PopUpClass()
popOverVC.update = addToArray
popOverVC.receivedData()
}
func addToArray() {
print("Adding")
}
}
class PopUpClass {
var update: (()->())?
func receivedData() {
if let updateFunction = update {
updateFunction()
}
}
}
let main = MainClass()
main.showPopUp()
Or you could create a global variable so it can be accessed anywhere. ... It is better to pass data but I just thought it would be easier in this instance, so the variable can be accessed anywhere in your entire project.
if it is just a notification, you can show it in an alert, but if you don't want to use an alert my offer to present another view controller is not like this , try my code :
//if you have navigation controller ->
let vc = self.storyboard?.instantiateViewController(
withIdentifier: "storyboadrID") as! yourViewController
self.navigationController?.pushViewController(vc, animated: true)
//if you don't use navigation controller ->
let VC1 = self.storyboard!.instantiateViewController(withIdentifier: "storyboadrID") as! yourViewController
self.present(VC1, animated:true, completion: nil)
also if you want to pass any data or parameter to destination view controller you can put it like this before presenting your view controller :
VC1.textView.text = "test"

show() View Controller doesn't work

I try to authenticate the user of my app if they are not. I first read the value saved in UserDefaults to know if they are logged in, and if not, I want to show the ViewController to allow them to log in.
But anyway, the LogInViewController won't show, this is my code :
import UIKit
import MapKit
class MainViewController: UIViewController, CLLocationManagerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if(!UserDefaults.standard.bool(forKey: "userIsConnected")){
let LogVC = self.storyboard?.instantiateViewController(withIdentifier: "LogInViewController") as! LogInViewController
show(LogVC, sender: self)
}
}
I also tried without the 'if' condition, and it doesn't work anymore
EDIT :
I also tried presentVC() and I had the error "Attempt to present on whose view is not in the window hierarchy"
Thanks for help
You are trying to login in the MainViewController. You should avoid when possible managing your login in a view controller. The login is more effective if managed in your AppDelegate.
Add this code to didFinishLaunchingWithOptions in AppDelegate.swift
if !UserDefaults.standard.bool(forKey: "userIsConnected") {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let loginViewController = storyboard.instantiateViewController(withIdentifier: "LogInViewController")
window?.makeKeyAndVisible()
window?.rootViewController?.present(loginViewController, animated: true, completion: nil
}
Make sure you can access UserDefaults.

Swift: How to return to the same instance of my UIViewController

I have a button in my SettingsViewController that when pressed, I want to present the same instance of my TimerViewController each time the button is pressed.
I think I have gotten fairly close with this post here, iOS Swift, returning to the same instance of a view controller. So if you could point me to saving the instance of TimerViewController, that would work the best.
This is the code I have right now -
var yourVariable : UIViewController!
if yourVariable == nil {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
yourVariable = storyboard.instantiateViewControllerWithIdentifier("timer") as! TimerInterface
}
presentViewController(yourVariable, animated: true, completion: nil)
the code you provided should work. if your SettingsViewController gets deallocated though the timerViewController also gets deallocated and recreated the next time you present it. so you have to make sure to save its instance at an appropriate location.
var timerViewController: TimerViewController!
if timerViewController == nil {
let timerViewController = UIStoryboard(name: "Main", bundle: nil)
yourVariable = storyboard.instantiateViewControllerWithIdentifier("timer") as! TimerInterface
}
presentViewController(timerViewController, animated: true, completion: nil)
The best would be to save the ViewController somewhere , and get back to it .
A way to "get back to it" :
add
var tvc: TimerViewController? = nil
inside AppDelegate
when you get to your Timer (the best would be when you left it , in viewDidDisappear)
you add :
(UIApplication.sharedAplication().delegate as! AppDelegate).tvc = self
then when you get to the setting , if you want to segue back to the timer
let tvc = (UIApplication.sharedAplication().delegate as! AppDelegate).tvc
(UIApplication.sharedApplication().delegate? as! AppDelegate).window?.rootViewController?.presentViewController(tvc, animated: true , completion: nil)
if you ask yourself why should you present it with the rootViewController (last line ) it is because you can present an already active viewController , this will not present an already active vc .

opening a tab bar controller on successful login swift

I am extremely new to swift and storyboards. I have an initial view set which presents the user with login or register options. on the success of my login web service, I am trying to open a tab bar. I am getting into the success of the webservice as I can see the response.
My code for attempting to load the tab bar is as folllows in my initial view controller:
func loadHomeScreen()
{
emailField.text = ""
passwordField.text = ""
self.presentViewController(UIStoryboard.tabbarController()!, animated: true, completion: nil)
}
And at the very bottom of that file, I have the following:
private extension UIStoryboard {
class func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) }
class func tabbarController() -> TabbarController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("TabbarControllerID") as? TabbarController
}
}
And in my storyboard I have given the tabbarcontroller the id above. When I run the app (tested on the simulator for iphone6), I am getting the error 'found nil while unwrapping an Optional value' and this is pointing to the line of code in my loadHome func above (self.presentViewController(UIStoryboard.tabbarController()!, animated: true, completion: nil))
Any help would be appreciated
You could instead instantiate your storyboard like so and the code would look like this under loadHomeScreen():
emailField.text = ""
passwordField.text = ""
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("TabbarControllerID") as! TabbarController
self.presentViewController(vc, animated: true, completion: nil)
You may need to change the bundle to the "mainBundle" as you have in your code, but this should work.
This may not be the optimal solution if your plan is to extend UIStoryboard for instantiating your ViewControllers but I think this might be a little easier/cleaner, depending on how your project is set up.

Resources