I do not understand what this code does(the code is about segues) - ios

I am watching an iOS course video and the person in the course types up this code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let nextVC = segue.destination as! CreateTasksViewController
nextVC.tasksVC = self
}
The CreateTasksViewController is the view in the project that we are supposed to segue to. Also the "tasksVC" is the current view controller that in the app we are supposed to be on.I do not understand what this code mean and it would be helpful if someone could explain exactly what the function of the code is. Also what is "as!"?If you need any more details regarding my problem, feel free to ask in the comments.

That's not the best segue code. Here's how I'd code it (for the record, there are several ways to code this):
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "[segue name here]" {
if let nextVC = segue.destination as? CreateTasksViewController {
nextVC.tasksVC = self
}
}
}
The code you posted has what is called a "forced unwrap" of the segue destination (that's the as! you asked about). Here's a question with a pretty good answer explaining the dangers of forced unwrapping. Scroll down to the divider in the answer.
While I'm sure the code compiles (and likely runs), the problem is code maintenance.
Suppose you have several segues defined between several scenes, each with their own view controller? My code, as matter of style, gives up a bit of "Swiftiness" for "explicitness":
(1) A check is made that the segue is the correct one
(2) An if let... as? is made to safely check if the segue destination if the correct view controller
(3) If everything passes the checks, then the code is executed. In this case, it looks like it passes the entire(!) view controller into a variable called tasksVC in the CreateTasksViewController.
I'm actually cautious about item 3 - why would anyone pass one view controller into another one as a variable? Most cases in prepare(for segue:) one or more variables inside the sending VC are passed to the destination. If I may offer this to you... find another tutorial!

Related

How to check if segues are linked properly in code and storyboard? Exclamation point in "as!" is also not highlighted?

I am creating a simple quiz app in xcode that has an intro, question, and results view controller. When I try to link my "QuestionViewController" to my "ResultsViewController" with the prepare for segue method in code, the exclamation point in "as!" keyword is not highlighted in pink.. It gives me a "Thread 1: signal SIGABRT". Yes I have linked it in the storyboard previously.
I've tried checking the identifiers of the segues in the storyboard and they look fine. Here is the code I was talking about:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ResultsSegue" {
let resultsViewController = segue.destination as! ResultsViewController
resultsViewController.responses = answersChosen
}
}
It is supposed to pass the data of answersChosen to my resultsViewController, but I am getting a "Thread 1: signal SIGABRT" error. And like I said, the "!" after "as" is not highlighted in pink like "as" is. In the iBook the "!" is pink.. that indicated to me something is not connected right.
Also in the console:
Could not cast value of type 'UINavigationController'
(0x112186760) to 'CityQuiz.ResultsViewController' (0x10a61e698)
2019-07-01 14:45:35.043824-0400 CityQuiz[29761:429223] Could not
cast value of type 'UINavigationController' (0x112186760) to
'CityQuiz.ResultsViewController' (0x10a61e698)
Isn't it supposed to say cast value of type "QuestionsViewController" instead of "UINavigationController" as well? How else do I check if things are linked properly besides actually clicking on the segues in the storyboard?
Edit #1: Turns out I had an extra navigation controller, that I somehow didn’t notice. I’m new to this, thanks for the responses. I am now getting some “NSUncaughtKeyException” but I found some other posts about those errors.
why don't you present view controller programmatically ? It's much more simple than this method. I assume the problem you have in your case is that the segue "ResultsSegue" is attached to the embedded navigation controller. And you probably should not force unwrapped view controller like that. To check optional is a better way to handle presentation like below.
if let resultsViewController = segue.destination as? ResultsViewController {
resultsViewController.responses = answersChosen }

Add data in segue won't recognize SecondViewController class when running

So here is my first view controller class:
import UIKit
class AboutUsTableViewController: UITableViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let volunteerPageVC = segue.destination as! DedicatedVolunteerViewController
// Idk why I have to use 'as!' instead of 'as'. Xcode made me do it
volunteerPageVC.person = "John Smith"
}
}
Here is my second view controller class:
import IUKit
class DedicatedVolunteerViewController: UIViewController {
var person: String?
#IBOutlet weak var HeaderTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
HeaderTitle.text = "About " + person!;
}
}
On my Main Storyboard, each of the cells on the AboutUsTableView segue to the DedicatedVolunteerViewController. I want to ability to have one view controller that can be used multiple times (i.e. people's profiles).
When I click on any of the cells in the table view, I get these errors:
2016-12-26 11:44:27.075 MyApp iOS[8350:493673] Unknown class _TtC20MyApp_iOS31DedicateVolunteerViewController in Interface Builder file.
2016-12-26 11:44:29.698 MyApp iOS[8350:493673] Unknown class _TtC20MyApp_iOS31DedicateVolunteerViewController in Interface Builder file.
I'm confused since when I compile the app, it gives no error saying about "Unknown class" or an error saying I need to add/import DedicatedVolunteerViewController into AboutUsTableViewController to be able to use it. I even tried to import it, but Xcode wouldn't let me and kept giving me errors.
P.S. I've a lot of trouble considering most places online give documentation on older versions of swift or obj-c which make it hard to find out how to use new versions of code.
It sounds like the class name is wrong in your storyboard scenes. Go follow the segue(s) from your table view controller to your second view controller. Then select the scene for the destination view controller, select the view controller itself, and select the "identify inspector." Check the class of the destination view controller. From the error you're getting, it sounds like it's class is _TtC20MyApp_iOS31DedicateVolunteerViewController instead of DedicateVolunteerViewController.
Swift uses 'optionals' which essentially means a nullable object. Optionals can have a valid value or nil. It is recommended that you use the 'guard' statement to handle optionals.
So, your prepareForSegue should look like:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let volunteerPageVC = segue.destination as? DedicatedVolunteerViewController else {
//Handle the nil value, log, etc
return
}
volunteerPageVC.person = "John Smith"
}
The ! is explicitly unwrapping the optional - meaning it MUST have a value or the app will crash. You should avoid using them where possible and use the ? as I show above instead with a guard statement. In the code above, it attempts to cast the segue.destination to a DedicatedVolunteerViewController but if it can't, it will fall into the guard statement (meaning the result was nil).
There's a lot of good information about optionals on Apple's site. Check out: Swift Basics
As for your specific error, it looks like a simple mis-spelling. The error is referring to 'DedicateVolunteerViewController' (missing a 'd') and your class name is 'DedicatedVolunteerViewController'

