IBOutlet References found nil when containing view controller is presented programmatically [duplicate] - ios

This question already has answers here:
Instantiate and Present a viewController in Swift
(18 answers)
Closed 2 years ago.
I am building a running app and am trying to programmatically present a view controller when the start run button is tapped. However, when I tap the button after setting up the presenting view controller, any reference to an IBOutlet is found nil when unwrapping in the order that they are called.
It's worth noting that when I connect the two view controllers with "show" via storyboard instead, that everything works fine, but I want to present the view controller programmatically and in fullscreen rather than the cardlike presentation by default.
From the presenting view controller:
#IBAction func startRunTapped(_ sender: Any) {
let inRunVC = CurrentRunVC()
inRunVC.modalPresentationStyle = .fullScreen
self.present(inRunVC, animated: true, completion: nil)
}
From the presented view controller:
import UIKit
import MapKit
class CurrentRunVC: LocationVC {
#IBOutlet weak var sliderImageView: UIImageView!
#IBOutlet weak var swipeBGImageView: UIImageView!
#IBOutlet weak var durationLabel: UILabel!
#IBOutlet weak var paceLabel: UILabel!
#IBOutlet weak var distanceLabel: UILabel!
#IBOutlet weak var pauseButton: UIButton!
var startLocation: CLLocation!
var lastLocation: CLLocation!
var timer = Timer()
var runDistance = 0.0
var pace = 0
var counter = 0
override func viewDidLoad() {
super.viewDidLoad()
let swipeGesture = UIPanGestureRecognizer(target: self, action: #selector(endRunSwiped(sender:)))
sliderImageView.addGestureRecognizer(swipeGesture) //error unwrapping here
sliderImageView.isUserInteractionEnabled = true // and here
swipeGesture.delegate = self as? UIGestureRecognizerDelegate
}
//errors also on any other reference to an IBOutlet
I've confirmed that all IBOutlets are connected properly.
Storyboard:
Thanks in advance

You are not grabbing the correct storyboard instance of CurrentVC but you are creating a new one. Instead of let inRunVC = CurrentRunVC(), use
let runVC = self.storyboard?.instantiateViewController(withIdentifier: "CurrentRunVC") as! CurrentRunVC //set the identifier in the storyboard identity inspector

Your view controller's outlets are defined in the storyboard, and created when the view controller is loaded from the storyboard. When you just initialise a new view controller in this line:
let inRunVC = CurrentRunVC()
Then the system will look for a xib file with the same name as the view controller, which doesn't exist, which means it just loads a blank view with no connected outlets. All those implicitly unwrapped optionals are now nil.
Either make a new segue from the button to the same view controller, but a modal presentation, or add a reference to the presented view controller in the storyboard and then load it using the instatiate with identifier method of the current storyboard.

Related

Pass data to only static storyboard embedded tableview controller on a UINavigation

So all my controller's are done programmatically to avoid segues and that sort of complicated stuff.
I have a viewcontroller (Call it ProfileViewController) that downloads data from the network.
So I have a method in ProfileViewController that instantiates a single storyboard file with a static tableview with cells that have textfields in them. Here is the method:
ProfileViewController:
func userSelectedUpdateProfile() {
// Obtain reference to the only storyboard file named EditProfileSB
let storyboard = UIStoryboard(name: "EditProfileSB", bundle: nil)
// Since the Tableview is embedded in a navigation controller (with ID set to "navigationID")
if let parentNavigationController = storyboard.instantiateViewController(withIdentifier: "navigationID") as? UINavigationController {
// Now find the embedded TableViewController and access it's properties to pass to.
if let childEditController = parentNavigationController.topViewController as? EditProfileTableViewController {
// ! Error here ! Found nil when doing this.
childEditController.nameTextfield.text = "Passed this to static cell"
}
present(parentNavigationController, animated: true, completion: nil)
}
}
So the code itself is self-explanatory to what I am trying to achieve here. The TableView is embedded in a Navigation (done on storyboard with "Editor > Embed In") so on the 2nd nested if let statement I am now checking to find that Edit controller and access its properties (nameTextfield).
I get a crash when I attempt to access the nameTextField.text property. This textfield is set using storyboard. Here is that EditProfileTableViewController:
class EditProfileTableViewController: UITableViewController {
#IBOutlet weak var nameTextfield: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
// Other methods ...
}
Here is the storyboard flow layout.
Am I missing something here? I keep getting a crash on childEditController.nameTextfield.text = "Passed this to static cell" on the method userSelectedUpdateProfile().
If your View Controller still not call viewDidLoad().
your textfield is not create.
#IBOutlet weak var nameTextfield: UITextField!
you can see it's attribute is weak here.
Try create a value and pass text to the value. Then in viewDidLoad(), you can set the value to your textField

Calling methods in child view controller in iOS

I have an app written in Swift for iOS 13 where I present a view modally using storyboards. Once the new view is being presented, I want the parent to call a method which is located inside the child view controller (of my custom class which inherits from UIViewController).
To do this, I plan to have a method inside my parent view controller that gets the modal view controller being presented as its child. Once I get this reference, I will call the child's function from my parent view controller.
I realise this is probably a bad design decision, but I haven't found a way to avoid this approach. I have looked all over stackoverflow to find an answer, but I haven't found any yet. Any help would be much appreciated.
You can instantiate the child view controller and set its properties before presenting it. Then the code that changes the child view controller based on the data is put in the viewDidLoad() method.
class ParentViewController: UIViewController {
func goToChildViewController(object: CustomObject) {
guard let childViewController = self.storyboard?.instantiateViewController(withIdentifier: "child") as? ChildViewController else { fatalError("Cannot instantiate child view controller!") }
childViewController.myProperty = true
childViewController.myObject = myObject // Example of how to pass data from a data model
self.present(childViewController, animated: true)
}
}
class ChildViewController: UIViewController {
var myProperty = false
var myObject: CustomObject? = nil
override viewDidLoad() {
if myProperty {
// Conditional code here
}
{
}
Alternatively, you could trigger a segue in code instead of presenting the child view controller directly.
In this case, you would set up the child view controller inside the parent view controller’s overridden prepare(for:sender:) method, where the child view controller can be accessed using segue.destinationViewController.
Through Segue
When segue triggered maybe through a button press or a table view selection prepare(for:) method will be called on your view controller, at this point you can configure your DestinationViewController by setting some properties.
RootViewController.Swift
#IBOutlet weak var textFieldFirstName: UITextField!
#IBOutlet weak var labelFullname: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let firstVC = segue.destination as? FirstViewController else { return }
firstVC.firstname = textFieldFirstName.text
}
After typing the firstname and tap enter button , firstname value is passed to firstViewController and assigned to related UILabel in viewDidLoad() Method.
FirstViewController.Swift
#IBOutlet weak var textFieldLastName: UITextField!
#IBOutlet weak var labelFirstName: UILabel!
var firstname: String?
override func viewDidLoad() {
super.viewDidLoad()
labelFirstName.text = “Firstname: \(firstname ?? “”)”
}
You can achieve same thing through closure and Delegates

Accessing image from prepare segue error [duplicate]

This question already has answers here:
Passing Image to another View Controller (Swift)
(4 answers)
Closed 5 years ago.
Normally, we link the image from storyboard to viewcontroller by IBOutlet by this code:
#IBOutlet private weak var resultImage: UIImageView!
we can update the image by calling this: resultImage.image = UIImage(named: "image"). It works ok
However, when i call this in the prepare for segue, it found nil:
let destinationDetail = segue.destination as! RestaurantViewController
destinationDetail.restaurantImage.image = UIImage(named: "image")
restaurantImage is an Outlet in RestaurantViewController: #IBOutlet weak var restaurantImage: UIImageView!
I dont understand why it found nil, please help!
Because view controlle haven't been set up yet. you can use IBOutlet after view is load.
In viewDidLoad or viewDidAppear of your destination viewController you will do something like this:
override func viewDidLoad() {
super.viewDidLoad()
resultImage.image = newImage
}

How to pass an image between view controllers without using segue

I'm getting an error :
Thread 1:EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP,subcode=0x0)
while trying to pass the image that is selected from Photo library, to the other view controller.
Source ViewController -
#IBAction func myButtonTapped(_ sender: UIButton)
{
let secondVC = storyboard?.instantiateViewController(withIdentifier: "secondVC") as! secondVC
secondVC.passedVName = VNameTextField.text!
secondVC.passedCcName = DropTextBox.text!
secondVC.passedVImage = VImageView.image!
navigationController?.pushViewController(secondVC, animated: true)
}
Destination ViewController -
class secondVC: UIViewController
{
#IBOutlet weak var DisplayPassedImage: UIImageView!
#IBOutlet weak var ccNameDisplayLabel: UILabel!
#IBOutlet weak var VNameDisplayLabel: UILabel!
var passedVName = ""
var passedCcName = ""
var passedVImage = UIImage()
override func viewDidLoad()
{
super.viewDidLoad()
VNameDisplayLabel.text = passedVName
ccNameDisplayLabel.text = passedCcName
DisplayPassedImage.image = passedVImage // Error- Thread 1:EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP,subcode=0x0)
}
}
Note: 'PassedVName' and 'passedCcName' data are successfully passed to destination ViewController. if I try to pass 'passedVImage',I get an error.
You can use NotificationCenter to pass action/message to any viewController. Here you need to take UIImage object in AppDelegate (Means Globally) so you can pass it any where.
Ex.
1) You are in firstVC
2) In the firstVC choose the Image and add this image in the image object that you took in AppDelegate
3) Post your Notification in firstVC that you want to pass action
4) Add Observe on your SecondVC
5) You can get image from AppDelegate object (Global object).
Otherwise you can use Protocol.
In my limited experience with you should change .jpg to .png image

Init viewcontroller in app delegate, including tableview delegates

I have a view controller class in my app:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var usersTable: UITableView!
var pendingRequest: FBRequest?
var pendingLoginForSlot: Int!
var userID: String?
var swipeGesture: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
usersTable.delegate = self
usersTable.dataSource = self
self.pendingLoginForSlot = -1
self.usersTable.tableFooterView = UIView(frame: CGRectZero)
}
I want this view controller to be initialized and completely functional in my app delegate after didFinishLaunchingWithOptions When I define this view controller in the app delegate like so:
var VC: ViewController = ViewController()
The VC.usersTable is nil. But when I do my function after viewDidLoad everything is working fine.
So how can I fire functions from within app delegate (e.g. logging in users that are defined in the tableview) and thus have the properties available that get defined in viewDidLoad and expect the same results after I load the VC in a regular manner by loading it via a tab barcontroller?
Thanks!

Resources