Can't assign Bool member of ViewController - ios

For some reason this prepare code for a segue crashes because newGridViewController is nil. Any ideas?
override func prepare(for segue: UIStoryboardSegue,sender: Any?) {
if segue.identifier == "Grid" {
if let newGridViewController = segue.destination as? GridViewController
if savePhotoWithSlicer.isOn {
newGridViewController?.savePhotoWithSlicer = true
if (newGridViewController?.savePhotoWithSlicer)! { print("TRUE") }
}
}
}

I guess the problem is let newGridController = segue.destination as? GridViewController
My thought is your GridViewController is nil. Since you force unwrap newGridViewController in this line if (newGridViewController?.savePhotoWithSlicer)! { print("TRUE") } the code crashes.
Show us some more code to give you detailed explanations.

You don't say what the exception is, but I imagine that it is "unexpectedly found nil..." caused by the force unwrap on the second last line,
I suspect that the root cause is the conditional downcast to GridViewController failed, so newGridViewController is actually nil. Since you use a conditional unwrap everywhere except the second last line, you don't get a crash until that point.
A better structure is to use an if let... conditional downcast:
if let newGridViewController = segue.destination as? GridViewController {
newGridViewController.savePhotoWithSlicer = savePhotoWithSlicer.isOn
if newGridViewController.savePhotoWithSlicer {
print("TRUE")
}
}
This will prevent the crash, but it probably still won't print "TRUE" as I strongly suspect that segue.destination is not a GridViewController - You need to check your storyboard and make sure that you have the right custom class for your scene.
UPDATE
Since you have now clarified that the segue leads to a navigation controller that embeds the GridViewController you can use this to get the desired view controller:
override func prepare(for segue: UIStoryboardSegue,sender: Any?) {
if segue.identifier == "Grid" {
if let navController = segue.destination as? UINavigationController {
if let newGridViewController = navController.viewControllers.first as? GridViewController {
newGridViewController.savePhotoWithSlicer = savePhotoWithSlicer.isOn
if newGridViewController.savePhotoWithSlicer {
print("TRUE")
}
}
}
}
}

Objective c and Swift will happily let you "assign" a value to nil, and that's probably what you are doing. Just b/c you assign "true" to nil, doesn't mean you actually did anything ("true" just gets ignored). So when you force unwrap the nil, you still crash.

Related

Populate textfield.text after segue

I have a show-type segue. I want destinationViewController nameTextField to contain some text in it when segue is performed. I tried doing:
override func prepare (for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ToEditScreen" {
let svc = segue.destination as! EditViewController
svc.nameTextField.text = cars[selectedIndex!].name //crashes
svc.nameString = cars[selectedIndex!].name
}
}
The error I get is: Unexpectedly found nil while implicitly unwrapping an Optional value.
I know that cars[selectedIndex!].name is not nil.
If I delete the textfield line everything is fine and nameString does get its value which is equal to cars[selectedIndex!].name. I know I can do:
nameTextField.text = randomString
but it seems like an extra step. Can I populate textfield.text from original ViewController?
You can add a string variable to the destination vc and set it there or
let svc = segue.destination as! EditViewController
svc.loadViewIfNeeded() // add this line
svc.nameTextField.text = cars[selectedIndex!].name

Multiple Container Views with Delegates

I have a View controller that contains 2 Container Views. One of them has a scroll-view one of them is just a small view.
Now, I want to communicate between all 3, the way I'm doing it is by using the main ViewController as a delegate for the other 2. I originally didnt know how to set them as delegates, as there is no transition or presentation of the other ones (they're just there)
After asking here a few months back I got the following answer:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "embedSegue") {
resultsVC = (segue.destination as! GoalsVC)
resultsVC.calcDelegate = self
}
}
However, I dont know how to do this for BOTH contained views. they are both using the same segue so I cant have them both have the same ViewController.
I was thinking using storyboard IDs but how do I reference them in the prepareforsegue?
would it be something like
if (segue.identifier == "embedSegue" && storyboardidentifier = "myVC1") {
resultsVC = (segue.destination as! GoalsVC)
resultsVC.calcDelegate = self
} else if (segue.identifier == "embedSegue" && storyboardidentifier = "myVC2") {
otherVC = (segue.destination as! NewVC)
resultsVC.calcDelegate = self
}
Except I dont know the exact code to reference the storyboard
If you use ? instead of ! you can use the success of the conversion to indicate whether you have the correct viewcontroller. The code would be as follows
if segue.identifier == "embedSegue" {
if let resultsVC = segue.destination as? GoalsVC {
resultsVC.calcDelegate = self
}
if let otherVC = segue.destination as? NewVC {
resultsVC.calcDelegate = self
}
}
You could simplify this code even further by delegating the setting of the delegate. Setup a new delegate, ensure your two embedded viewcontrollers conform to it. In the code below I have assumed the delegate you have already written is called CalcDelegate.
protocol CalcDelegateDelegate {
var calcDelegate : CalcDelegate? {get set}
}
class GoalsVC : UIViewController, CalcDelegateDelegate {
var calcDelegate: CalcDelegate? = nil
}
class NewVC : UIViewController, CalcDelegateDelegate {
var calcDelegate: CalcDelegate? = nil
}
Then your prepare code is simplified to
if segue.identifier == "embedSegue" {
if var delegate = segue.destination as? CalcDelegateDelegate {
delegate.calcDelegate = self
}
}
Any new view controller that is embedded in your main controller just needs to conform to the delegate and it automatically gets the calcDelegate and can communicate with your other view controllers.

