clearing variables of root view controller when switching tabs programmatically - swift

I have a storyboard/root viewcontroller as the third tab on my app. On that tab the user selects an image, and writes a caption, which are stored as variables. From the root viewcontroller the user is taken to a preview screen via a segue which passes the variables, i.e. image and text. From this screen the user posts the object. From the post button, I am popping the viewcontroller, and programatically navigating to the first tab of my app. That all works, but the problem is when I then navigate back to the third tab, the stored variables are still there, and I'd like them to be cleared out.
I've seen many posts on keeping the variables, but none on how to reset them. I've tried to use viewWillDissapear but I don't want them cleared when I use the segue because I want the user to be able to go back and make some changes if needed.
UPDATED CODE WITH CORRECT ANSWER
#IBAction func postButtonPressed(_ sender: Any) {
PostFunction.createPost(image: self.postImage, postText: self.hashtag) { (true) in
self.tabBarController?.selectedIndex = 0
let tab3 = self.tabBarController!.viewControllers![2] as! UINavigationController
let vc = tab3.viewControllers.first as! PostHomeVC
vc.clear()
self.navigationController?.popViewController(animated: false)
}
}
Any suggestions on how to "reset" the rootviewcontroller from the above method?
Thanks!

I am popping the viewcontroller, and programatically navigating to the first tab of my app
before you do the programmatic switch to the first tab do
let tab3 = self.tabBarController.viewControllers[2] as! UINavigationController
let vc = tab3.viewControllers.first as! VCName
vc.clear()
or
let vc = self.navigationController!.viewControllers.first as! VCName
vc.clear()
and write that clear method inside the vc as you need

Related

Saving Changes from an Unwind. Working in conjunction with <Back

