Swift - Passing data form UITextField to TextView in another viewController - ios

I know, the topic of this question was asked several times before but I don't get the right solution for my problem, so I hope someone can help me.
I have two viewControllers. In firstVC, I change my text with an textView. This text is stored in UserDefaults. So now, I'd like to display the stored text on my secondVC in a textView.
My code for firstVC:
class editprofileViewController: UIViewController {
#IBOutlet weak var changeTextInput: UITextField!
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
let stringKey = UserDefaults.standard
changeTextInput.text = stringKey.string(forKey: "savedStringKey")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func change(_ sender: Any) {
let myText = changeTextInput.text;
UserDefaults.standard.set(myText, forKey: "savedStringKey")
UserDefaults.standard.synchronize()
}
}
Here I write the text in the textfield and store it.
My secondVC:
self.bioTxt.text = // here should be the text from firstVC
bioTxt.isUserInteractionEnabled = false
bioTxt.textAlignment = NSTextAlignment.center
Thanks for your help!

First ViewController
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var txtView: UITextView! //your textView
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: - Button Action
#IBAction func btnClick(_ sender: UIButton) {
let userDefaultStore = UserDefaults.standard //userDefault object
userDefaultStore.set(txtView.text, forKey: "key_Value") //store textView value in userDefault
//navigate secondViewController
let secondVC = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
self.navigationController?.pushViewController(secondVC, animated: true)
}
}
Second ViewController
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var txtView: UITextView! // your textView
override func viewDidLoad() {
super.viewDidLoad()
let userDefault = UserDefaults.standard //create UserDefault object
txtView.text = userDefault.string(forKey: "key_Value")!//get userdefault value using same key which used to store date and set in textView
txtView.isUserInteractionEnabled = false
}
}

Storing in USerDeaults everytime is not encourageable. Remember that User defaults can hold a small amount of data. The only highly required information you can put into this.
Coming to your scenario, if you are navigating from FirstVC to SecondVC every time, then just add one variable globally on second view controller and assign in firstView controller before navigating to second view controller.

You can user UserDefaults.standard.string(forKey: ) function to get the value stored in UserDefaults. Try this
self.bioTxt.text = UserDefaults.standard.string(forKey: "savedStringKey")

Related

Passing text from a TextLabel in a View, to a Label from another View

I have a TextLabel in the ViewController VC, which will receive the input of the user whenever the user has put text on there. Then, pressing a button, that text that was on the TextLabel, will pass onto a Label at the SecondaryView VC. But the thing is that I have tried multiple ways to set the text from the TextLabel to the Label on the SecondaryView VC.
This is the first way I tried:
This is my ViewController.swift file.
class ViewController: UIViewController {
var text: String = ""
#IBOutlet weak var mainViewTextLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is SecondaryView {
let vc = segue.destination as? SecondaryView
vc?.text = "\(mainViewTextLabel!)"
}
}
}
#IBAction func onButtonTap(_ sender: Any) {
}
}
And this is my SecondaryView.swift file:
import UIKit
class SecondaryView: UIViewController {
var text:String = ""
#IBOutlet weak var secondaryViewLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
secondaryViewLabel?.text = text
}
}
When I run the app and type any text in the TextField and press the Button to go to the SecondaryView VC, there is no text on there.
If someone knowns another way to pass text from a View to another, or a way that the text can appear, I would appreciate it.
You have a couple of issues, I think.
Firstly, you are calling prepare(for:...) within viewDidLoad. This function isn't something you call yourself. It's something that you provide an implementation for and the system calls it just before the segue.
The second is that you are passing a reference to a UITextField rather than the text of that text field.
Maybe something like this would be better:
class ViewController: UIViewController {
var text: String = ""
#IBOutlet weak var mainViewTextLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do anything else you need to do when loading
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is SecondaryView {
let vc = segue.destination as? SecondaryView
vc?.text = mainViewTextLabel.text ?? ""
}
}
}
It looks like you are not passing through the text of the UITextField. Instead you are passing through a reference to the UITextField as a string.
What you want to do is access the .text property:
vc?.text = mainViewTextLabel.text!
The .text property returns a String optional or String? and it is generally bad practice to force unwrap it, since it could crash your application. Instead, you can use a guard/let statement to make sure it is not null. So:
vc?.text = "\(mainViewTextLabel!)"
can be replaced by:
guard let textFromTextField = mainViewTextLabel.text else {
return
}
vc?.text = textFromTextField