Having trouble initializing variable

I'm trying to initialize a variable in a view controller before that view controller takes over the view, and it seems no matter how I try to initialize it, it doesn't work and the software runs as if the value is the default value. Part of the problem seems to be the view that contains the variable in question is nil when I try to set the variable's value. I don't know why this is. Any help would be appreciated. Thanks! Here is my code:
override func setStartPosition()
{
if sessionNameIndex != nil
{
if let curSession = ShareData.sharedInstance.sessionDataObjectContainer[sessionNames[sessionNameIndex]]
{
initialValue = YesNo(rawValue: (curSession.currentValue))
if mySessionDisplay == nil
{
mySessionDisplay = SessionDisplayView(frame: self.view.frame)
if mySessionDisplay == nil
{
var shouldneverbehere = 0 //Always goes here!
}
else
{
mySessionDisplay.onView(index: 2, sessionName: sessionNames[sessionNameIndex])
mySessionDisplay.curScene.setStartPosition(newValue: val!)
}
}
}
}
}
This function gets called in the following code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if let destination = segue.destination as? SessionDisplayViewControllerTwo
{
if let myNames = sender as? [String]
{
destination.sessionNames = myNames
destination.sessionNameIndex = 1
destination.setStartPosition()
}
}
}
Let me know if you need more information. Once again, thanks for your consideration of this matter.
A view controller's views have not yet been loaded in prepareForSegue. As #muescha suggests in his comment, you should set properties in prepareForSegue, and then wait for viewDidLoad or viewWillAppear before you try to install them in your views. I'm not sure why your attempt to manually create your SessionDisplayView is failing, but you should not be trying to create views in prepareForSegue in any case, so the solution is "Don't do that."

Value of type 'UIViewController' has no member 'jsonfile' named JSON

I currently have a button set to go to a TableViewController but decided I wanted to embed that TableViewController in a TabBarController. I am running into an error while trying to pass it to the UITabBarController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showListSegue") {
let tabBarController = segue.destinationViewController as! UITabBarController
tabBarController.selectedIndex = 0 // choose which tab is selected
let des = tabBarController.selectedViewController!
des.jsonfile = self.jsonfile
}
}
In the last line of code, des.jsonfile = self.jsonfile, I am getting the error...
Value of type 'UIViewController' has no member 'jsonfile'
I am trying to pass the jsonfile to the TableViewController which is now embedded in the UITabBarController. How can this be done? I have this variable in the TableViewController is was getting passed to but now that I threw this TabBarController in the mix I am getting all confused.
I also tried to create a Cocoa file for the TabBarcontroller and set the variable var jsonfile : JSON! but that did not work either. (That is the variable in my TableViewController that I want to pass it to) Please help. Thank you.
You need to let the compiler know that selectedViewController is a type with the member jsonFile. Also, you should check that it actually is existing and of the correct class at runtime. Here's the kind of pattern you should be using:
class JSONDisplayController: UIViewController {
var jsonfile: String
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showListSegue") {
guard let tabBarController = segue.destinationViewController as? UITabBarController else {
preconditionFailure("Unexpected destination.")
}
tabBarController.selectedIndex = 0 // choose which tab is selected
guard let des = tabBarController.selectedViewController as? JSONDisplayController else {
preconditionFailure("Unexpected selection.")
}
des.jsonfile = jsonfile
}
}

Populating prepareForSegue's destinationViewController via returned type

Good afternoon!
I'm relatively new to Swift, though I think I've managed to wrap my head around most of it, however I'm having difficulties setting a segue's destinationViewController indirectly.
I understand that destinationViewController accepts AnyObject? but how do I go about returning the class as a function's return value directly to destinationViewController? Something like:
override func prepareForSegue( segue: UIStoryboardSegue, sender: AnyObject? ) {
if( segue.identifier == "nextView" ) {
let nextScene = segue.destinationViewController as? getNextView()
// ...blah blah blah...
}
}
Where getNextView() is overridden by a subclass whose sole purpose is to return a reference to the destinationViewController:
override func getNextView -> AnyObject! {
return SomeClassBasedOnUIViewController
}
XCode isn't happy I'm employing "consecutive statements" on one line in my prepareForSegue() and I'm at a loss how to resolve it so any help would be greatly appreciated!
Thanks!
EDIT:
Sorry about that, but getNextView() doesn't actually return an Optional. Just picked up on that and amended the question. The issue persists regardless, though.
What you're trying to do is illogical. This is a compile time indication of the class or protocol to which the reference must conform. Trying to get this reference at runtime won't help you. Instead you should be checking for conformance to a static protocol and then dispatching calls based on that protocol to an unknown implementation class.
override func prepareForSegue(segue: UIStoryboardSegue, sender:AnyObject!) {
if segue.identifier == "nextView" {
let nextScene : BaseClassBasedOnUIViewController = segue.destinationViewController as! getNextView()
}
}
destinationViewController shouldn't be optional. Can you try this one.
override func getNextView -> AnyObject! {
return SomeClassBasedOnUIViewController
}

Resources