I have a problem understanding what I can do with the default Back BarButtonItem and how I can instigate a rewind.
The following shows a simplified layout. I embed a ViewController in a Navigation controller and add a BarButtonItem ("Show") and connect to a second VC. This adds the "Back" button to the second controller as shown below.
In the first VC I will show the user some details, in the second VC greater details will be shown. With the show button and the default back button the user can easily navigate back and forth with expected behavior.
My problem comes in that the user will be allowed to modify the "More Details Here" and I will need to pass that to the first VC after the user unwinds back.
I cannot control-drag from the Back to the Exit icon but I have determined I can do this with a "Save" button on the navigation bar as shown. (First dragging an Navigation Item to the top, then the BarButtonItem) then control-drag to Exit icon.
With the Save button, I can initiate a segue unwind and capture the changes back in my first VC with code like the following.
#IBAction func unwindFromSecondVC(_ sender: UIStoryboardSegue) {
if sender.source is AddCharacterViewController {
if let senderVC = sender.source as? SecondViewController {
details = senderVC.newDetails
}
}
}
This creates a problem when the user makes a change and then clicks the back button thereby loosing the changes.
My preferred solution would be to have the back button initiate a segue and transfer the changes. But this does not appear to be possible. My second solution would be to have the back button notice a transfer has not been made and stop the unwind seque. Neither approach appears possible based on my research.
How should I best handle this problem while giving the user the common Back navigation?
If you want to save any changes the user has made in the second VC, you could create a SecondVCDelegate:
protocol SecondVCDelegate : class {
func detailsDidChange(newDetails: String)
}
In SecondVC, declare a delegate property:
weak var delegate: SecondVCDelegate?
And whenever the details change (the text field's value changed, or whatever event happened), you call
delegate?.detailsDidChange(newDetails: newDetails)
In FirstVC.prepareForSegue, you should set self as the delegate of SecondVC:
if let vc = segue.destination as? SecondVC {
vc.delegate = self
}
// ...
extension FirstVC : SecondVCDelegate {
func detailsDidChange(newDetails: String) {
details = newDetails
}
}
This way, whenever the newDetails change, FirstVC will be notified.
If you just want to notify FirstVC when the user leaves SecondVC, you could instead call the delegate in SecondVC.viewWillDisappear or a method like that.

Programmatically press back button for UIViewController with UITableView iOS swift

I have a UIViewController that implements UITableViewDelegate, UITableViewDataSource and that contains a UITableView as a member variable. When a user click on one of the rows of that table, the app performs a storyboard segue to open the detail view controller. That detail view controller of course has a button in the top left of the screen that is the "back" button to go back up to the UIViewController with the UIViewTable.
So, suppose that I want to programmatically "click" that back button. How exactly would I do that in swift? This is the most recent version of swift (swift 4?) in XCode 10.1.
UPDATE:
So here is how I solved this. As the answers below show, it is possible to use self.navigationController?.popViewController(animated: true) to just return to the previous view controller. What I discovered I also wanted to do, however, was to call a specific method in that view controller so that it executed a certain behavior once it got shown. It turns out that is also possible, but in my case it was a bit tricky, since that prior view controller was actually a UITabBarController. Therefore I had to get the ViewController that I was interested in from the UITabBarController. I did it like this:
let numvc = navigationController!.viewControllers.count
let tvc:UITabBarController = navigationController!.viewControllers[numvc-2] as! UITabBarController
let my_vc: MyCustomVC = tvc.viewControllers![0] as! MyCustomVC
my_vc.some_function()
Here of course MyCustomV is my custom view controller class and some_function() is the method I want to call on that class. Hope this helps someone.
When You run a segue you perform a "pushViewController" method to the next view, so if you want to go back to the previous view programmatically you just have to do is pop the last view like so:
self.navigationController?.popViewController(animated: true)
UPDATE
You just need the if statement if you have multiple segues from that viewController, if not, you can delete and just cast the next view as you wish and set the properties, let the autocomplete write the *prepare(for segue... * method for you, so You don't run into any problems
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yourSegueName" {
let destinationVC = segue.destination as! CustomViewController
destinationVC.labelExample.text = "Some text I'm sending"
}
}
Are you sure you need to "click" the button?
If all you need is to dismiss details view controller, you can just call navigationController?.popViewController(animated: true)
Or if you want to deal directly with button, you can tell it to send its actions: backButton.sendActions(for: .touchUpInside)
Or if you absolutely need to show button clicking animation, then you will need something like this (you should play and choose suitable delay):
backButton.isHighlighted = true
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.3) {
backButton.isHighlighted = false
backButton.sendActions(for: .touchUpInside)
}

Swift - UITabBarContorller Initial View - when to force launch login screen?

I have programatically set up a custom UITabBarControler with three tabs, each of which is a UIViewController embedded a UINavigationController.
I am not using storyboards. I set the custom tab controller as the root in AppDelegate
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = CustomTabBarController()
The app runs fine and I get the three tabs and can move between them.
sample of how tabs are populated (in viewDidLoad of the customer tab bar controller)
let ordersVC = OrdersViewController() // where Orders is a UIViewController
ordersVC.title = "Orders"
ordersVC.view.backgroundColor = UIColor.white
let ordersVCNavi = UINavigationController(rootViewController: ordersVC)
ordersVCNavi.navigationBar.tintColor = UIColor.black
...
viewControllers = [homeVCNavi, inventoryVCNavi, ordersVCNavi]
Now I need to first to see if the user is logged in (using Firebase). I can easily check for already logged in (Firebase cached) or not logged in.
I do this logged in check in AppDelegate
My problem is when I need to force a login (jump to login view controller). I can not find a place that works.
- tried placing the call in the custom UITabBarController didLoad and the code is ignored
- tried placing the call in the didLoad and the willAppear in the initial tab controller also ignored
I can place a button on the initial tab and that button will indeed launch the login controller. So I can get to the login controller from a button press.
upon pressing a button I can execute this code and the login controller will show
let vc = LoginViewController()
self.navigationController?.pushViewController(vc, animated: false)
But if I know I need to force login and I try to do that same code snip above in viewDidLoad() or viewWillAppear() in the initial tab controller, or in the custom UITabBarController then the push is ignored. Nothing happens.
What is best practice for forcing login screen when initial view is tabbarcontroller?
Where should one put the navigation to the login controller to force login when not already logged in. Want to go to login so that user can not use the app if not logged in.
in didFinishLaunchingWithOptions
if loggedIn {
window?.rootViewController = CustomTabBarController()
}
else {
window?.rootViewController = LoginViewController()
}
after your login is successful
UIApplication.shared.keyWindow?.rootViewController = CustomTabBarController()