delegate method not getting called with UITabBarController

In FourthViewController, I have a slider, which has values ranging from 1 to 1000. The value that is set gets sent via the delegate to PatternViewController, where it should be used to do sth (I put the print for testing purposes).
I've worked with delegates before and it was all ok, checked the code multiple times and multiple answers here on stack, I can't seem to find the issue. Any help would be much appreciated
update: I have added a button so that it would be easier to track along. It turns out that by pressing first time the button, nothing happens. but if I first checkout the PatternViewController, then I go back to FourthViewController and press the button, the delegate gets triggered. anyone got any idea on why is this happening?
FourthViewController
import UIKit
class FourthViewController: UIViewController {
//MARK: Outlets
#IBOutlet var persistenceButton: UIButton!
#IBOutlet var persistenceSlider: UISlider!
#IBOutlet var persistenceLabel: UILabel!
weak var delegate: FourthViewControllerDelegate?
//MARK: Stored Properties - Constants
let userDefaults = UserDefaults.standard
let keyName = "sliderValue"
//MARK: Initializer
override func viewDidLoad() {
super.viewDidLoad()
loadSliderValue()
initialSetUp()
}
//MARK: Actions
#IBAction func handleValueChanged(_ sender: UISlider) {
updateLabel()
persistSliderValue(value: persistenceSlider.value, key: keyName)
}
//MARK: Methods
func updateLabel() {
persistenceLabel.text = String(format: "%.2f", persistenceSlider.value)
}
func persistSliderValue(value: Float, key: String) {
userDefaults.set(value, forKey: key)
}
func loadSliderValue() {
let persistedValue = userDefaults.float(forKey: keyName)
persistenceSlider.value = persistedValue
updateLabel()
}
}
func initialSetUp() {
persistenceButton.addTarget(self, action: #selector(handleButtonPressed), for: .touchUpInside)
}
#objc func handleButtonPressed() {
delegate?.valueChanged(value: persistenceSlider.value)
}
}
PatternViewController
import UIKit
class PatternViewController: UIViewController, FourthViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setUp()
}
func setUp() {
if let tabBar = self.tabBarController, let viewController = tabBar.viewControllers, let fourthViewController = viewController[3] as? FourthViewController {
fourthViewController.delegate = self
}
}
func valueChanged(value: Float) {
print(value)
}
}
It depends upon how you instantiated the tab view controller. If you do it with storyboards, for example, the view controllers for the respective tabs are instantiated lazily, only instantiated as the user taps on them. (This helps reduce latency resulting from instantiating all four of the tabs’ view controllers.)
While you theoretically could go ahead and have the tab bar controller instantiate the four view controllers programmatically up front, rather than just-in-time via the storyboard, I might instead consider specifying a UITabBarControllerDelegate for the tab bar controller. Have the tab bar controller’s delegate method update the relevant tab’s view controller’s model.
Here is an example with two tabs, the first has a slider and the second has a label that displays the slider’s value. In this simplified example, I’ve moved the model object (the value associated with the slider) into the tab bar controller, and it passes it to the second view controller when you select the associated tab.
// TabViewController.swift
import UIKit
class TabBarController: UITabBarController {
var value: Float = 0.5
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
}
// MARK: - UITabBarControllerDelegate
extension TabViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
guard let viewController = viewController as? SecondViewController else { return }
viewController.value = value
}
}
And
// FirstViewController.swift
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
guard let tabBarController = tabBarController as? TabViewController else { return }
slider.value = tabBarController.value
}
#IBAction func didAdjustSlider(_ sender: UISlider) {
guard let tabBarController = tabBarController as? TabViewController else { return }
tabBarController.value = sender.value
}
}
And
// SecondViewController.swift
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
var value: Float = 0 { didSet { updateLabel() } }
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .percent
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
updateLabel()
}
func updateLabel() {
label?.text = formatter.string(for: value)
}
}
Probably needless to say, I not only set the base view controller class for the two tab’s view controllers, but also set the base class for the tab bar controller’s storyboard scene to the above TabBarController.

Swift 3 - Delegate always returns nil value

