unexpectedly found nil while unwrapping an Optional value (swift) - ios

I get the error in the title while trying to change the text of a label which is part of the Page class (a subclass of UIViewController)
#IBAction func StartButton(sender: AnyObject) {
for quote in quoteList {
var newPage = Page()
//error is on the next line:
newPage.Label.text = quote
pageArray!.append(newPage)
}
}
}
and here is the Page class:
class Page : UIViewController{
var index: Int = 0
var parent: PageArray?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var Label: UILabel!
#IBAction func previousButton(sender: AnyObject) {
}
#IBAction func gotoListButton(sender: AnyObject) {
}
#IBAction func nextButton(sender: AnyObject) {
}
}
I am new when it comes to swift programming, and iOs in general; I apologise if a similar question has already been asked. I searched first, but didn't find a solution that worked for me.
I suspect the problem is with the initialization of newPage, and tried doing it a few different ways, but I can't seem to get it right.
My question is what exactly am I doing wrong, and how can I fix it?
EDIT: Got it working like this (by working I mean not crashing and doing nothing):
#IBAction func StartButton(sender: AnyObject) {
var pageArray: PageArray = PageArray()
for quote in quoteList {
var newPage = Page(nibName: "Page", bundle: nil)
if newPage.Label != nil {
newPage.Label.text = quote
}
pageArray.append(newPage)
}
}
It seems certain now that newPage.Label is nil.

Well, pageArray is probably nil and with ! you are pretending that it is not.
Instantiating pageArray should solve your issue.
You can check the first answer here to learn more about question and exclamation marks in swift
EDIT:
Your problem might also come from your controller initialization, you might want to try:
let mystoryboard:UIStoryboard = UIStoryboard(name: "storyboardName", bundle: nil)
var newPage:Page = mystoryboard.instantiateViewControllerWithIdentifier("idYouAssigned") as! Page

I knew your problems. When you load a ViewController from Nib. Actually It's take a delay time until your ViewController has been loaded.
This code below help your ViewController load immediate.
#IBAction func StartButton(sender: AnyObject) {
var pageArray: PageArray = PageArray()
for quote in quoteList {
var newPage = Page(nibName: "Page", bundle: nil)
let _ = newPage.view // A trick. It's help your view load immediate
// Your Page has been loaded.
newPage.Label.text = quote
pageArray.append(newPage)
}

Related

Getting API data + Optional Problems

I'm struggling to get an optional type to a Label.text. It keeps on giving me "nil" value and won't change the text.
import UIKit
class VerifyPNViewController: UIViewController {
#IBOutlet weak var VerificationMessage: UILabel!
#IBAction func backButton(_ sender: Any) {
self.view.window!.rootViewController?.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
func didSuccess(_ response: GetLoginVerificationMessage){
let Main = response.result!
DispatchQueue.main.async {
print(Main)
self.VerificationMessage?.text = Main
print(self.VerificationMessage?.text)
}
}
}
and I will get a
234443 << which is a response.result!
nil << print(self.VerificationMessage?.text)
I have no idea why this value won't go into the "self. Verification?.text" Does anyone have ideas?
Thank you.

Changing Label text on main controller after modal closed swift macOS

