Prepare For Segue With Array - Xcode 8.0 Swift 3.0
I am pretty new to iOS/Swift but I recently ran into the same situation. Here is how I do it.
SourceViewController.swift
class SourceViewController: UIViewController {
let stringToPass = "Hello World"
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.destination as! DestinationViewController
destinationVC.receivedString = stringToPass
}
}
DestinationViewController.swift
class DestinationViewController: UIViewController {
var receivedString: String?
if let newString = receivedString {
print(newString)
}
...
I realize this is slightly different than your example, but the important thing to note is that when you create "destinationVC" you are then able to modify the properties of it. The key difference is you have to provide the scope of the variable (destinationVC.receivedString) when assigning a value or in your case appending to an array:
destViewController.items.append(textField.text!)
Without providing the scope Xcode is unable to find the variable (identifier) you are trying to modify since it wasn't part of the current file or part of an import.
Initializer for conditional binding must have Optional type, not '[String]' - Xcode 8.0 Swift 3.0
I would personally do what you want to do a little bit differently. In VC1 I would add this code:
var username = [String]()
In VC2:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toSecondViewController" {
let hello = segue.destination as! ViewController
hello.username.append(textField.text!)
}
}
Permanent Data and Prepare For Segue
Ok so because you said you want to persist the data over app starts you will need the User Defaults to store your items. As Dan already suggested you are basically doing it right. You just want to set a variable that was not declared before. But I will show you this in the following code. I will also attach a second approach in which the items are passed to next view controller while performing the segue.
First example: Imagine we have two view controllers like in your example. The first view controller contains a UITextField to do user text input. Whenever we switch from the first view controller to the second view controller with the help of a storyboard segue (e.g. when pressing a button) we take the existing texts from previous segues from the User Defaults and add the current user input and then persist it back to the User Defaults. This happens in the first view controller:
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if segue.identifier == "toSecondViewController" {
let itemsFromDefaults = UserDefaults.standard.object(forKey: "items")
var items: [String]
if let tempItems = itemsFromDefaults as? [String]
{
items = tempItems
items.append(textField.text!)
}
else
{
items = [textField.text!]
}
print(items)
UserDefaults.standard.set(items, forKey: "items")
}
}
}
This first view controller looks pretty similar to your code but I wanted to add it for completeness.
Then in the second view controller we just grab the items from the user defaults and store it directly in a instance variable of this view controller. With the help of this we can do what we want in other methods within the view controller and process the items further. As I said what you were missing was the instance variable declaration to store the items in.
class ViewController2: UIViewController {
private var items: [String]? // This is only accessible privately
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.items = UserDefaults.standard.object(forKey: "items") as? [String]
}
}
Second Example: You could also declare a internal/public variable in ViewController2 so that you can set it directly from the first view controller in perform segue. Than you wouldn´t need to grab the items from the User Defaults in ViewController2. For that you can access the destination view controller of the segue then cast it to ViewController2 and directly set the items of it.
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if segue.identifier == "toSecondViewController" {
let itemsFromDefaults = UserDefaults.standard.object(forKey: "items")
var items: [String]
// [...] Do the stuff to get the items and add current input like before [...]
let destinationViewController = segue.destination as! ViewController2
destinationViewController.items = items
}
}
}
class ViewController2: UIViewController {
var items: [String]? // This is accessible from outside now
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print(items) // We can now print this because it is set in prepareForSegue
}
}
I really hope I could help you with that and explained it understandable. Feel free to leave a comment if you have any questions.
Pass text to TextView via Segue
setting text on UITextField
of DestinationViewController
is not possible in the prepareForSegue:sender
, because all view components of the recently allocated controller are not initialized before the view is loaded (at this time, they are all nil), they only will be when the DestinationViewController
view is loaded.
You need to use optional variable infoString
in DetailsViewController
which you can set in prepareForSegue
method
if (segue.identifier == "HomeToDetails") {
let nav = segue.destination as! UINavigationController
let nextVC = nav.topViewController as! DetailsViewController
nextVC.infoString = "TESTING"
}
in DetailsViewController.swift
class DetailViewController: UIViewController {
var infoString: String?
override func viewDidLoad() {
super.viewDidLoad()
if let info = infoString {
self.infoTextView.text = info
}
}
}
Pass text to TextView via Segue
setting text on UITextField
of DestinationViewController
is not possible in the prepareForSegue:sender
, because all view components of the recently allocated controller are not initialized before the view is loaded (at this time, they are all nil), they only will be when the DestinationViewController
view is loaded.
You need to use optional variable infoString
in DetailsViewController
which you can set in prepareForSegue
method
if (segue.identifier == "HomeToDetails") {
let nav = segue.destination as! UINavigationController
let nextVC = nav.topViewController as! DetailsViewController
nextVC.infoString = "TESTING"
}
in DetailsViewController.swift
class DetailViewController: UIViewController {
var infoString: String?
override func viewDidLoad() {
super.viewDidLoad()
if let info = infoString {
self.infoTextView.text = info
}
}
}
Cannot convert value of type 'UIBarButtonItem!' to expected argument type '_OptionalNilComparisonType'
Have you tried casting your sender (Any?) as a UIBarButtonItem?
if saveButton == (sender as? UIBarButtonItem)
Related Topics
How to Properly Test Against Certain Values in Nseventmodifierflags via Swift
How to Get All Characters of the Font with Ctfontcopycharacterset() in Swift
Contextual Member Has No Associated Value in Swift 3
Value of Optional Type 'Nsurl' Not Unwrapped; Did You Mean to Use '!' or ''
How to Create a Dynamic Link in Firebase Programmatically? Swift
Cannot Use Unarchivefromfile to Set Gamescene in Spritekit
How to Stop Tokbox Screen Sharing in Swift
Swift How to Design Uiwebview Auto Resize Full Screen in Story Board
Uicollectionviewcell Not Forcing Cell Size
Testing If Double from Textfield Has a Value or Not
Macos Playground: How Can Change the Background Color
Swift 3 - How to Insert Tableviewcells from Another View
@Published Property Not Triggering Anything
Getting a String Value from Nsorderedset Using Swiftui Foreach