I have a program in which the user will enter data in one view controller and the information is calculated in another. I've gotten to the point where I can enter the data and when I segue to the SecondViewController I can access my data. But when I switch back to the first ViewController, the UITextFields are blank because the ViewController is re-instantiated.
Basically my application so far has one ViewController with two UITextfields and a SecondViewController that shows the data. I need a way to save the instance of the first ViewController when I am switching between the two.
I've tried using the answer from iOS Swift, returning to the same instance of a view controller , but yourVariable cannot be set to nil, so I got stuck there.
This is my code for segue-ing from the first ViewController to the SecondViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if (segue.identifier == "Show")
{
var destinationVC = segue.destinationViewController as! MainViewController
}
}
I only have a button that I press to go back to the first ViewController.
You can create a class to store your text file, create instance of it in appdelegate(in a global scope) and get your text data to the textfield using observer. global instance of a class may not be the best choice if your app is not as simple as you described it in which case you can use NSUsersDefault to save it as plist. I hope I helped :)
//in first view controller
#IBOutlet weak var textfield: UITextField!{
didSet{
textfield.text = text
}
}
var text:String = ""{
didSet{
textfield?.text = text
}
}
func override viewWillAppear(animated: Bool) {
//set value of text from your global class instance or
//decode it here from the archive file if you used NSUsersDefault
text =
}
Related
Trying to pass data from one view controller MainScreenVC to Another RatesVC with protocol and extension, but that's not working, app crashing everytime . I'm clearly see that problem with code on second VC(because print showing correct data after action on first VC) but not sure where is error.
StoryBoard and 1st VC Example
Second VC
1st View controller
import UIKit
protocol transferNameOfCurrency {
func currencySelected(nameOfCurrency: String)
}
class MainScreenVC: UIViewController {
var transferCurrencyDelegate: transferNameOfCurrency?
var nameOfTheCurrency: String?
#IBAction func updateRates(_ sender: Any) {
nameOfTheCurrency = "EUR"
transferCurrencyDelegate?.currencySelected(nameOfCurrency:
nameOfTheCurrency)
print(nameOfTheCurrency)
}
}
2nd ViewController
import UIKit
class RatesVC: UIViewController {
var currencySelected: String?
override func viewDidLoad() {
super.viewDidLoad()
if let push = self.storyboard?.instantiateViewController(withIdentifier: "MainScreenVC") as? MainScreenVC
{
push.transferCurrencyDelegate = self
}
// Do any additional setup after loading the view.
}
}
extension RatesVC: transferNameOfCurrency {
func currencySelected(nameOfCurrency: String) {
currencySelected = nameOfCurrency
print(currencySelected)
}
}
The most obvious problem lies here:
if let push = self.storyboard?.instantiateViewController(withIdentifier: "MainScreenVC") as? MainScreenVC {
push.transferCurrencyDelegate = self
}
You have to realize that instantiateViewController creates a new view controller - it's not the reference to the view controller presented at the screen. In that code you just created a completely new view controller and then set its delegate to self, but otherwise nothing else.
Without knowing the context it is really hard to suggest anything - prepare(for:) segue might be the place where you want to set the delegate. Anyway, the problem is that you have to obtain a reference to the controller that is presented on the screen, the one that is supposed to be reacting to those events.
Moreover, from the memory management aspect, you should really consider making the delegate property a weak one to prevent memory leaks.
EDIT
So after seeing the minimal working example you provided at link, I think I can provide the solution on how to get that string to the SecondVC.
Your first view controller with comments:
import UIKit
class ViewController: UIViewController {
var newLine: String = "EUR"
#IBAction func push(_ sender: Any) {
// here the secondVC does not exist yet, calling delegate.transferWord() here would have no sense
// performSegue will create that secondVC, but now it does not exist, nor it is set up as the delegate
self.performSegue(withIdentifier: "ViewController", sender: navigationController)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let secondVC = segue.destination as? SecondVC, segue.identifier == "ViewController" {
// at this moment secondVC did not load its view yet, trying to access it would cause crash
// because transferWord tries to set label.text directly, we need to make sure that label
// is already set (for experiment you can try comment out next line)
secondVC.loadViewIfNeeded()
// but here secondVC exist, so lets call transferWord on it
secondVC.transferWord(word: newLine)
}
}
}
No need for delegates here, because your ViewController is the one pushing the SecondVC to the Navigation controller - that means that you can access it directly in prepare(for:), as you can see above.
Now the SecondVC is super simple (I omitted unnecessary code):
import UIKit
class SecondVC: UIViewController {
#IBOutlet weak var label: UILabel!
func transferWord(word: String) {
label.text = word
}
}
Storyboards can stay as they are.
I am trying create Tab Bar application. I added to the first viewcontroller a button and a text field. When I wrote some text and pushed button I expected that loads second viewcontroller and in the label field appears text from the text field.
I have 2 problems.
Second viewcontroller loaded twice.
when I push button on tab bar and load first viewcontroller again? me text disappear.
I only start work with Xcode. Help me please and describe resolve in details🙏
import UIKit
var tfTextString: String = ""
class FirstViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
#IBAction func enter(_ sender: Any) {
if textField.text != "" {
//performSegue(withIdentifier: "segue", sender: self)
//tfTextString = textField.text!
self.tabBarController?.selectedIndex = 0
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var secondController = segue.destination as! SecondViewController
secondController.myString = textField.text!
}
}
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var myString = String()
override func viewDidLoad() {
super.viewDidLoad()
label.text = myString
}
}
It looks like your view controller is loaded twice because you are loading it twice.
Don't think of each screen on the storyboard as being it's own distinct entity as per the singleton pattern. They aren't. They're their own distinct class / view combination, which gets instantiated (loaded) once every time you call it. To put it another way, you aren't looking at the 'actual' classes in the storyboard, you're looking at the template that's used to build the class when called for. So you're calling for it twice, in two different places, and it happily produces multiple copies of the underlying view / controller for you.
I think you don't want to use a navigation controller to 'push' the view back onto the stack. You're going to want to tell the tab bar to switch to a different view instead.
The swift version of the code you're looking for is:
self.tabBarController?.selectedIndex = 1
I think the objective-C equivalent would have been...
[[self tabBarController] setSelectedIndex:1];
But it's been long enough since I've done swift I may have overlooked something important in there.
I am looking for help in figuring out how to have a row in a MultivaluedSection present a view controller with a second Eureka form and return a value back to the MultivaluedSection row. I've been able to get a regular ButtonRow to push a view controller using a segue, but I can't figure out not to get a value back to the row in the MultivaluedSection. I'm not sure if the ButtonRow method supports returning values or not so I started looking for other solutions. One I found is to use a custom presenter row (https://github.com/xmartlabs/Eureka#custom-presenter-rows), but I don't understand how to make that work.
Here one thing I did find, but again, I don't understand how to put this all together:
Help creating simple Custom Presenter Row
- https://github.com/xmartlabs/Eureka/issues/716
Can someone either point me to a working sample or help walk me through getting this setup?
If you are already pushing a new VC with a segue, then you might want to implement a protocol and define the functions to pass data back.
Here is a good tutorial with Navigation controllers where at the end a Protocol is added.
eg:
View one (could be the Form View Controller with the ButtonRow)
class FormVC: FormViewController , FooViewControllerDelegate{
var text : String!
override func viewDidLoad() {
super.viewDidLoad()
}
/// Delegate protocol callback implementation
func myVCDidFinish(controller: FooViewController, text: String) {
// Receive the data as a delegate
self.text = text
// In this case we also want to finish the view
controller.navigationController?.popViewController(animated: true)
}
/// This represents the prepare for segue mentioned as implemented in the question
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Act upon the segue we want from this VC
// The string is defined in the storyboard, so it must be exactly the same
if segue.identifier == "mySegue"{
// Creating the second VC instance
let vc = segue.destination as! FooViewController
// Since this class is now a delegate, setup the delegate
vc.delegate = self
}
}
}
View two (the pushed View controller)
protocol FooViewControllerDelegate {
func myVCDidFinish(controller: FooViewController, text: String)
}
class FooViewController: UIViewController {
/// Data
var text : String!
/// Set up an optional delegate
var delegate:FooViewControllerDelegate? = nil
override func viewDidLoad() {
super.viewDidLoad()
// Init label
self.text = "Pushed view data to pass back
}
}
I have an app setup to have a Master root viewController with a Navigation bar that has a "Settings button". When the user taps the settings button, it brings up a 'settingsTableViewController' that is not dynamic, but rather static so i can format the tableviews with settings like style.
in this settings view, i have a UITextField that takes a Person's name.
When the user clicks "Done" in the navigation bar, i want to pass that name back to the Master Root ViewController so i can use it in the title to display the Person's name. (I want to pass the name upon dismissing the view)
I have tried to use segue's but no luck. Here is a bit of what i tried.
SettingsViewController (pop's over the MasterVC)
class SettingsTableViewController: UITableViewController {
#IBOutlet weak var nameTextfield: UITextField!
#IBAction func done(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
override viewDidLoad(){
self.title = "Settings"
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Trying to pass what was typed in the textfield to the Root ViewController and store this name in a variable of type String called 'name'
if segue.identifier == "masterView"{
let masterViewController = segue.destinationViewController as! MasterTableViewController
masterViewController.name = nameTextField.text
print("Segue was used")
}
}
}
MasterTableViewController.swift
class MasterTableViewController: UITableViewController {
var name: String!
// I am trying to then display the name entered in the pop-over view in this root view controller's title
override viewDidLoad(){
self.title = name!
}
}
My question is, what did i do wrong? I have tried to use delegation but i get nil and the data doesn't seem to get passed either way so any leads will greatly help. Thanks
There is a different kind of segue called Unwind Segue to do that. It doesn't create a new mvc and its used to pass data back to the vc that presented the current one. here is how to do it
First, go to your storyboard and control drag from settingVc to the exit button on top of it (to itself). it will give you 'Selection Segue' and choose IBAction goBack. This means any vc that presented the current one will get to prepare if they implement this method. In other words, you're putting out a protocol and the presenter vc will conform by implementing goBack IBAction. here is how to implement that. In your Mater vc
//You must have this function before you do the segue in storyboard
#IBAction func goBack(segue: UIStoryboardSegue){
/* this is optional
if let stv = segue.sourceViewController as? SettingsTableViewController{
self.name = stv.nameTextfield?.text
}
*/
}
In your setting vc (current vc)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//make sure you set the id of your segue to "Your Go back Unwind segue". ofc u can name it whatever
if segue.identifier == "Your Go back Unwind segue"{
if let mtvc = segue.destinationViewController as? MasterTableViewController{
mtvc.name = nameTextField.text
print("Segue was used")
}
}
}
and done method should be something like this
#IBAction func doneEditing(sender: UIButton) {
presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
}
I am re-writing a tutorial converting the code from Objective-C to swift. The app moves from VC one where there is 3 sliders (Red, Green and Blue) that set the background colour, a label of the colour name and a button that links to the second VC. In the second VC the colour from the first VC is used as the background and the user has a chance to name the colour.
When the user enters the colour name it should return the new colour name to the orginal VC and the label that shows the colour name should show the text entered.
The following is the code that is causing issue:
func textFieldShouldReturn(nameEntry: UITextField) -> Bool
{
ViewController().colourLabel.text = nameEntry.text
nameEntry.resignFirstResponder()
dismissViewControllerAnimated(true, completion: nil)
return true
}
The error "fatal error: unexpectedly found nil while unwrapping an Optional value" is generated. However debugging nameEntry.text has a string in it.
I'm a little stumped. I could try and do a prepare for unwind segue but it is meant to be a tutorial app.
Cheers
ViewController() actually creates a new instance of your ViewController. This is not a reference to the already existing ViewController. What you can do is create a weak variable pointing to first ViewController inside the second ViewController and set it at prepareForSegue or when the second View controller is shown.
class SecondViewController : UIViewController {
weak var firstViewController : ViewController?
// Other code
func textFieldShouldReturn(nameEntry: UITextField) -> Bool
{
firstViewController?.colourLabel.text = nameEntry.text
nameEntry.resignFirstResponder()
dismissViewControllerAnimated(true, completion: nil)
return true
}
}
Inside First View Controller prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SecondViewController" {
let secondViewController = segue.destinationViewController as SecondViewController
secondViewController.firstViewController = self
}
}
It's possible that the view controller returned by ViewController() has not yet loaded its views. You could try checking this in a setter function and storing it for later use once the views have been loaded.
class VC : UIViewController {
#IBOutlet weak var colourLabel: UILabel!
var savedLabelText: String?
override func viewDidLoad() {
super.viewDidLoad()
self.colourLabel.text = self.savedLabelText
}
func setColorLabelText(label: String) {
if self.isViewLoaded() {
self.colourLabel.text = label
}
else {
self.savedLabelText = label
}
}
}