UPDATED:
I have designed custom tabBar using buttons. I have 3 tabs,
First tab has Messages icon, Second has Profile icon and Third has Photos icon. For third tab button, I have used uiCollectionView() where I need to set images.
For the Third tab's ViewController,there is one condition that I need to check, before changing the title of the first tab button. If messages JSON array is not empty then set "new message" title on the first tab button, else the Messages icon won't change.
There is one ParentTabViewController which has these 3 tabs, I have used uiView, where I change the content according to the tab buttons pressed. I tried to access the values of 3rd tab in ParentTabViewController by using delegate, but the delegate is always nil. I did like this:
class ParentTabViewController: UIViewController,MessageDelegateProtocol{
#IBOutlet weak var contentView: UIView!
#IBOutlet var tabBarButtons : [UIButton]!
#IBOutlet weak var firstTabButton: UIButton!
var MessageVC : UIViewController!
var ProfileVC : UIViewController!
var PhotosVC : UIViewController!
var viewControllers : [UIViewController]!
var message : String!
var selectedIndex:Int = 0
var photoVC = PhotosVC()
override func viewDidLoad() {
super.viewDidLoad()
photoVC.newMessageDelegate = self
let storyBoard = UIStoryboard(name:"Main", bundle:nil)
MessageVC = storyBoard.instantiateViewController(withIdentifier: "messagevc")
ProfileVC = storyBoard.instantiateViewController(withIdentifier: "profile")
PhotosVC = storyBoard.instantiateViewController(withIdentifier: "photos")
viewControllers = [MessageVC, ProfileVC, PhotosVC]
tabBarButtons[selectedIndex].isSelected = true
didPressTabs(tabBarButtons[selectedIndex])
}
#IBAction func didPressTabs(_ sender: UIButton)
{
let previousIndex = selectedIndex
selectedIndex = sender.tag
tabBarButtons[previousIndex].isSelected = false
let previousVC = viewControllers[previousIndex]
previousVC.willMove(toParentViewController: nil)
previousVC.removeFromParentViewController()
previousVC.view.removeFromSuperview()
sender.isSelected = true
let presentVC = viewControllers[selectedIndex]
addChildViewController(presentVC)
presentVC.view.frame = contentView.bounds
contentView.addSubview(presentVC.view)
presentVC.didMove(toParentViewController: self)
if selectedIndex == 2{ // this is what I thought of doing.Correct me if wrong.
// check the condition
// if messagesArray != nil
// set the first tab title "new message"
}
else{
// do not change the button image
}
}
func sendMessage(message : String)
{
self.message = message
print("message........", self.message, "\n\n")
}
}
Here is the View Controller for 3rd tab:
import UIKit
protocol MessageDelegateProtocol:class {
func sendMessage(message : String)
}
class PhotosVC: UIViewController,UICollectionViewDataSource, UICollectionViewDelegate{
#IBOutlet weak var collectionView: UICollectionView!
var userMessageArray = [UserMessageClass]() // array of model class
var newMessage : String!
weak var newMessageDelegate : MessageDelegateProtocol?
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
loadData() // function to get json reponse
}
// implement collectionView delegate and dataSource methods
func getData(newMsg : UserMessageClass) //func to get values from model class
{
newMessage = newMsg.messageString // here I get the "new message" String
newMessageDelegate?.sendMessage(message: newMessage)
} enter code here
func loadData()
{
// get json response. And pass the payload to UserMessageClass using that class's array
userMessageArray.append(UserMessageClass(dict : jsonData))
var msgData = UserMessageClass(dict: jsonData)
getData(alarm: msgData)
}
}
I tried searching a lot about accessing tab buttons in another VC, but didn't find any nearby approach as such. Also I am not able to figure out why delegate is always nil. Suggestions or Help would be grateful. Many Thanks :)
The problem is the following line.
let firstTab = storyBoard.instantiateViewController(withIdentifier: "parentVC") as! ParentViewController
You are probably expecting it to give you the instance of ParentViewController which you have setup initially. However, it will give you the instance of a newly initiated ParentViewController which is not what you want.
To counter this problem you can either make use of a delegate or completion block defined which will be defined inside your ParentViewController class.
Update:
Try adding PhotosVC.newMessageDelegate = self under the line
PhotosVC = storyBoard.instantiateViewController(withIdentifier: "photos")
Also change var PhotosVC : UIViewController! to var photosVC: PhotosVC!
This should work now.

Loading a switch state from NSUserDefault on load

