passing values to a Modal UIViewController - Swift - ios

I have a list of user profiles in a UICollectionView and when I tap on one of the users, I have a segue that Modally shows the users profile however I can't seem to pass the values from one View to another. Eg.. Image data, Username, etc.
On my UICollectionViewController I have the following:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showSelectedUserProfile" {
let cell = sender as! GrafterCell
let SP = segue.destinationViewController as! SelectedProfileViewController
let indexPath = self.collectionView?.indexPathForCell(cell)
var username = allUsernames[indexPath!.row]
SP.username.text = username
}
}
cell is the cell from the collection view, SP if the destination view controller
When I run the script I get the following error
fatal error: unexpectedly found nil while unwrapping an Optional value
it shows that SP.username is nil and I don't know why it is nil as it's connected to the Class and called username
class SelectedProfileViewController: UIViewController {
#IBOutlet weak var btnHideSelectedProfileViewController: UIButton!
#IBOutlet weak var userProfilePicture: UIImageView!
#IBOutlet weak var username: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
self.navigationController?.navigationBarHidden = true
}
#IBAction func btnHideSelectedProfileViewControllerClicked(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}

You have an array of objects with a property username with a property text. From that I gather that you are perhaps storing an array of objects where the username property is a UILabel?
This would be very bad design. Your data array should just have data objects, not UI elements. Never use the content of UI elements to store and retrieve data (which is what you seem to be doing). UI elements should be used only to display data.
You need to give your destination controller a property of type String to hold the username information.
Also, don't call a label username which is misleading. If its a label, call it nameLabel to make the code readable and intelligible to an outside reader.

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

Passing data from view controller to another view controller

I have not found an exact solution for my problem. I need to pass data from one view controller to another view controller. The problem is, that after the segue, the passed string data does not appear in the label.
func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject) {
if (segue.identifier == "segue1") {
if let destination = segue.destination as? ResultsViewController {
destination.name = correctslabel.text!
The second controller: there is just variable and "name" and a UIlabel, which does not show the passed data.
import UIKit
class ResultsViewController: UIViewController {
#IBOutlet var namelabel: UILabel!
var name = ""
override func viewDidLoad() {
super.viewDidLoad()
name = namelabel.text!
}
}
I've tried many ways to do it, but none of them worked. Thank you
Finally worked for me to do an override func of it, which meant, that I had to change the sender from AnyObject to Any?.
Haven't you tried this?
// ResultsViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
namelabel.text = name
}

Sending data from custom UITableViewCell to detail ViewController - Swift

I'm relatively new to programming and this is my first attempt at developing an iOS without following a tutorial, so please bear with me.
I have a custom table view called 'CupboardViewController' that returns four string labels based on a custom UITableViewCell class called 'ItemTableViewCell'. I want to be able to show those four labels in a separate view controller called 'detailViewController' when a user clicks on a table item.
This is the code I have but it's crashing with no obvious error message when the segue is called. Please help!
CupboardViewController
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject!) {
// sender is the tapped `UITableViewCell`
let cell = sender as! ItemTableViewCell
let indexPath = self.tblItems.indexPathForCell(cell)
// load the selected model
let titleToPass = itemMgr.items[indexPath!.row].name
let detail = segue.destinationViewController as! detailViewController
// set the model to be viewed
detail.titleToPass = titleToPass
}
}
detailViewController
#IBOutlet weak var titleDetailLabel: UILabel!
#IBOutlet weak var qtyDetailLabel: UILabel!
#IBOutlet weak var dateDetailLabel: UILabel!
#IBOutlet weak var descriptionDetailLabel: UILabel!
var titleToPass: String!
var qtyToPass: String!
var descriptionToPass: String!
var dateToPass: String!
override func viewDidLoad() {
super.viewDidLoad()
titleDetailLabel.text = titleToPass
qtyDetailLabel.text = qtyToPass
dateDetailLabel.text = dateToPass
descriptionDetailLabel.text = descriptionToPass
}
// Do any additional setup after loading the view.
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
ItemTableViewCell
#IBOutlet weak var itemTitle: UILabel!
#IBOutlet weak var itemSubtitle: UILabel!
#IBOutlet weak var itemSubDetail: UILabel!
#IBOutlet weak var dateDetail: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
It is definitely best to avoid the force unwrap ! as much as possible, so I'd definitely recommend using guard let or if let in most cases. In addition to saving you from crashes, they also offer some great ways to see exactly where your code is failing that are otherwise tougher to track down.
For example, you have this line, which returns an optional NSIndexPath
let indexPath = self.tblItems.indexPathForCell(cell)
Rather than force unwrapping this optional, you can modify the line slightly to make it handle nil, and also add a print to tell you that this line failed, like this:
guard let indexPath = self.tblItems.indexPathForCell(cell) else {
print("No index path returned.")
return
}
If the value is not nil you can operate with the indexPath constant like you normally would. You'll probably want to continue this approach with most of these optionals, unless you're 100% sure they won't be nil in any situation.
As for your particular crash, you can correct me if I'm wrong, but I suspect it's because you have those four variables on your detailViewController that are being accessed in viewDidLoad, but you're only setting one of them in the segue. This means that in viewDidLoad you will be trying to access those (and they have the ! too), and since it won't find anything it'll crash immediately. See if fixing that, and getting rid of some of the !s helps.

Making a randomizer picking words from text field (Swift)