UIViewController call function in container view viewcontroller

Using Swift 3. This question has been around and there are quite a few examples. Unfortunately none of them have worked for me. I have a uiviewcontroller that has a container view. This container view has a segue to another view controller. Everything loads up correctly and displays correctly the first time. The problem that I am facing is that I have a button on the main uiviewcontroller which by tapping it, it will refresh the container view. This is where the problem is. The outlets in the child controller are nil.
This is how I am trying to all the function in the child controller.
let wcvc = self.storyboard!.instantiateViewController(withIdentifier: "WeatherCurrentViewController") as! WeatherCurrentViewController
wcvc.callJson()
In the child controller the label is like this
#IBOutlet var currentweather: UILabel!
But in the function that is called I get an error of unexpectedly found nil while unwrapping an Optional value when I try to do
print(currentweather)
Anyone have an idea of what I can do to call a function in a view controller that is loaded by segue in a container view and still have the outlets be valid? Please let me know if more info is needed.
Found the solution. I needed to use segues identifiers and not the storyboard. Doing this
private var currentEmbeddedViewController: WeatherCurrentViewController!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let cvc = segue.destination as? WeatherCurrentViewController, segue.identifier == "CurrentWeather" {
self.currentEmbeddedViewController = cvc
}
}
And calling the function in the controller like so
self.currentEmbeddedViewController.callJson()
Was the solution. This question offered the solution. Sorry to waste everyones time. Hopefully, this will help someone else.
The reason why you get an exception is that your currentweather label will get loaded from the storyboard only when the view of that view controller loads. This means, that when you try to call callJson() the view is not yet loaded and thus no IBOutlets are loaded yet as well, meaning they are nil.
I would suggest passing some data to theWeatherCurrentViewController and only on viewDidLoad updating the views. viewDidLoad guarantees that view and all the outlets are loaded and are not nil

Swift segue passing nil string

Well, in my app i have to set the value of another view var, but it seems that the val i assign this var is null when it passes through the segue, the
code:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!)
{
if segue.identifier == "DetailSegue"
{
let detailViewController = ((segue.destinationViewController) as RowViewController)
let indexPath = self.table!.indexPathForSelectedRow()!
detailViewController.string = name.text
}
}
and in the second view i have something like this:
var string: String?
override func viewDidLoad() {
super.viewDidLoad()
descrip.text = string
...
Before you ask, name does have a value, but the segue seems to pass it nil :/ i really hope you guys could help me out on this one!
Basic debugging for a problem like this:
Step 1:
Check if the storyboard segue has the same identifier as the one you put in your code. Remember, lower case and upper case letters do matter.
Step 2:
Use plenty of println()s. This is one of the most useful, simple debugging tools in Xcode. You can print the values of your beginning label, your string on the next view controller, etc. Before you ask questions on Stack Overflow, try to figure it out for yourself -- that will make you a better programmer, and make you not have to rely on online communities as much.
Step 3:
Test out some other, simpler tasks with your problem to see if it is really a bug in Xcode or a mistake. Example, try to assign the value you are changing on the next view controller to a static piece of text instead of a label's text.
Hope this helped, and that you've fixed your problem.

Connect performSegue w/ prepareForSegue in Swift

Sorry for the vague title, I've never been really good with words but this is the general outline of what I want to do.
I have two view controllers,
-One called settings
-One called setDate
In the settings view controller I have two buttons, one called 'summary time', the other called 'daily time'
Each are linked to different IBActions that segue to the setDate view controller using performSegue.
My question is how could I perform an if statement in the setDate view controller by comparing the sender that performed the segue?
Sorry if I don't make much sense but could anyone help me?
You normally configure the 'setDate' view controller in the 'prepareForSegue' method of the 'settings' view controller. Something like this:
class SettingsViewController : ... {
// ...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segueSummaryTime" {
let dest = segue.destinationViewController as SetDateViewController
// configure dest for 'summary time'
// for example:
dest.isForSummary = true
// ...
}
else if segue.identifier == "segueDetailTime" {
// as above but for 'detail time'
}
}
// ..
}
For the above, you should have linked each button to a different segue, each with an identifier as I've illustrated above.

Resources