I am using delegates to get a string value from my modal. When the modal closes I am trying to update Label text using that string. However, I am getting error: Unexpectedly found nil while implicitly unwrapping an Optional value: file. I am not sure how to fix this. I think it's happening because the view is not yet active.
import Cocoa
class ViewControllerA: NSViewController, SomeDelegate {
#IBOutlet weak var msgLabel: NSTextField!
var s: String = "";
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func setDetails(s: String) {
self.user = s;
print("Notified", self.s) // <-- prints: Notified hello again
msgLabel.stringValue = self.s <-- DOESN'T WORK
}
func showModal() -> Void {
msgLabel.stringValue = "hello" // <--- WORKS
let cbvc: NSViewController = {
return self.storyboard!.instantiateController(withIdentifier: "ControllerBVC")
as! NSViewController
}()
self.presentAsModalWindow(cbvc);
}
#IBAction func onBtn(_ sender: Any) {
self.showModal();
}
}
protocol SomeDelegate {
func setDetails(s: String)
}
class ViewControllerB: NSViewController {
#IBOutlet weak var textF: NSTextField!
var delegate: SomeDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
let vc = ViewControllerA()
self.delegate = vc
}
#IBAction func onBtn(_ sender: Any) {
DispatchQueue.main.async {
self.delegate?.setDetails(s: self.textF.stringValue)
self.dismiss("ControllerAVC")
}
}
}
You have a number of problems.
In ViewControllerB.viewDidLoad you are assigning a new instance of ViewControllerA to the delegate property. Don't do that. Your viewDidLoad method should look like this:
override func viewDidLoad() {
super.viewDidLoad()
}
In the showModal method ViewControllerA should assign itself as the delegate on ViewControllerB before ViewControllerB it is presented.
func showModal() -> Void {
let cbvc: NSViewController = {
let vc = self.storyboard!.instantiateController(withIdentifier: "ControllerBVC")
as! ViewControllerB
vc.delegate = self
return vc
}()
self.presentAsModalWindow(cbvc);
}
In the setDetails method just assign the string to your text field directly:
func setDetails(s: String) {
msgLabel.stringValue = s
}

Segue arrays not passing why?

