I've created a new window controller that hosts a tabViewController inside my app. I've added classes to the window controller, and have the same class across all the view controllers in the tab view.
I can connect buttons and give them an action and it works perfectly, however, I try to connect an outlet and attempt to change something via the outlet, it returns nil and crashes the program. For example, this exact code works in the pre-made viewController of my app, but returns:
Unexpectedly found nil while implicitly unwrapping an Optional value
when running it through the new tab view controller I created.
What's weird to me is I can use a regular view controller and the outlets connect fine, but if I want to use a tab view controller, these nil errors are happening.
I made sure that the nil was not related to grabbing the inputs for the button by printing the audio devices, and the audio devices are there and able to be printed. It seems as if the button is not there even though it is connected.
I have also tried to simply change an NSTextField color after connecting it to an outlet and this returns the same nil error.
Any idea what I might be doing wrong here? Thanks so much for the help.
class FirstLaunchViewController: NSViewController {
var FLWindow: FirstLaunchWindowController?
var selectedAudioDevice = [String]()
#IBOutlet weak var deviceListPopUpButton: NSPopUpButton!
#IBOutlet weak var visualizeButton: NSButton!
override func viewDidLoad() {
super.viewDidLoad()
self.preferredContentSize = NSMakeSize(self.view.frame.size.width, self.view.frame.size.height)
populateDeviceList()
}
#IBAction func CloseStartupGuide(_ sender: Any) {
self.view.window?.windowController?.close()
}
#IBAction func popUpDidChange(_ sender: Any) {
print("changed")
}
//grab inputs for button
fileprivate func populateDeviceList() {
deviceListPopUpButton.removeAllItems()
for device in AudioDevice.allInputDevices() {
var teststring = ""
teststring = device.uid!
print(device.name)
deviceListPopUpButton.addItem(withTitle: device.name)
deviceListPopUpButton.lastItem?.tag = Int(device.id)
selectedAudioDevice.append(device.uid!)
}
}
}
}
Related
I have a game where I store the value of the high score of a player as "highScore", in the first view controllers.
What I want to do is to try to transfer this variable data, to the second view controller.
I entered in the following code in the first view controller's storyboard.
Btw, highscore2, is the variable that I delcared in the second view controller, to store the data from the first highscore variable.:
override func prepareForSegue(segue: UIStoryboardSegue!, sender:AnyObject!)
{
if (segue.identifier == "segueTest")
{
var svc = segue!.destinationViewController as! viewTwo;
svc.highScore2 = "High Score \(highScore)"
}
}
Now here is the code in my second view controller (viewTwo):
class viewTwo: UIViewController
{
#IBOutlet var highScoretwolbl: UILabel!
var highScore2:String!
override func viewDidLoad() {
highScoretwolbl.text = highScore2
}
}
For some reason, the code compiles, but the high score is not displayed, and is "nil".
Avoid forced unwrapping.
if let svc = segue.destinationViewController as? viewTwo{
svc.highScore2 = "High Score \(highScore)"
}
Then put in a breakpoint and make sure you got to all of these lines.
As stated in other answers, viewDidLoad only gets fired once. Since you
are using a forced unwrapped string, it is initially nil. It (highScore2) won't get a value until you set it which happens after viewDidLoad. Add some print statements so you can see the sequence of events.
Your best option is to use viewWillAppear. This will get fired every time
your view appears. This is what you want.
You don't need to use a force unwrapped variable. Just make a string a
make it empty initially.
override func prepareForSegue(segue: UIStoryboardSegue, sender:AnyObject?)
{
if segue.identifier == "segueTest"
{
if let svc = segue.destinationViewController as? viewTwo {
print("setting score in prepareForSegue")
svc.highScore2 = "High Score \(highScore)"
}
else {
print("something bad happened")
}
}
}
class viewTwo: UIViewController
{
#IBOutlet var highScoretwolbl: UILabel!
// The following uses forced upwrapping which means it could be nil
// if you never set it and your app will crash.
// var highScore2: String!
// Instead, do this:
var highScore2: String = ""
override func viewWillAppear(animated: Bool) {
// putting this here will ensure that every time the screen
// is shown, the label will get set from the string.
highScoretwolbl.text = highScore2
}
}
If I recall correctly, the viewDidLoad method is called only once when the view controller is first initialized. So you are setting the highScoretwolbl variable before highScore2 actually has any data, this is why it is nil.
I would recommend setting the text inside viewWillAppear so it updates every time the segue happens.
So something like
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
highScoretwolbl.text = highScore2
}
Here is a reference to the lifecycle of all the methods of a view controller. View controller lifecycle
Full code for this branch here
View controller "MovieDetailsVC" is presented to the navigation controller when a cell is selected.
The presenting view controller, "ViewController", stores the row of the tableView to display in NSUserDefaults as an Int.
"MovieDetailsVC" reads the row ok. It then pulls the whole array of custom class info from CoreData and stores the array row in a property.
The data is displayed ok at first. The IBOutlet connections are setup ok. I've disconnected and reconnected twice all outlets on MovieDetailsVC, so that should be ok.
"viewDidLoad" is called a successive time. Not sure from where. When it is called, the coredata entity and row number are pulled ok.
The issue is at line "lblTitle.text = self.movieRecord.title". I'm assuming any of the IBOutlets would cause the same issue.
The error thrown is what you would see if the outlets were not connected:
fatal error: unexpectedly fond nil while unwrapping Optional value.
code for the MovieDetailsVC is below. Any ideas why this outlet link would break after working ok would be greatly appreciated.
import UIKit
import CoreData
class MovieDetailsVC: UIViewController {
#IBOutlet var lblTitle: UILabel!
#IBOutlet var lblDescr: UILabel!
#IBOutlet var lblLink: UILabel!
#IBOutlet var imgMovie: UIImageView!
var movieRecord:FavMovie!
var favMovies = [FavMovie]()
override func viewDidLoad() {
super.viewDidLoad()
fetchAndSetResult()
}
func fetchAndSetResult() {
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let context = app.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "FavMovie")
do {
let results = try context.executeFetchRequest(fetchRequest)
self.favMovies = results as! [FavMovie]
} catch let err as NSError {
print(err.description)
}
if let row = NSUserDefaults.standardUserDefaults().objectForKey("movieRow") as? Int {
self.movieRecord = self.favMovies[row]
configureCellDescr()
}
}
func configureCellDescr() {
lblTitle.text = self.movieRecord.title
lblDescr.text = self.movieRecord.descrWhyGood
lblLink.text = self.movieRecord.linkImdb
imgMovie.image = self.movieRecord.getImg()
}
}
I just have a look at your source code in github and find the problem. There are two issues and I will explain it following.
it does that the second time that the app overrides viewdidload
The reason that your code would call the viewDidLoad method twice is because you have a segue in your storyboard that connect the tableViewCell to movieDetailVC. This will present the movieDetailVC once you click the cell.
And in your code for didSelectCell method, you create another movieDetailVC object and present it.
So actually movieDetailVC would be presented twice when you click the cell. This cause the issue.
Any ideas why this outlet link would break after working ok would be greatly appreciated
The reason why the IBOutlet is nil is because of the way you present movieDetailVC in your code. You create the movieDetailVC object using: let movieDetailsVC = MovieDetailsVC(). Doing it this way, the IBOutlets will not be connected correctly, because ios dont know about the storyboard information.
The correct way to create a MovieDetailVC object is to instantiate from storyboard. See this post for difference.
Solution
There is a very simple solution for your code design:
just remove let movieDetailsVC = MovieDetailsVC() and self.navigationController?.presentViewController(movieDetailsVC, animated: true, completion: nil) from your ViewController class. Since you save the selection data in NSUserDefault, the cell segue will present movieDetailVC and your movieDetailVC can also get the selection data from userDefault.
I create Start_button and make #IBOutlet and #IBAction
#IBOutlet weak var Start_button: UIButton!
#IBAction func Start_button(sender: AnyObject)
Now, i want hide button after click. I try this, but this don't work:
#IBAction func Start_button(sender: AnyObject)
{
Start_button.hidden = true;
}
Error message:
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
How i can hide this button?
Thanks for helping!
Its nil because you probably haven't connected it from your storyboard/nib. You need to connect the outlet, you can't just create an outlet in code and expect it to be connected to the visible element. The same goes for your action. #IBOutlet / #IBAction stands for Interface Builder Outlet/Action, which means you have to connect them in Interface Builder.
Also its better if your action uses the sender, and not a local variable (when its pointing to the same thing). And you shouldnt use ;at the end of the line.
#IBAction func Start_button(sender: UIButton) // Change to UIButton
{
sender.hidden = true
// OR
// (sender as! UIButton).hidden = true
}
#IBAction func button_nameA(sender: AnyObject) {
// show hidden buttons
self.Target_Object.hidden = false
}
So when you click the A buttons its automaticly send to you target and in target also you have to hide button if you working single view app
I want to update the label in the DetailViewController everytime I selected a tableRow in the MasterViewController. To achieve this, I designed a delegate, which I have in the MasterVC
protocol TestTableViewControllerDelegate {
func selectedRow(selectedCar : Car)
}
class TestTableViewController: UITableViewController {
...
var delegate : TestTableViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = DetailViewController()
The delegate works just fine, (it is implemented correctly in the DetailVC), it can pass values from TestTableVC to DetailVC and also correctly do println(), which prints a new Car.model String to the console every time I select a row in the TTVC.
The DetailVC looks like this (shortened):
class DetailViewController: UIViewController, TestTableViewControllerDelegate {
#IBOutlet var textLabel: UILabel!
var theCar : Car? {
didSet(newCar) {
refreshUI()
}
}
override func viewDidLoad() {
super.viewDidLoad()
refreshUI()
}
func selectedRow(selectedCar : Car) {
theCar = selectedCar
refreshUI()
}
func refreshUI() {
textLabel?.text = theCar!.model
}
}
I can achieve any kind of action with my delegate, expect for refreshing the UI. I have tried numerous ways, this is my latest attempt. Before that, I tried setting the textLabel's text property directly within the delegate method, didn't work. This problem only occurs when working with the UI-elements. I know it has something to do with the view not being loaded yet, but why does my refreshUI() function not work at all?
I am still a beginner, so any tip or help would be much appreciated!
A workaround I've used is to cerate a properly in the delegate and pass the value to it instead of the UI element. When the view loads I update the label's text properly with the value of the delegates property. I would think there's a better way to do this (I'm new to programming) but this is the best soultion I've come up with so far. Will update with sample code soon.
I have a tab bar controller as the starting point of my app, where one of the tab and what happens subsequently is meant for admins only. So I was password protecting a tab. I thought of adding a little modal dialogue in the viewDidLoad function of my view controller (which by the way is a UITableViewController),
Suppose I can get the text that the user entered in the dialogue box in the variable inputTextField.
The relevant section of the code from viewDidLoad():
if inputTextField?.text != "secret" {
return
}
super.viewDidLoad()
But it does not work. Any hint appreciated. Sorry if it is too basic, I am completely new to iOS and Swift programming, so pardon my ignorance folks.
Here is a simple example. Lots of ways. I dropped two UIViews in Storyboard on the first tab's VC. The one in the back I gave a dark color to simulate the hidden secret view (secretView). Inside of the view on top (entryView) I dragged a label "Enter Passcode" and a text field (passCode). I just hid the back view unless the secret code was correct.
import UIKit
class FirstViewController: UIViewController, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.passCode.delegate = self
entryView.hidden = false
secretView.hidden = true
}
#IBOutlet weak var entryView: UIView!
#IBOutlet weak var secretView: UIView!
let secretCode = "X"
#IBOutlet weak var passCode: UITextField!
func textFieldShouldReturn(textField: UITextField!) -> Bool {
if textField.text == secretCode {
entryView.hidden = true
secretView.hidden = false
} else {
self.passCode.text = "Try Again!"
}
textField.resignFirstResponder()
return true
}
}