Issues passing data from text field with unwind segue - ios

I am following the accepted answer to this question, but I am having issues passing the data from ViewControllerB (second View Controller) back to ViewController (initial View Controller). Based on the output of a few print statements I added, I think the unwind to ViewController is happening before textFieldDidEndEditing.
dataPassed from ViewControllerB: Default passed data
newValue: Default passed data
textFieldDidEndEditing: Pass this back
I was able successfully pass the data back to ViewController by creating an outlet to the button on ViewControllerB and setting dataPassed = textField.text. Another way I was able to get this to work was by changing dataReceived = sourceViewController.dataPassed to dataReceived = sourceViewController.textField.text in the unwindToThisController function. But it does not look like the poster of the accepted answer mentioned above used either or these workarounds, and I am unsure if either of these workarounds are an acceptable way to do this.
I am using Xcode 8.2.1 and Swift 3.
ViewController code (initial View Controller)
/* ViewController.swift */
import UIKit
class ViewController: UIViewController {
var dataReceived: String? {
willSet {
print("newValue: " + newValue!)
labelOne.text = newValue
}
}
#IBOutlet weak var labelOne: UILabel!
#IBAction func buttonOne(_ sender: UIButton) {
performSegue(withIdentifier: "viewNext", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
labelOne.text = "Default passed data"
}
// Segue ViewController -> ViewControllerB
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "viewNext" {
let viewControllerB = segue.destination as! ViewControllerB
viewControllerB.dataPassed = labelOne.text
}
}
// Segue ViewController <- ViewControllerB
#IBAction func unwindToThisControllerWithSegue(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? ViewControllerB {
print("dataPassed from ViewControllerB: " + sourceViewController.dataPassed!)
dataReceived = sourceViewController.dataPassed
//dataReceived = sourceViewController.textField.text
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
ViewControllerB code (second View Controller)
/* ViewControllerB.swift */
import UIKit
class ViewControllerB: UIViewController, UITextFieldDelegate {
var dataPassed: String?
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
textField.text = dataPassed
textField.delegate = self
}
// Text field delegate methods
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
print("textFieldDidEndEditing: " + textField.text!)
dataPassed = textField.text
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

I am the asker of that question and fortunately I have a backup of that sample project and here is what I found there. This is what I did in the first view controller
var displayValue: Double{
get{
return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
}
set{
display.text?="\(newValue)"
typing=false
}
}
#IBAction func unwind(segue: UIStoryboardSegue){
let foo = segue.sourceViewController as! viewControllerB
let a = foo.dataSent
displayValue = NSNumberFormatter().numberFromString(a!)!.doubleValue
}
And it's working fine. Maybe if you can share the sample project you're working on I can see why it's not working? :)

Related

How to pass Data from the SearchBar to UILabel?

how do you pass data from search Bar to UILabel in a different view controller
Passing Data between View Controllers
I have tried multiple questions here on stack, but it just doesn't work or the simulator ends up crashing for me
class FirstVC: UIViewController, DataEnteredDelegate {
#IBOutlet weak var label: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondViewController" {
let secondViewController = segue.destination as! SecondVC
secondViewController.delegate = self
}
}
func userDidEnterInformation(info: String) {
label.text = info
}
}
// protocol used for sending data back
protocol DataEnteredDelegate: class {
func userDidEnterInformation(info: String)
}
class SecondVC: UIViewController {
// making this a weak variable so that it won't create a strong reference cycle
weak var delegate: DataEnteredDelegate? = nil
#IBOutlet weak var searchBar: UISearchBar!
#IBAction func sendTextBackButton(sender: AnyObject) {
// call this method on whichever class implements our delegate protocol
delegate?.userDidEnterInformation(info: searchBar.text!)
// go back to the previous view controller
_ = self.navigationController?.popViewController(animated: true)
}
}
I don't find anything suspicious in your code which cause app crash. Meanwhile for data transfer between view controller we can use delegation and NotificationCenter. Below code uses NotificationCenter
let kStringTransfer = "StringTransferNotification"
class FirstViewController: UIViewController, DataEnteredDelegate {
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(setString(notify:)), name: NSNotification.Name(rawValue: kStringTransfer), object: nil)
}
func getSecondVC() {
if let second = self.storyboard?.instantiateViewController(withIdentifier: "Second") as? ViewController {
self.navigationController?.pushViewController(second, animated: true)
}
}
#IBAction func searchBarBtn(_ sender: Any) {
//go to search view controller
getSecondVC()
}
#objc func setString(notify: Notification) {
//set search bar string to text field
textField.text = notify.object as? String
}
}
class SecondViewController: UIViewController, UISearchBarDelegate {
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
searchBar.delegate = self
}
#IBAction func goBackBtn(_ sender: Any) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: kStringTransfer), object: searchBar.text)
self.navigationController?.popViewController(animated: true)
}
}
When I need pass just small values between 2 viewControllers, I don't use a delegate, just create a variable in second view and pass like this.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "showView") {
let vc = segue.destination as! secondViewController
vc.stringTemp = "My string"
}
}

