My code takes text from a textfield and segues to a separate view controller. The problem is that it can only store 1 segue entry. The new one replace the old every time the button is pressed. How on vc2 can I store every new entry to the segue.
VC
import UIKit
class ViewController: UIViewController {
#IBOutlet var txt: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func pressButton(_ sender: Any) {
if txt.text != "" {
performSegue(withIdentifier: "segue", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondController = segue.destination as! twoViewController
secondController.myString = txt.text!
}
}
twoVC
import UIKit
class twoViewController: UIViewController {
#IBOutlet var labelSmtih: UILabel!
var myString = String()
override func viewDidLoad() {
super.viewDidLoad()
labelSmtih.text = myString
}
}
If the second view controller is a UISplitViewController for example you should be able to append the text of the button to the label, by doing something in the lines of:
#IBAction func pressButton(_ sender: Any) {
if txt.text != "" {
performSegue(withIdentifier: "segue", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondController = segue.destination as! twoViewController
secondController.myString += txt.text!
}
}
If you really have a separated view controller it will probably lose the data once you go back to the first VC. In that case, I suggest you save the current text of the label on viewWillDisappear of the 2nd VC and then retrieve this on the prepareforsegue function on the first VC. More info here: Swift: How to store user preferences?
Related
I am new to Xcode and am trying to save an array from secondViewController into the View controller. I have a a series of view controllers embedded in a navigation controller so when I click 'back' on the navigation bar I want to keep the data that was collected in an array 'collectionArray' and save to 'collectionArray2' . Here is the protocol delegate method I've tried:
This is in my ViewController where I want the array saved to :
import UIKit
class ViewController: UIViewController {
var collectionArray2: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
let controller = secondViewController()
controller.delegate = self
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let first = segue.destination as! secondViewController
first.collectionArray.append(contentsOf: collectionArray2)
}
}
extension ViewController: PopupDelegate {
func popupSelectedView(array: [String]) {
collectionArray2.append(contentsOf: array)
}
}
This is my secondViewController where I want to take 'collectionArray':
import UIKit
protocol PopupDelegate: class{
func popupSelectedView(array: [String])
}
class secondViewController: UIViewController {
var exerciseButton: String!
var collectionArray: [String] = []
weak var delegate: PopupDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func Exercisess(_ sender: UIButton){
exerciseButton = sender.currentTitle
collectionArray.append(exerciseButton!)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if self.isMovingFromParent {
delegate?.popupSelectedView(array: collectionArray)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let second = segue.destination as! FinalViewController
second.cellLabelArray.append(contentsOf: collectionArray)
}
}
Thank you
your problem is that your are referring to a difference instance of secondViewController. Don't set your delegate in viewDidLoad but when you prepare the segue:
override func viewDidLoad() {
super.viewDidLoad()
//let controller = secondViewController() REMOVE THIS
//controller.delegate = self REMOVE THIS
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let first = segue.destination as! secondViewController
first.collectionArray.append(contentsOf: collectionArray2)
first.delegate = self // set the delate here
}
}
by the way, you should name all your classes starting with capital letters, so it should be SecondViewController
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
I got small containerView with UILabel on main app screen. I got UIButton on main UIViewController. I want to change text of label that belongs to containerView class by clicking button in UIViewController.
I try to make it with delegation, but for some reason i got a mistake (Unwraping optional)...
I try to make it with Protocol, bud method "addText" in ContView dont works((((((
class ViewController: UIViewController {
var delegate: DelegateProtocol?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func button(_ sender: Any) {
delegate?.addText(String2: "123")
}}
///////////////////////////////////////////////////////////////////////
protocol DelegateProtocol {
func addText(String2: String)
}
//////////////////////////////////////////////////////////////////////
class ContViewController: UIViewController, DelegateProtocol {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "con" {
let vc = segue.destination as! ViewController
vc.delegate = self
}
}
func addText(String2: String) {
label.text = String2
}
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
first make an global variable in your view controller
private var viewController: YourVC?
then give your container view segue an identifier and do follwing
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "yourviewcontroller Segue identifiew" { //your container view segue identifier
viewController = segue.destination as? SelectedImageVC
}
}
now you can use you viewController to access label in your containerview controller like
if let controller = viewController {
controller.yourlabel.text = "text you want"
}
I have a parent view (with ViewController) which has a sidebar menu and containerView(ContainerViewController) in it. at the containerView, I have 2 segue link with their own view and viewController (Screen1 with Screen1ViewController, Screen2 with Screen2ViewController).
I have buttons to launch screen1 and screen2 from ViewController but somehow the setting performSegue() function seems like not firing.
ViewController
import UIKit
class ViewController: UIViewController {
var container : ContainerViewController!
#IBOutlet var SideBar: UIView!
#IBOutlet weak var viewConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
container = self.storyboard?.instantiateViewController(withIdentifier: "ContainerViewController") as! ContainerViewController
}
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.
if segue.identifier == "tocontainer"{
self.container = segue.destination as! ContainerViewController
}
}
#IBAction func launchScreen1(_ sender: Any) {
container.performSegue(withIdentifier: "toscreen1", sender: self)
}
#IBAction func launchScreen2(_ sender: Any) {
container.performSegue(withIdentifier: "toscreen1", sender: nil)
}
}
ContainerViewController
import UIKit
class ContainerViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
func launchScreen2() {
print("launchscreen2")
self.performSegue(withIdentifier: "toscreen1", sender: self)
}
}
As you can see i even tried a function launchScreen2 for the button to call. the print message did appear in console but somehow no effect on the self.performSegue.
I believe is the way I prepare the segue is wrong, I just don't know what else to try.
I try to access a textfield content in my view profileView from my first view viewController but it doesn't work.
I read that I have to use override func prepare(for segue: UIStoryboardSegue, sender: Any?) {} so I did but it still not working and I don't understand why.
Here is my viewController code:
// This code is the code generated by xCode, only the loginField was added.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var loginField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
and here is my profileController prepare func:
var login: String!
#IBOutlet weak var myLabel: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("prepare")
if (segue.identifier == "mainMenu")
{
let svc = segue.source as! ViewController
login = svc.loginField.text
self.myLabel.text = login
print(login)
}
}
Actually, the thing i really read was "prepareForSegue" func but when I try to override it, xCode won't compile the code...
Thanks for your help.
write your prepareforSegue method in your viewController
why?
Because you want to send ViewController textfield data to profileViewController.That is write your prepareForSegue in Source. Here,
Source = ViewController
Destination = ProfileView
Note:
Use a variable in your destinationViewController(ProfileView Controller) and keep the textField data in this variable.
like this in prepareForSegue.
destinationVC.someVariable = self.SourceViewLabel.text // your sourceViewController
and in viewDidLoad of your destinationViewController(ProfileView) write this.
self.profileLabel.text = somVariable
In my first view i wrote this.
#IBAction func nexVCAction(_ sender: Any) {
self.performSegue(withIdentifier: "secondvc", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "secondvc" {
let vc = segue.destination as! secondVC
vc.stringHolder = textField.text!
}
}
And this is the second view code.
import UIKit
class secondVC: UIViewController {
#IBOutlet weak var textlabel: UILabel!
var stringHolder:String = ""
override func viewDidLoad() {
super.viewDidLoad()
textlabel.text = stringHolder
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I made git project for this.
Check only FirstVC.swift and secondVC.swift
And this is the output.
You cannot set the text of the UITextField and any other components of UIKit in the prepare for segue, because all View components of the recently allocated controller are not initialised yet they are all nil in prepareForSegue. they allocated only be when the viewController is presented.
You set Text for current ViewController TextField at prepareForSegue. Instead of this you should set text at viewWillAppear method while pop or dismiss the presented ViewController by using Notification, Delegate or userDefaults etc...
You just need a small fix since UITextField and any other components of UIKit are not initialised yet in the prepare for segue:
var login: String!
#IBOutlet weak var myLabel: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("prepare")
if (segue.identifier == "mainMenu")
{
let svc = segue.source as! ViewController
login = svc.loginField.text
// self.myLabel.text = login
print(login)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.myLabel.text = login
}
Data flow from one controller to another is the very popular theme for ios beginners. There are several approaches to achieve this goal: segues, delegate pattern, notifications, initialization of new instance of controller, saving data to local storage etc.
You should user prepare(for segue: sender:) method only when you have segue from you current controller to another. You can get access to destination controller instance via destination property of segue parameter.
Also never call IBOutlet properties of destination controller in prepareForSegue - it can be cause of crash, because Outlets may be uninitilized at this time. Instead use String property for example.
Here is sample code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "mainMenu") {
if let destinationVC = segue.destination as? ViewController {
destinationVC.login = self.myLabel.text
}
}
}
Where login in destinationVC is the String variable to store login.