I'm trying to make app that takes info from the two text fields and randomly selects one of the sentences and places it in a label on another view controller. I'm a student in the Mobile Apps 1 class so I'm new to this. If you could explain it as much as possible it will be greatly appreciated. Happy new year!
My code:
class twoIdeasViewController: UIViewController {
#IBOutlet weak var twoIdeaContinueButton: UIButton!
#IBOutlet weak var twoIdea2TextField: UITextField!
#IBOutlet weak var twoIdea1TextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Enter Ideas"
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
let twoIdea1:String = twoIdea1TextField.text!
let twoIdea2:String = twoIdea2TextField.text!
return true
}
func prepareForSegue(segue: UIStoryboardSegue, Object: AnyObject?){
let twoIdeaFinal = segue.destinationViewController as! twoFinalViewController
twoIdeaFinal.twoIdea = //the variable that will contain the randomizer
}
}
Make use of arc4random_uniform() to generate a random number that controls which of the two text fields you wish to extract and send text from. Also, you seem to need to fix up your prepateForSegue method: you need to match the segue identifier with the identifier of your 2nd view controller (set in attributes inspector while selecting this other view controller in your storyboard).
#IBOutlet weak var twoIdea2TextField: UITextField!
#IBOutlet weak var twoIdea1TextField: UITextField!
// ...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
/* Get the new view controller using segue.destinationViewController.
Pass the randomly chosen text view text to the UILabel of the
new view controller. */
/* Here: you need to match with the identifier of your
VC 'twoFinalViewController' (set in attributes inspector) */
if segue.identifier == "twoFinalVC" {
let viewController = segue.destinationViewController as! ViewController
let random = arc4random_uniform(2)
viewController.twoFinalLabel.text = (random == 0) ? (twoIdea1TextField.text ?? "") : (twoIdea2TextField.text ?? "")
}
}
For a detailed description covering segue communication between two view controllers (UITableViewController and UIViewController), see the following thread
Global variable and optional binding in Swift
You can use something like that
func getRandomString() -> String
{
let randomNumber = arc4random_uniform(2) + 1
switch randomNumber
{
case 1:
return twoIdea1TextField.text!
case 2:
return twoIdea2TextField.text!
default:
return ""
}
}
I have no time, but I think that with an enum is simpler than what I did.

Passing data from several VC's to a last one in Swift

As you can appreciate in this pic we have an App with three different VC's and a Last one with some variable data depending of the options selected on the previous ones.
So, for instance, in this case the user had selected a blue color, a suited style and a L as size.
Our idea is to pass data from the first VC, second VC, and the third VC to the gaps in Last VC.
Any suggestions? It would be very appreciated.
Create a model class where you can store those properties in :
class MyChoices {
var color : String? // or you could use enums for each of them
var style : String? // that would be a better choice, but for the
var size : String? // sake of simplicity I use strings in this example
}
then you pass a variable of type MyChoices from one VC to another in your prepareForSegue method
EDIT (some more info, see answer from FactorJose)
In VC 1 add your variable
class VC1: UIViewController {
#IBOutlet weak var nextOutlet: UIButton!
#IBOutlet weak var colourLabel: UILabel!
var choice : MyChoice?
...
and then further on :
#IBAction func redButton(sender: AnyObject) {
nextOutlet.hidden = false
colourLabel.text = "Red colour selected"
choice.color = "Red"
}
for all those IBActions.
then in your prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let nextVC = segue.destinationViewController as! VC2
nextVC.choice = self.choice
}
VC2 and VC3 are very similar again
Hi all! As you can appreciate in this pic we have an App with three different VC's and a Last one with some variable data depending of the options selected on the previous ones.
So, for instance, in this case the user had selected a blue color, a suited style and a L as size.
Our idea is to pass data from the first VC, second VC, and the third VC to the gaps in Last VC.
Any suggestions guys? It would be very appreciated
Code :
class VC1: UIViewController {
#IBOutlet weak var nextOutlet: UIButton!
#IBOutlet weak var colourLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
nextOutlet.hidden = true
}
#IBAction func redButton(sender: AnyObject) {
nextOutlet.hidden = false
colourLabel.text = "Red colour selected"
}
#IBAction func blueButton(sender: AnyObject) {
nextOutlet.hidden = false
colourLabel.text = "Blue colour selected"
}
#IBAction func greenButton(sender: AnyObject) {
nextOutlet.hidden = false
colourLabel.text = "Green colour selected"
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {}
}
VC2 and VC3 are the same as VC1 (same outlets and buttons)
lastVC
#IBOutlet weak var colourLabel: UILabel!
#IBOutlet weak var styleLabel: UILabel!
#IBOutlet weak var sizeLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
And the last class to store the strings
import UIKit
class MyChoices {
var colour : String?
var style : String?
var size : String?
}
What can we do?
An alternative to Glenn's answer is to use NSUserDefaults. Think of it like a mini key-value database for PropertyLists.
You can add a new key-value to it with
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(clothingColor, forKey: "Clothing Color")
And then retrieve a previously saved key-value by doing
let defaults = NSUserDefaults.standardUserDefaults()
let clothingColor = defaults.objectForKey("Clothing Color")
Since the user defaults is shared and stored in disk, the information will persist across your different view controllers. It also respects encapsulation, which is good object orientation practice, since your size controller won't have to know anything about color or style and vice versa.

Resources