Delegate method is not called swift ios?

We have two controller - ControllerA and ControllerB. Controller A contains the normal button and text field(mail id). When we enter the mailid and tap the button. we will present the ViewControllerB and we have an option called to change the email and click back. We are using a delegate to pass the viewControllerB value to ViewController. But delegate function is not called.
ViewControllerB :
protocol countryViewControllerDelegate{
func passMailId(code: String)
}
var delegate: countryControllerDelegate?
#IBAction func createNewFolder(_ sender: Any?) {
delegate?.countryCode(code: emailText.text)
self.dismiss(animated: true, completion: nil)
}
ViewControllerA :
var instance = ViewControllerB()
override func viewDidLoad() {
instance.delegate = self
}
func showCoutryPicker(){
self.performSegue(withIdentifier: "DropDown", sender: self)
}
extension ViewControllerA:countryViewControllerDelegate{
func countryCode(code: String) {
print(code)
}
}
Is any other way to fix this?
Your segue instance is different than the 1 here
var instance = ViewControllerB()
So you should either present
self.present(instance,animated:true,completion:nil)
OR
inside prepareForSegue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "DropDown" {
let des = segue.destination as! ViewControllerB
des.delegate = self
}
}
Simply use prepare(for segue: ) check the code below,
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "DropDown"){
let vc = segue.destination as! ViewControllerB
vc.delegate = self
}
}
The problem you had is that you are creating an instance from ViewControllerB
var instance = ViewControllerB()
And segue on the other hand, it wont work because it would be considered as a new instance rather than the segue destination.
You can use either delegate or instance in the below code. It will be useful for anyone:
ViewController :
class ViewController: UIViewController {
#IBOutlet weak var myTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
myTextField.text = "Hello World"
// Do any additional setup after loading the view, typically from a nib.
}
// Without segue
#IBAction func passData(_ sender: Any) {
let sb = storyboard?.instantiateViewController(withIdentifier: "viewcontroller2") as! ViewController2
sb.passText = "Hello World"
//set self to Delegate
sb.delegate = self
//set self to Instance
sb.instance = self
present(sb, animated: true, completion: nil)
}
// With segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let sb = segue.destination as! ViewController2
sb.passTextSegue = "Hello World with segue"
//set self to Delegate
sb.delegate = self
//set self to Instance
sb.instance = self
}
}
extension ViewController : ViewController2Delegate{
func passValue(Str: String) {
print(Str)
}
}
ViewController2 :
protocol ViewController2Delegate : class {
func passValue(Str:String)
}
class ViewController2: UIViewController {
//Create instance for Delegate
weak var delegate : ViewController2Delegate?
//Create instance for ViewController
var instance: ViewController?
override func viewDidLoad() {
super.viewDidLoad()
myTextField.text = passText
myTextFieldSegue.text = passTextSegue
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var myTextField: UITextField!
var passText: String?
#IBOutlet weak var myTextFieldSegue: UITextField!
var passTextSegue: String?
#IBAction func manage(_ sender: UIButton) {
//Pass value using Delegate
delegate?.passValue(Str: "Happy Coding~")
//Pass value using Instance
instance?.myTextField.text = "Happy Coding~ :)"
dismiss(animated: true, completion: nil)
}
/*
// 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.
}
*/
}

How to pass data from ViewController3 to ViewController1