NSUserDefaults changing every segue

Sorry for the newbie question but i am really stuck.
I have a UIViewController and 4 tableviews in an app. When i click on a button on the UIViewcontroller it segues to a UITableviewController called "Beach". When the user clicks on a cell of the table, it segues back to the UIViewController and displays the selected cells title as the buttons title. The problem that i am having is when i click on a nother button to a tableview and then clicks on the cell, the previous buttons title sets back to the previous title.
i have a prepare for segue function in the tables view controllers and this returns the selected table title (named : Titleoftable) to the main VC which, the strings.
the way i am currently doing it is to make a NSUserDefault below but the problem still remains the same - The value changes top "" every time i click on another table V---
let path = tableView.indexPathForSelectedRow
let cell = tableView.cellForRowAtIndexPath(path!)
let persistenceStoreKey = "MyStringKey"
let stringToStore = "\((cell?.textLabel?.text!)!)"
// Store data
NSUserDefaults.standardUserDefaults().setObject(stringToStore, forKey: persistenceStoreKey)
NSUserDefaults.standardUserDefaults().synchronize()
// Get data
let myStringt = NSUserDefaults.standardUserDefaults().stringForKey(persistenceStoreKey)
destination.textOfBeach = (myStringt)!
destination.isBeachSelected = true
}
I have been stuck on this problem for ages now! PLEASE HELP!!
PS- I am using swift-2 and Xcode7
Make sure that you are not pushing a new ViewController after you click on the tableview's cell. It gives me the impression that it might be the case since you are seeing only the title of the last cell you pressed.. Make sure you actually pop the tableview's controller instead of pushing a new one.
If you want to keep using NSUserDefaults, you could just call
navigationController?.popViewController(animated: true)
in your cellSelection function, and read the defaults value in the viewWillAppear of the main ViewController.
override func viewWillAppear(_ animated: Bool) {
let userDefault = NSUserDefaults.standard()
if let beachSelection = userDefault.string(forKey: "BeachControllerSelection") {
textOfBeach = beachSelection
isBeachSelected = true
}
//same for the rest of the other table view's selections
}

How do I segueing to the UIViewController I'm in?

I'm trying to go from one view, filled with data from an object, to the same view but filled with a different object via segue.
Using a segue is necessary as apposed to switching the object and refreshing the view because my users need to be able to go back to the past view controller when they hit the back button.
Example:
(ThisView, with thisObject populating the view) -> (ThisView, with thisOtherObject populating the view)
What I've tried:
presentView Controller: This didn't work because it is not the default segue I'm trying to achieve.
let next: NewClubProfile = storyboard?.instantiateViewControllerWithIdentifier("clubProfile") as! NewClubProfile
presentViewController(next, animated: true, completion: nil)
Using Navigation Controller: Can't figure out how to segue the new object to populate the other view
let next: NewClubProfile = storyboard?.instantiateViewControllerWithIdentifier("clubProfile") as! NewClubProfile
self.navigationController?.pushViewController(next, animated:true)
Should have taken longer to solve this myself before posting this but I couldn't find an answer that fit what I needed. Anyway, solved it, here's what worked for me.
let next: ViewController = storyboard?.instantiateViewControllerWithIdentifier("viewName") as! ViewController
next.object = newObject
self.navigationController?.pushViewController(next, animated:true)