I'm at my wits end. I've got basically the same thing working (but with a simpler function to generate the variables in a test project) but now my code is passing blank arrays to the next view controller.
I'm just wanting to pass 2 arrays, the values for which are generated through a function, to the next VC - I've triple checked my code and I'm sure I've done it right but obviously not! Any ideas!?
1st VC :
import UIKit
class WorkoutSetupController: UIViewController {
#IBOutlet weak var timeInputField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()}
//Create function to generate a new random workout, with a random exercise array, and random reps array
func generateNewWorkout() -> (randomExerciseArray:[String], randomRepsArray:[Int]) {
let randomKey = Int(arc4random_uniform(4) + 3)
var workoutSet = [String]()
let possibleExercises = masterExerciseArray
var repsSet = [Int]()
while workoutSet.count < (randomKey) {
let randomIndex = Int(arc4random_uniform(UInt32(possibleExercises.count)))
workoutSet.append(possibleExercises[randomIndex])
}
while repsSet.count < (randomKey) {
repsSet.append(Int(arc4random_uniform(30)))
}
//return the values and print them to make sure the function is working (it is!)
let workoutToBePassed = workoutSet
let repsToBePassed = repsSet
print (workoutToBePassed, repsToBePassed)
return (workoutToBePassed, repsToBePassed)
}
//perform a manual segue to the WorkoutController page, using the function above to generate the values to be passed
#IBAction func workoutButtonPressed(_ sender: UIButton) {
performSegue(withIdentifier: "doItNow", sender: generateNewWorkout())
}
//set the variables to be passed to the new VC, accepting the format of the returned arrays from my function for the variables
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "doItNow" {
if let destVC = segue.destination as? WorkoutController, let variablesToBePassed = sender as? (arr1:[String],arr2:[Int]) {
print (variablesToBePassed.arr1, variablesToBePassed.arr2)
destVC.selectedWorkoutExerciseArray = variablesToBePassed.arr1
destVC.selectedWorkoutRepsArray = variablesToBePassed.arr2
}
}
}
}
second VC :
import UIKit
class WorkoutController: UIViewController {
//set the variables we're passing from the first VC with the right data type
var selectedWorkoutExerciseArray = [String]()
var selectedWorkoutRepsArray = [Int]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func printButtonPressed(_ sender: UIButton) {
//print the variables out - this is currently printing blank arrays arghhghghghh!
print (selectedWorkoutExerciseArray, selectedWorkoutRepsArray)
}
}
Where am I going wrong!? I can't see it anywhere! Please help!
The parameter labels of the tuple don't match, you have to check
let variablesToBePassed = sender as? (randomExerciseArray:[String], randomRepsArray:[Int]) {
This is a good example of the benefit of force unwrapping objects which must have a value of the expected type
let destVC = segue.destination as! WorkoutController
let variablesToBePassed = sender as! (arr1:[String],arr2:[Int])
If the code crashes it reveals the design error at once.

Missing Argument for parameter #1 in call error

I'm new in this website, and I already know that it helps me a LOT in coding, so thanks to the founder of this website and to the questioners and the answerers and everyone else :D
Still, one problem I have though. I have this 'Missing Argument for Parameter #1 in call' error. Its really annoying me, I'm trying to make an app, and for how much time I put into this app, I don't want to delete it. Please.
So here is the code:
class ViewController: UIViewController {
#IBOutlet var UsernameTextField: UITextField!
#IBOutlet var PasswordTextField: UITextField!
#IBOutlet var EmailTextField: UITextField!
#IBAction func LogIn(sender: AnyObject) {
}
#IBAction func SignUp(sender: AnyObject) {
SignUp() //The error is here
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
func SignUp(){
var user = PFUser()
user.username = UsernameTextField.text
user.password = PasswordTextField.text
user.email = EmailTextField.text
}
let user = PFUser()
user.username = "Name:"
user.password = "Pass:"
user.email = "Email:"
user.signUpInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
if error == nil {
// Hooray! Let them use the app now.
} else {
// Examine the error object and inform the user.
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You've got two functions with the same name, you should rename one of them!
First function:
#IBAction func SignUp(sender: AnyObject)
Second function:
func SignUp()
The reason you get the error is because the compiler is trying to use your first function rather than the second one, so the easiest way to fix it is to change the name of one of the functions.

Protocols and Delegates in Swift

I have two View Controllers: "DiscoverViewController" and "LocationRequestModalViewController".
The first time a user opens the "DiscoverViewController", I overlay "LocationRequestModalViewController" which contains a little blurb about accessing the users location data and how it can help them.
On the "LocationRequestModalViewController" there are two buttons: "No thanks" and "Use location". I need to send the response from the user back to the "DiscoverViewController"
I have done some research and found that delegates/protocols are the best way to do it, so I followed a guide to get that working, but I'm left with 2 errors and can't figure them out.
The errors are:
On DiscoverViewController
'DiscoverViewController' is not convertible to 'LocationRequestModalViewController'
On LocationRequestModalViewController
'LocationRequestModalViewController' does not have a member name 'sendBackUserLocationDataChoice'
I've marked where the errors are happen in the following files:
DiscoverViewController.swift
class DiscoverViewController: UIViewController, UITextFieldDelegate, CLLocationManagerDelegate, LocationRequestModalViewControllerDelegate {
func showLocationRequestModal() {
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var locationRequestVC: AnyObject! = storyboard.instantiateViewControllerWithIdentifier("locationRequestVC")
self.presentingViewController?.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
self.tabBarController?.presentViewController(locationRequestVC as UIViewController, animated: true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let vc = segue.destinationViewController as LocationRequestModalViewController
vc.delegate = self //This is where error 1 happens
}
func sendBackUserLocationDataChoice(controller: LocationRequestModalViewController, useData: Bool) {
var enableData = useData
controller.navigationController?.popViewControllerAnimated(true)
}
override func viewDidLoad() {
super.viewDidLoad()
showLocationRequestModal()
}
}
LocationRequestModalViewController
protocol LocationRequestModalViewControllerDelegate {
func sendBackUserLocationDataChoice(controller:LocationRequestModalViewController,useData:Bool)
}
class LocationRequestModalViewController: UIViewController {
var delegate:LocationRequestModalViewController? = nil
#IBAction func dontUseLocationData(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func useLocationData(sender: AnyObject) {
delegate?.sendBackUserLocationDataChoice(self, useData: true) // This is where error #2 happens
}
override func viewDidLoad() {
super.viewDidLoad()
//Modal appearance stuff here...
}
}
The answer is in your question itself. Both errors tells the exact reason.
Issue 1
let vc = segue.destinationViewController as LocationRequestModalViewController
vc.delegate = self //This is where error 1 happens
The self is of type DiscoverViewController
But you declared the delegate as:
var delegate:LocationRequestModalViewController? = nil
You need to change that to:
var delegate:DiscoverViewController? = nil
Issue 2
The same reason, LocationRequestModalViewController does not confirm to the LocationRequestModalViewControllerDelegate, change the delegate declaration.
You have defined your delegate as having type LocationRequestModalViewController which does not conform to LocationRequestModalViewControllerDelegate.

Resources