I am new to Swift and was wondering on how to pass data from ViewController to ViewController that isn’t connected by segue. I have spend 2 hours on this and still can’t figure a way to do it.
Edit: The problem that I am confused on is How to take out all the stacked ViewController expect the root ViewController while being able to pass data back to the first ViewController from ViewController3 with
'navigationController?.popToRootViewController(animated: false)'
class ViewController: UIViewController, nameDelegate {
#IBOutlet weak var label1: UILabel!
#IBOutlet weak var textfield1: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
label1.text = "Name"
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc2 = segue.destination as! ViewController2
vc2.middleOfTransferingName = textfield1.text!
}
#IBAction func buttonPressed1(_ sender: Any) {
performSegue(withIdentifier: "go2", sender: self)
}
func nameThatWasEntered(name: String) {
label1.text = name
}
}
class ViewController2: UIViewController, nameDelegate {
var middleOfTransferingName: String = ""
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc3 = segue.destination as! ViewController3
vc3.transferedWithSegueLabelText = middleOfTransferingName
vc3.namePassBack = self
}
#IBAction func buttonPressed2(_ sender: Any) {
performSegue(withIdentifier: "go3", sender: self)
}
func nameThatWasEntered(name: String) {
}
}
protocol nameDelegate {
func nameThatWasEntered(name: String)
}
class ViewController3: UIViewController {
#IBOutlet weak var label3: UILabel!
#IBOutlet weak var textfield3: UITextField!
var transferedWithSegueLabelText: String = ""
var namePassBack: nameDelegate?
override func viewDidLoad() {
super.viewDidLoad()
label3.text = transferedWithSegueLabelText
}
#IBAction func buttonPressed3(_ sender: Any) {
namePassBack!.nameThatWasEntered(name: textfield3.text!)
navigationController?.popToRootViewController(animated: false)
}
}
I found the way to transfer ViewController3 data to ViewController!
var vc3 = ViewController3()
then making ViewController as the delegate of vc3 upon loading.
override func viewDidLoad() {
super.viewDidLoad()
vc3.namePassBack = self
}
Then going back to ViewController3, I add ""vc3."" before using the delegate.
#IBAction func buttonPressed3(_ sender: Any) {
vc3.namePassBack!.nameThatWasEntered(name: textfield3.text!)
navigationController?.popToRootViewController(animated: false)
}
if you use segue then use 'Unwind segue'
for this write given code in your 'A view controller'
#IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
}
In B_viewcontroller, On press button call 'Unwind segue'.
Note:- With the help of Unwind segue you can send the value for 3ed vc to 1se vc same as you send value form 1st vc to 2nd vc
for more go to this link

Button Triggered Issues

I'm getting data back from secondVC to firstVC and showing it in a text field.
My VC's are FirstVC with a text field and secondVC with a button.
The flow of my VC is when a user clicks on the text field (here text field action Editing Did Begin called) in firstVC then the secondVC will open. And then when the button clicks in secondVC then goes back to firstVC with some data and data will be show in same text field.
So the above is all working fine.
Now I want that the second time when again I click on the text field (now text field contains some data) so then again go to secondVC.
The problem is that now the text field contains data. When I click on it, it doesn't work because of button action property Editing Did Begin.
How to handle this?
Below is my code,
First VC
import UIKit
class firstViewController: UIViewController, UITextFieldDelegate, MyProtocol {
var valueSentFromSecondViewController : String?
#IBOutlet weak var myTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func myTextFieldACTIONWhenEditingDidBegin(_ sender: Any) {
myTextField.isUserInteractionEnabled = false
let secondVC = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController") as! secondViewController
secondVC.delegate = self
self.navigationController?.pushViewController(secondVC, animated: true)
}
func setResultsAfterEvaluation(valueSent: String) {
self.valueSentFromSecondViewController = valueSent
print(valueSentFromSecondViewController!)
myTextField.text = valueSent
//print(valueSent)
}
}
Second VC
import UIKit
protocol MyProtocol {
func setResultsAfterEvaluation(valueSent: String)
}
class secondViewController: UIViewController {
var delegate : MyProtocol?
var sentValue : String?
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func btn(_ sender: Any) {
sentValue = "Ahtazaz"
delegate?.setResultsAfterEvaluation(valueSent: sentValue!)
self.navigationController?.popViewController(animated: true)
}
}
I believe this is a more simple way to do what you are trying to achieve, as it doesn't involve protocols and extra functions. If you have any questions, please ask. :)
First View Controller:
import UIKit
//By declaring a variable outside of any class, it is always active in memory and accessible anywhere.
var textFieldText: String = ""
class ViewController1: UIViewController, UITextFieldDelegate {
#IBOutlet weak var textField1: UITextField!
func textFieldDidBeginEditing(_ textField: UITextField) {
//textField.resignFirstResponder is used to dismiss the keyboard. By putting it in this function, it hides the keyboard, which prevents users from entering custom text into your text field.
textField.resignFirstResponder()
let VC2 = self.storyboard?.instantiateViewController(withIdentifier: "ViewController2") as! ViewController2
self.navigationController?.pushViewController(VC2, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
//This links textField1 on your storyboard to the textFieldDidBeginEditing function.
textField1.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//Every time the view is about to appear, this is called. This is where we update the text field's text.
textField1.text = textFieldText
}
}
Second View Controller:
import UIKit
class ViewController2: UIViewController {
#IBOutlet weak var button1: UIButton!
#IBOutlet weak var button2: UIButton!
//If the data you are trying to pass is the button's title, use these two functions.
#IBAction func button1Tapped(_ sender: UIButton) {
textFieldText = (button1.currentTitle)!
self.navigationController?.popViewController(animated: true)
}
#IBAction func button2Tapped(_ sender: UIButton) {
textFieldText = (button2.currentTitle)!
self.navigationController?.popViewController(animated: true)
}
//If the data you are trying to pass is not the button's title, use these.
#IBAction func button1Tapped(_ sender: UIButton) {
textFieldText = "Your Text Here"
self.navigationController?.popViewController(animated: true)
}
#IBAction func button2Tapped(_ sender: UIButton) {
textFieldText = "Your Text Here"
self.navigationController?.popViewController(animated: true)
}
}