I am working on a settings view for a basic app. Basic, in there is just one switch in the settings view for the user. The switch setting is saved with NSUserDefault. I use a delegate to send the switch signal from the settings view to the main view. The delegation works properly.
The UI is basic. On the main view, a label will read On in green (if the switch is on) and Off in red (if the switch is off.) There is a setting button in the top right that will segue (settingsSegue) to the settings UITableViewController, where the UISwitch is located.
The problem is loading up the NSUserDefault once the app loads. In viewDidLoad, I check to see if there's a value saved for the switch key. If there is, load it up. If not, set it to false (in the storyboard, the switch is set to false as default.) The Switch Status loads up as Off every time. Even if the default value is On. This shouldn't be happening.
ViewController.swift:
import UIKit
var nsDefaults = NSUserDefaults.standardUserDefaults()
class ViewController: UIViewController, SettingsViewControllerDelegate {
var onFromMain = Bool()
#IBOutlet weak var switchStateLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if let mySavedKey = nsDefaults.objectForKey("savedSwitchSettingDefault") {
// A value exists. Load it up.
nsDefaults.objectForKey("savedSwitchSettingDefault")
print("The switch is set! \(mySavedKey)")
checkSwitchState()
}
else {
// Nothing stored in NSUserDefaults yet. Set switch to False.
nsDefaults.setBool(false, forKey: "savedSwitchSettingDefault")
checkSwitchState()
}
}
func myVCDidFinish(controller: SettingsViewController, switchState: Bool) {
onFromMain = switchState.boolValue
checkSwitchState()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "settingsSegue" {
let nav = segue.destinationViewController as! UINavigationController
let secondVC = nav.topViewController as! SettingsViewController
secondVC.delegate = self
}
}
func checkSwitchState() {
if onFromMain {
switchStateLabel.text = "On"
switchStateLabel.textColor = UIColor.greenColor()
}
else {
switchStateLabel.text = "Off"
switchStateLabel.textColor = UIColor.redColor()
}
}
}
SettingsViewController.swift:
import UIKit
protocol SettingsViewControllerDelegate {
func myVCDidFinish(controller: SettingsViewController, switchState: Bool)
}
class SettingsViewController: UITableViewController {
var delegate: SettingsViewControllerDelegate? = nil
#IBOutlet weak var switchOutlet: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
switchOutlet.on = nsDefaults.boolForKey("savedSwitchSettingDefault")
}
#IBAction func closeSettingsPageBarButtonItemPressed(sender: UIBarButtonItem) {
if (delegate != nil) {
delegate!.myVCDidFinish(self, switchState: switchOutlet.on)
self.dismissViewControllerAnimated(true, completion: nil)
}
}
#IBAction func switchPressed(sender: UISwitch) {
// Tap the switch to change the setting.
nsDefaults.setBool(switchOutlet.on, forKey: "savedSwitchSettingDefault")
}
}
I believe my problem lies somewhere in loading up the default key for "savedSwitchSettingDefault". Is this correct? Or does the issue lie elsewhere in the code?
You can tidy things up quite a bit by relying on the fact that the default you want is false and that boolForKey gives you false when the key isn't present.
Also, by accessing the setting in viewWillAppear you can avoid the need for the delegate callback.
ViewController.swift
import UIKit
class ViewController: UIViewController {
let nsDefaults = NSUserDefaults.standardUserDefaults()
var onFromMain = false
#IBOutlet weak var switchStateLabel: UILabel!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.onFromMain = self.nsDefaults.boolForKey("savedSwitchSettingDefault")
self.checkSwitchState()
}
func checkSwitchState() {
if self.onFromMain {
switchStateLabel.text = "On"
switchStateLabel.textColor = UIColor.greenColor()
}
else {
switchStateLabel.text = "Off"
switchStateLabel.textColor = UIColor.redColor()
}
}
}
SettingsViewController.swift:
import UIKit
class SettingsViewController: UITableViewController {
let nsDefaults = NSUserDefaults.standardUserDefaults()
#IBOutlet weak var switchOutlet: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.switchOutlet.on = self.nsDefaults.boolForKey("savedSwitchSettingDefault")
}
#IBAction func closeSettingsPageBarButtonItemPressed(sender: UIBarButtonItem) {
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func switchPressed(sender: UISwitch) {
// Tap the switch to change the setting.
self.nsDefaults.setBool(self.switchOutlet.on, forKey: "savedSwitchSettingDefault")
}
}
When retrieving the bool value from User Defaults, boolForKey will return false if the value doesn't exist. So in this case there's no need for unwrapping. From the documentation:
If a boolean value is associated with defaultName in the user defaults, that value is returned. Otherwise, false is returned.
If the value is getting set (you are sure of it), and the behavior of the app is not working correctly your problem might lie elsewhere.
I would recommend using another approach, declare your "onFromMain" as an optional boolean, then unwrap it when you need it.
var onFromMain: Bool?
...
func checkSwitchState() {
//- This will unwrap your optional or set false if its nil
let switchSate = onFromMain ?? false
//- Then you can set the values based on the value (or the default false)
switchStateLabel.text = switchState ? "On" : "Off"
switchStateLabel.textColor = switchState ? UIColor.greenColor() : UIColor.redColor()
}
Then attach the debugger with a breakpoint and see if the value is being unwrapped or if its defaulting to false.
Also, you are setting your delegate only when the segue is called, depends of the scenario, and if i understand you correctly, you migt not get the value until you have actually navigated to the settings view. So when opening the app (without navigating to the settings view) the onFromMain will never get populated.
Alternatively you can fetch the value on the view did load method to get it straight away when you load the app.

