I have two view controllers and I want to store the "name" entered in the second and to be shown in the first one. I already made it work that a value is stored, and now I want to store the name entered by the user.
So here's the code of the view controller where the name should be shown:
import UIKit
var name = [String]()
class profile: UIViewController {
//Name
#IBOutlet weak var nameTextField: UILabel!
var vornameLabel = String()
override func viewDidLoad() {
super.viewDidLoad()
//Name
nameTextField.text = "\(name)"
//Vorname speichern
NSUserDefaults.standardUserDefaults().setObject(name, forKey: "vorname")
name = NSUserDefaults.standardUserDefaults().objectForKey("vorname") as! [String]
}
And this is the code of second view controller where the "name" is entered:
import UIKit
class profileSettings: UIViewController {
#IBOutlet weak var vornameLabel: UITextField!
#IBOutlet weak var nachnameLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func addInformation(sender: AnyObject) {
vornameLabel.text = "\(name)"
NSUserDefaults.standardUserDefaults().setObject(name, forKey: "vorname")
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//in Profil
var destProfile:profile = segue.destinationViewController as! profile
destProfile.vornameLabel = vornameLabel.text
destProfile.nachnameLabel = nachnameLabel.text
}
}
1) If the user is entering their name and that is going into vornameLabel successfully then in addInformation() you are overwriting what ever the user entered into vornameLable with "(name)" so you have now lost what the user entered.
2) name is not being set to anything (unless you have that in another part of your code that you have not shown)
3) So 1 and 2 together means vornameLable contains nothing.
4) Even if vornameLable did contain something, then in viewDidLoad() you are calling setObject which will overwrite any value that was stored to NSUserDefaults with whatever is in name.
I think you need to show more code, this can't be all of it.If it is then it not doing anything.
Updated after receiving info the posted code is all there is:
a) name doesn't contain anything, therefore you are not saving anything to defaults. Change it to var name = "My name".
b) Change vornameLabel.text = "(name)" tp vornameLabel.text = name
c) change NSUserDefaults.standardUserDefaults().setObject(name, forKey: "vorname") to NSUserDefaults.standardUserDefaults().setObject(vornameLable.text, forKey: "vorname")
d) remove NSUserDefaults.standardUserDefaults().setObject(name, forKey: "vorname") in viewDidLoad
Yeah, set your values in viewWillAppear:
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
name = NSUserDefaults.standardUserDefaults().objectForKey("vorname") as! [String]
nameTextField.text = "\(name)"
}
Related
I'm trying to make a leaderboard for a word scrambler app so I am saving the data before segueing to the next view controller which will eventually be a leaderboard. My outlets are all connected and the segue identifier was written correctly so I don't see why the app crashes after done is pressed
the error line occurs here: class AppDelegate: UIResponder, UIApplicationDelegate {
var finalScore = Int()
var playerName = String()
var allMyStoredData = UserDefaults.standard
class secondVC: UIViewController {
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var nameTF: UITextField!
#IBOutlet weak var doneButton: UIButton!
var playerScore = 0
override func viewDidLoad() {
super.viewDidLoad()
scoreLabel.text = "Your score is: \(finalScore)"
loadData()
}
#IBAction func donePressed(_ sender: Any) {
saveData()
//this part won't execute
performSegue(withIdentifier: "toLeaderboard", sender: self)
}
func saveData () {
playerName = nameTF.text!
playerScore = finalScore
allMyStoredData.set(playerName, forKey: "saveTheName")
allMyStoredData.set(playerScore, forKey: "saveTheScore")
}
func loadData () {
if let loadPlayerName:String = UserDefaults.standard.value(forKey: "saveTheName") as? String {
playerName = loadPlayerName
}
if let loadTheScore:Int = UserDefaults.standard.value(forKey: "saveTheName") as? Int {
playerScore = loadTheScore
}
}
}
Update: there was an outlet in the view controller the segue "toLeaderboard" goes to which wasn't connected or used so I deleted it and now the code is fine
I am creating my first simple budgeting app. Basically, I take a few user inputs like monthly income & savings goal. Then they click "start", & the app calculates stuff such as, their daily budget etc.
Here I'm running into trouble. After all the calculations, I display "how much you can spend each day" (e.g. $20 a day), which I pass forward through segues from their original inputs on the original screen.
Now, in this VC (UserInfoVC) I created a button which lets them add how much money they spent today. So when they click this "add money spent" button, I open a new VC (AddSubtractMoney) where I present a calculator where they can enter how much they spent today (i.e. $12) and click submit.
I run their input compared to their daily budget to get a New daily budget.
Now, I'm having trouble passing this updated number backwards, to display it on the previous VC on the label "dailySpendingLimitLabel". I know segues are not the best way to go about passing data backwards.
I've tried closures, but I end up getting lost in the syntax, and protocols and delegates (it's my 2nd month coding).
Is there a simple way to achieve passing this data back to the previous VC and populating the data in that previous display label?
Below is the code.
The First snippet is from the UserInfoVC where I display their originally entered data that I segued through. The Second snippet is from the AddSubtractMoney class where I placed the calculator and created an object "newestUpdate" inside a function that allows me to calculate the number they entered on the calculator minus their old daily budget. To arrive at a new budget which I want to present backwards to the UserInfoVC.
class UserInfoViewController : ViewController {
var userNamePassedOver : String?
var userDailyBudgetPassedOver : Double = 99.0
var userDailySavingsPassedOver : Double = 778.00
var userMonthlyEarningsPassedOver : Double?
var userDesiredSavingsPassedOver : Double?
var newAmountPassedBack : Double = 0.0
#IBOutlet weak var dailySavingsNumberLabel: UILabel!
#IBOutlet weak var userNameLabel: UILabel!
#IBOutlet weak var dailySpendingLimitLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
userNameLabel.text = userNamePassedOver
dailySpendingLimitLabel.text = String(format: "%.2f", userDailyBudgetPassedOver)
dailySavingsNumberLabel.text = String(format: "%.2f", userDailySavingsPassedOver)
}
#IBAction func addSubtractMoneyPressed(_ sender: UIButton) {
performSegue(withIdentifier: "addOrSubtractMoney", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addOrSubtractMoney"{
let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
addOrSubtractMoneyVC.dailyBudgetPassedThrough = userDailyBudgetPassedOver
}
}
}
extension UserInfoViewController: AddSubtractMoneyDelegate {
func calculatedValue(value: Double) {
dailySpendingLimitLabel.text = String(userDailyBudgetPassedOver - value)
}
}
import UIKit
protocol AddSubtractMoneyDelegate {
func calculatedValue(value: Double)
}
class AddSubtractMoney: UIViewController {
#IBOutlet weak var outputLabel: UILabel!
var runningNumber = ""
var finalNumberPassedOver : Double?
var amountPassedBackToUserInfo : Double = 0.0
var dailyBudgetPassedThrough : Double = 0.0
var delegate: AddSubtractMoneyDelegate?
override func viewDidLoad() {
super.viewDidLoad()
outputLabel.text = "0"
// Do any additional setup after loading the view.
}
#IBAction func buttonPressed(_ sender: UIButton) {
runningNumber += "\(sender.tag)"
outputLabel.text = runningNumber
}
#IBAction func submitNewInfo(_ sender: UIButton) {
// FIX FIX
AddSubtractMoneyController.addToMoneySpentArray(amountISpent: outputLabel.text!)
sendBackUpdatedNumber()
dismiss(animated: true, completion: nil)
}
#IBAction func allClearedPressed(_ sender: UIButton) {
runningNumber = ""
outputLabel.text = "0"
}
// THIS LINE PRODUCES THE CORRECT INPUT IN OUTPUT CONSOLE WHEN I PRINT- BUT I CANT FIGURE HOW TO TRANSFER IT BACK TO PREVIOUS VC
func sendBackUpdatedNumber(){
let newestUpdate = UserInfo(whatYouSpentToday: runningNumber, oldDailyBudgetPassed: dailyBudgetPassedThrough)
amountPassedBackToUserInfo = dailyBudgetPassedThrough - Double(runningNumber)!
newestUpdate.goalToSaveDaily = amountPassedBackToUserInfo
print(amountPassedBackToUserInfo)
self.delegate?.calculatedValue(value: amountPassedBackToUserInfo)
}
}
My suggestion is to use a callback closure. It's less code and easier to handle than protocol / delegate.
In AddSubtractMoney declare a callback variable and call it in sendBackUpdatedNumber passing the Double value
class AddSubtractMoney: UIViewController {
// ...
var callback : ((Double)->())?
// ...
func sendBackUpdatedNumber(){
let newestUpdate = UserInfo(whatYouSpentToday: runningNumber, oldDailyBudgetPassed: dailyBudgetPassedThrough)
amountPassedBackToUserInfo = dailyBudgetPassedThrough - Double(runningNumber)!
newestUpdate.goalToSaveDaily = amountPassedBackToUserInfo
print(amountPassedBackToUserInfo)
callback?(amountPassedBackToUserInfo)
}
}
In prepare(for segue assign the closure to the callback variable and add the code to be executed on return
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addOrSubtractMoney"{
let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
addOrSubtractMoneyVC.callback = { result in
print(result)
// do something with the result
}
addOrSubtractMoneyVC.dailyBudgetPassedThrough = userDailyBudgetPassedOver
}
}
Using delegate
if segue.identifier == "addOrSubtractMoney" {
let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
addOrSubtractMoneyVC.dailyBudgetPassedThrough = userDailyBudgetPassedOver
addOrSubtractMoneyVC.delegate = self
}
}
You need to add delegate property in AddSubtractMoney class
var delegate: AddSubtractMoneyDelegate?
Create Protocol in AddSubtractMoney class
protocol AddSubtractMoneyDelegate {
func calculatedValue(value: Double)
}
And respond to delegate
func sendBackUpdatedNumber(){
let newestUpdate = UserInfo(whatYouSpentToday: runningNumber, oldDailyBudgetPassed: dailyBudgetPassedThrough)
amountPassedBackToUserInfo = dailyBudgetPassedThrough - Double(runningNumber)!
newestUpdate.goalToSaveDaily = amountPassedBackToUserInfo
print(amountPassedBackToUserInfo)
self.delegate.calculatedValue(value: amountPassedBackToUserInfo)
}
Now you need to implement this delegate method in class where delegate is set.
Here in UserInfoViewController class delegate is set so you need to implement its delegate method
extension UserInfoViewController: AddSubtractMoneyDelegate {
func calculatedValue(value: Double) {
//set label here
}
}
You could possibly also use an unwind segue to pass back the data.
If you don't under stand flow behind delegate(protocol oriented), you can simply go through below code. it only works if both class
But it is not a good practice
Learn about protocol, closure, or Notification Center broadcasting for mostly used, flexible and reusable coding methods.
UserInfoViewController
class UserInfoViewController : ViewController {
fun receiveBackUpdatedNumber(numberString:String){
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addOrSubtractMoney"{
let addOrSubtractMoneyVC = segue.destination as! AddSubtractMoney
addOrSubtractMoneyVC.userInfoViewController = self
}
}
}
}
AddSubtractMoney
class AddSubtractMoney: UIViewController {
var userInfoViewController: UserInfoViewController!
var updatedNumber = ""
func sendBackUpdatedNumber(){
self.userInfoViewController.receiveBackUpdatedNumber(numberString: updatedNumber)
}
}
If you are confirtable you can go with protocols.. protocols insist a class to compulsory implement a method, which make code more reusable and independent.
In Above method we are passing instance of current viewcontroller(UserInfoViewController) to next viewcontroller(AddSubtractMoney) on performing segue, So by that we can access any properties of function in UserInfoViewController from AddSubtractMoney. So it make easy to pass data from AddSubtractMoney to -> UserInfoViewController
im attempting to add user data via a create account view controller which contains all UITextFields (password, confirm password, first name, last name, phone number). when the create account button is tapped, the users email shows up in the authentication section on the firebase website but the user information from the first name, last name and phone number text fields are not passed into the database. I'm new to iOS development and have never used firebase so im unsure what the issue is. the app runs without crashing.
below is my Create Account view controller
thanks in advance
import UIKit
import FirebaseAuth
import QuartzCore
import FirebaseDatabase
import Firebase
class CreateAccount: UIViewController {
var refUsers: DatabaseReference!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var confirmPasswordTextField: UITextField!
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var lastNameTextField: UITextField!
#IBOutlet weak var phoneNumberTextField: UITextField!
#IBOutlet weak var alreadyHaveAccountLabel: UILabel!
#IBAction func loginButtonTapped(_ sender: Any) {
performSegue(withIdentifier: "showLoginScreen", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
self.refUsers = Database.database().reference().child("Users");
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
if Auth.auth().currentUser != nil {
print("success")
self.presentMainScreen()
}
}
#IBAction func createAccountTapped(_ sender: Any) {
if let email = emailTextField.text, let password = passwordTextField.text {
Auth.auth().createUser(withEmail: email, password: password, completion:{ user, error in
if let firebaseError = error {
print(firebaseError.localizedDescription)
return
} else {
self.addUser()
print("this is the first name:", self.firstNameTextField.text!)
print("this is the last name:", self.lastNameTextField.text!)
print("this is the phone number" , self.phoneNumberTextField.text!)
print("success")
self.presentMainScreen()
}
})
}
}
func addUser(){
let key = refUsers.childByAutoId().key
let user = ["id":key,
"FirstName":firstNameTextField.text! as String,
"LastName":lastNameTextField.text! as String,
"PhoneNumber":phoneNumberTextField.text! as String
]
refUsers.child(key).setValue(user)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
func presentMainScreen(){
let mainstoryboard = UIStoryboard(name: "Main", bundle: nil)
let mainTabController = mainstoryboard.instantiateViewController(withIdentifier: "MainTabController") as! MainTabController
mainTabController.selectedViewController = mainTabController.viewControllers?[0]
self.present(mainTabController, animated: true, completion: nil)
//let storyboard:UIStoryboard = UIStoryboard(name:"Main", bundle:nil)
//let loggedInVC:LoggedInVC = storyboard.instantiateViewController(withIdentifier: "LoggedInVC") as! LoggedInVC
//self.present(loggedInVC, animated: true, completion: nil)
}
}
Try this:
Instead of set value use update value
let childUpdates = ["/user/\(key)": user]
refUser.updateChildValues(childUpdates)
Hope this helps :)
I'm following a very simple swift course and while everything works for the instructor the same code does not execute on my side and I'm trying to understand why is that happening.
The app is very simple, is consists of adding tasks to a TableView with a name and a switch to determine if those are important (in which case an emoji is added to the name)
While trying to access and modify the "name" attribute of my "Taskentity" core data entity, the editor gives me the error "Value of type "Taskentity" has no member "name"".
The code is the following :
import UIKit
class AddTaskViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var isImp: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btnTapped(_ sender: Any) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let task = Taskentity(context: context)
task.name = textField.text! // **!error!**
task.isImportant = isImp.isOn
(UIApplication.shared.delegate as! AppDelegate).saveContext()
navigationController!.popViewController(animated: true)
}
}
And my Core Data file looks like this :
Thanks for any help!
Your screen shot shows the problem perfectly. You have named the attribute corename, not name. So naturally the name name is not its name!
You have a problem with you attributes. The one titled 'corename' which is a string should be renamed to 'name' Xcode cannot tell what .name means because you have nothing that Devi gets what it is.
I have attached how your code should look to fix this problem:
import UIKit
class AddTaskViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var isImp: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btnTapped(_ sender: Any) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let task = Taskentity(context: context)
task.corename = textField.text! // **!error!**
task.isImportant = isImp.isOn
(UIApplication.shared.delegate as! AppDelegate).saveContext()
navigationController!.popViewController(animated: true)
}
}
I am trying to pass a news ID of type string to the second VC and load the object based on it from Realm. When I debugged, I found that the prepare for segue is correctly setting the detailNewsVC.newsID to the primary key of my news item but the second VC is not receiving it. Any help on this?
Checks I have made:
Made sure that the detail VC identifier is correct
detailNewsVC.newsID in VC 1 is correctly setting the news ID .. This is to make sure that realm is correctly sending the newsID and it is working fine.
Changed the viewDidLoad in VC 2 to viewWillLoad..Just to make sure that second vc is not loaded before for any reason but no luck on that.
Restarted xcode
Replaced newsID in VC 2 with an actual news primary key and it's correctly pulling the related news. I think the culprit is that the VC2 property: newsID is not updating when prepare for segue is called.
First VC code for prep for segue:
extension HomeVC: UICollectionViewDelegate {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == SegueIdentifier.gotodetail.rawValue, let sendNewsID = sender as? String {
let navVC = segue.destination as? UINavigationController
let detailNewsVC = navVC?.viewControllers.first as! DetailNewsVC
detailNewsVC.newsID = sendNewsID
print("Detail News ID = \(detailNewsVC.newsID)")
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let newsID = newsArray[indexPath.row].newsId
performSegue(withIdentifier: SegueIdentifier.gotodetail.rawValue, sender: newsID)
}
}
Second VC Code:
class DetailNewsVC: UIViewController {
#IBOutlet private weak var scrollView: UIScrollView!
#IBOutlet private weak var newsTitle: UILabel!
#IBOutlet private weak var newsImage: UIImageView!
#IBOutlet private weak var newsDescription: UILabel!
var newsID = ""
override func viewDidLoad() {
super.viewDidLoad()
let realm = try! Realm()
print("News ID: \(newsID)")
guard let news = realm.object(ofType: News.self, forPrimaryKey: newsID as AnyObject) else {
print("Cannot load news")
return
}
print(news)
newsTitle.text = news.newsTitle
if let url = URL(string: news.urlToImage), let data = try? Data.init(contentsOf: url) {
newsImage.image = UIImage(data: data)
}
newsDescription.text = news.newsDescription
}
}
Move your prepare function out of the extension and put it in HomeVC. According to Apple's Swift Guide extensions cannot override existing functionality.
Extensions can add new functionality to a type, but they cannot override existing functionality.
Apple Developer Guide
It's hard to tell in which order UIKit calls the UIViewController methods, but it might be possible that viewDidLoad is getting called before you get the chance to set the value of newsID.
The following might be overkill, but it'll guarantee the views will be updated during viewDidLoad, or otherwise if newsID is set after the fact:
class DetailNewsVC: UIViewController {
#IBOutlet private weak var scrollView: UIScrollView!
#IBOutlet private weak var newsTitle: UILabel!
#IBOutlet private weak var newsImage: UIImageView!
#IBOutlet private weak var newsDescription: UILabel!
public var newsID = "" {
didSet {
updateUIForNews()
}
}
override func viewDidLoad() {
super.viewDidLoad()
updateUIForNews()
}
private func updateUIForNews() {
guard !newsID.isEmpty else {
return
}
let realm = try! Realm()
print("News ID: \(newsID)")
guard let news = realm.object(ofType: News.self, forPrimaryKey: newsID as AnyObject) else {
print("Cannot load news")
return
}
print(news)
newsTitle.text = news.newsTitle
if let url = URL(string: news.urlToImage), let data = try? Data.init(contentsOf: url) {
newsImage.image = UIImage(data: data)
}
newsDescription.text = news.newsDescription
}
}