View Controller delegate returns nil

My code is below. When I press the 'Done' or 'Cancel' buttons, it doesn't work as I want. I did some debugging, and delegate is nil even though I set it. Please help - thanks.
class ViewController: UIViewController,EditViewControllerDelegate {
#IBOutlet weak var label: UILabel!
//页面跳转时
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditView" {
//通过seque的标识获得跳转目标
let controller = segue.destinationViewController as! EditViewController
//设置代理
controller.delegate = self
//将值传递给新页面
controller.oldInfo = label.text
}
}
func editInfo(controller:EditViewController, newInfo:String){
label.text = newInfo
//关闭编辑页面
controller.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
func editInfoDidCancer(controller:EditViewController){
//关闭编辑页面
controller.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
}
import UIKit
protocol EditViewControllerDelegate {
func editInfo(controller:EditViewController, newInfo:String)
func editInfoDidCancer(controller:EditViewController)
}
class EditViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
var delegate:EditViewControllerDelegate?
var oldInfo:String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if oldInfo != nil{
textField.text = oldInfo
}
}
#IBAction func done(sender: AnyObject) {
delegate?.editInfo(self, newInfo: textField.text!)
}
#IBAction func cancel(sender: AnyObject) {
delegate?.editInfoDidCancer(self)
}
}
Try this,
class ViewController: UIViewController,EditViewControllerDelegate {
var controller: EditViewController?
#IBOutlet weak var label: UILabel!
//页面跳转时
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "EditView" {
//通过seque的标识获得跳转目标
controller = segue.destinationViewController as! EditViewController
//设置代理
controller.delegate = self
//将值传递给新页面
controller.oldInfo = label.text
}
}
func editInfo(controller:EditViewController, newInfo:String){
label.text = newInfo
//关闭编辑页面
controller.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
func editInfoDidCancer(controller:EditViewController){
//关闭编辑页面
controller.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
}
import UIKit
protocol EditViewControllerDelegate {
func editInfo(controller:EditViewController, newInfo:String)
func editInfoDidCancer(controller:EditViewController)
}
class EditViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
var delegate:EditViewControllerDelegate?
var oldInfo:String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if oldInfo != nil{
textField.text = oldInfo
}
}
#IBAction func done(sender: AnyObject) {
delegate?.editInfo(self, newInfo: textField.text!)
}
#IBAction func cancel(sender: AnyObject) {
delegate?.editInfoDidCancer(self)
}
}
I’m not able to tell from your code how you are handling the opening of EditViewController from your ViewController.
I’m guessing that your prepareForSegue:sender: is not getting called resulting in the delegate not getting set. To fix this, you will need to add a performSegueWithIdentifier:sender: call at the point where the segue needs to happen as in
self.performSegueWithIdentifier("EditView", sender: self)
You should replace whatever code is performing the opening of EditViewController with that call.
I have an app the uses showViewController:sender: to open up a second view controller from the first one even though there is a Show segue defined in my storyboard. The segue in my storyboard doesn’t get used in that case and prepareForSegue:sender: is never called as a result.
If I replace my
showViewController(myVC, sender: self)
with
performSegueWithIdentifier("mySegue", sender: self)
then prepareForSegue:sender: will get called. If I am setting a delegate, like you are, in prepareForSegue:sender:, the delegate will get set before the segue happens.

Resources