NSUser defaults not saving correctly?

So recently I have been making a practice app that has a textfield and a label. When you enter something into the textfield and hit save it should change the label to that text and save it. The reason why it saves is so when you open the app the text of the label is equal to the last thing you saved. The text will save again if you hit save on a new string in the text box.
The problem I am having is that the label won't appear with text until you save something new and the string isn't saving so their is nothing there even if you saved something.
Here is my code from the ViewController.swift:
//
// ViewController.swift
// Help
//
// Created by Lucas on 9/30/15.
// Copyright (c) 2015 Lucas. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
#IBOutlet var Savedlbl: UILabel!
#IBOutlet var Textfield: UITextField!
#IBOutlet var Label: UILabel!
var current = ""
var Saved = ""
override func viewDidLoad() {
super.viewDidLoad()
let currentDefault = NSUserDefaults.standardUserDefaults()
Savedlbl.text = Saved
if(currentDefault.valueForKey("Saved") != nil)
{
self.Saved = currentDefault.valueForKey("Saved") as! NSString! as String
}
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func Set(sender: AnyObject) {
setall()
}
func setall()
{
current = Textfield.text!
Label.text = Textfield.text!
let currentDefault = NSUserDefaults.standardUserDefaults()
Saved = (currentDefault.valueForKey("saved") as? String)!
currentDefault.setValue(Saved, forKey: "saved")
Savedlbl.text = Textfield.text
currentDefault.synchronize()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Here is where I think the problem is in the view did load that would make sense. When the app loads its not setting it to the last thing saved.
import UIKit
class ViewController: UIViewController {
#IBOutlet var Savedlbl: UILabel!
#IBOutlet var Textfield: UITextField!
#IBOutlet var Label: UILabel!
var current = ""
var Saved = ""
override func viewDidLoad() {
super.viewDidLoad()
let currentDefault = NSUserDefaults.standardUserDefaults()
Savedlbl.text = Saved
if(currentDefault.valueForKey("Saved") != nil)
{
self.Saved = currentDefault.valueForKey("Saved") as! NSString! as String
}
// Do any additional setup after loading the view, typically from a nib.
}
Thanks and let me know if you need any more info, I will provide it as fast as I can!
Don't use valueForKey/setValue:forKey:. Those are KVC methods. You want setObject:forKey: and objectForKey:.
Your problems are that you are doing things in the wrong order. In viewDidLoad you are setting the textfield before you have retrieved the value from user defaults and in setall you are re-saving the previously saved value, not the new value.
Try:
import UIKit
class ViewController: UIViewController {
var saved=""
var current=""
#IBOutlet var Savedlbl: UILabel!
#IBOutlet var Textfield: UITextField!
#IBOutlet var Label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let currentDefault = NSUserDefaults.standardUserDefaults()
self.saved=currentDefault.stringForKey("Saved") ?? ""
self.Savedlbl.text=self.saved
}
#IBAction func Set(sender: AnyObject) {
setall()
}
func setall() {
self.current = self.Textfield.text!
self.Label.text = self.current
let currentDefault = NSUserDefaults.standardUserDefaults()
currentDefault.setObject(self.current,forKey:"Saved")
self.Savedlbl.text = self.Textfield.text
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Also, by convention variables should start with a lower case letter and classes with an upper case. I changed Saved to saved and Current to current but I didn't change the IBOutlets because you will need to remake the connection in IB for those, but you should.

Resources