I get a problem in using container view and segmented control to achieve switch views.I have put two container views in one view controller, and each of container view embed a new view(three view controller in the same UIViewController).
Here is my code
import UIKit
import Charts
class ReportViewController: UIViewController {
#IBOutlet weak var containerTwoWeeks: UIView!
#IBOutlet weak var containerToday: UIView!
#IBOutlet weak var segUi: UISegmentedControl!
#IBOutlet weak var resultLabel: UILabel!
#IBOutlet weak var paymentPie: PieChartView!
#IBOutlet weak var incomePie: PieChartView!
override func viewDidLoad() {
super.viewDidLoad()
// pieChartUpdate()
// selectResultData()
}
#IBAction func segChange(_ sender: UISegmentedControl) {
switch segUi.selectedSegmentIndex {
case 0:
self.containerTwoWeeks.isHidden = false
self.containerToday.isHidden = true
break
case 1:
self.containerTwoWeeks.isHidden = true
self.containerToday.isHidden = false
break
default:
break
}
}
func selectResultData() {
resultLabel.attributedText = reportService.selectResultData()
}
func pieChartUpdate () {
(too much code here, ignore it)
}
}
Everything works well until I called the method pieChartUpdate() or selectResultData() in vieDidLoad(),the error message as below
"Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value"
I can switch the views without calling the pieChartUpdate() or selectResultData() method, so I really want to know the reason and solution.Hope someone could figure out this problem,thanks.
You got very common mistake and error. But don't worry.
Check your full error message. There must be a clue what variable you are trying to access and getting nil (it might be resultLabel).
Then make sure the connection between storyboard and view controller for this variable is correct. You can try to remove and create it again if you don't get what is wrong.
If you need more help, please show us full error message.
Related
I started coding about 2 weeks ago and chose Swift for iOS development purposes and I am having quite a struggle with debugging. This project is a simple Tic Tac Toe app and everything runs fine except for when I tap on one of my UILabels for an X to appear.
I keep getting this Thread 1: EXC_BAD_ACCESS (code=2, address=0x1134ca968) error on my canTap and I don't know how to fix it.
#IBAction func onTappedGridLabel(_ sender: UITapGestureRecognizer) {
if gameOver {
return
}
var canPlay = false
for label in labels {
let selectedPoint = sender.location(in: background)
if label.frame.contains(selectedPoint) {
if label.canTap {
if(xTurn) {
label.text = "X"
}
else {
label.text = "O"
}
xTurn = !xTurn
label.canTap = false
checkForWinner()
}
}
if label.canTap {
canPlay = true
https://www.dropbox.com/s/amokuept1crt8i0/Tic%20Tac%20Toe%202.0.zip?dl=0
Here's a link with the entire project if anyone is interested. I think it's pretty short.
The problem is that a UILabel has no canTap property. Your label subclass GridLabel has that property, but you have not designated that your labels in the storyboard are instances of your label subclass, so they are just ordinary labels.
In other words, you have this code:
#IBOutlet weak var gridLabel0: GridLabel!
#IBOutlet weak var gridLabel1: GridLabel!
#IBOutlet weak var gridLabel2: GridLabel!
#IBOutlet weak var gridLabel3: GridLabel!
#IBOutlet weak var gridLabel4: GridLabel!
#IBOutlet weak var gridLabel5: GridLabel!
#IBOutlet weak var gridLabel6: GridLabel!
#IBOutlet weak var gridLabel7: GridLabel!
#IBOutlet weak var gridLabel8: GridLabel!
var labels = [GridLabel]()
But you are lying in every one of those lines. None of your labels is a GridLabel. They are all ordinary UILabels. Hence, as soon as you try to access the canTap of one of them, you crash.
In the storyboard, select the nine grid labels and, in the Identity inspector, change their class to GridLabel. Problem solved.
This question already has answers here:
Fatal error: unexpectedly found nil while unwrapping an Optional values [duplicate]
(13 answers)
Closed 2 years ago.
I've been wanting to code for a while, and just started using my free time to try to learn so I know very little but am eager to learn! Also first time poster.
I'm making a simple dice game app, and I've been horribly stuck trying to unwrap strings and variables. I keep getting the error "Unexpectedly found nil while implicitly unwrapping an Optional value" when trying to assign a user input name to a label on the next view controller.
Let me know if this is too convoluted off an app design, but this is what I'm trying to design:
first view controller where 2-4 players enter their names. They hit the next button and it takes them to the next viewController where there are dice, a roll button, and the names they just entered.
I have the UITextField and UILabel for the names correctly connected to IBOutlets.
I forced the UITextField to unwrap by making them optional to prevent the app from crashing, but the names are still not updated.
Here's my code:
class ViewController: UIViewController, UITextViewDelegate, UITextFieldDelegate {
#IBOutlet weak var name1: UITextField!
#IBOutlet weak var name2: UITextField!
#IBOutlet weak var name3: UITextField!
#IBOutlet weak var name4: UITextField!
//connects name strings
#IBOutlet weak var dice1: UIImageView!
#IBOutlet weak var dice2: UIImageView!
#IBOutlet weak var dice3: UIImageView!
#IBOutlet weak var dice4: UIImageView!
//links dice images
#IBOutlet weak var name1Label: UILabel!
#IBOutlet weak var name3Label: UILabel!
#IBOutlet weak var name4Label: UILabel!
#IBOutlet weak var name2Label: UILabel!
//links name labels on second viewController
var diceArray = ["dice1", "dice2", "dice3", "dice4", "dice5", "dice6"]
//creates an array to set dice images
var randomArray = [0,0,0]
var randomDice1 : Int = 0
var randomDice2 : Int = 0
var randomDice3 : Int = 0
//creates variables to hold random number
func storeNames () {
name1Label?.text = name1.text. THIS IS WHERE I KEEP GETTING FATAL ERRORS
name2Label?.text = name2.text
name3Label?.text = name3.text
name4Label?.text = name4.text
//stores the user inputed names into an array
}
//stores the names from textfield into name strings
override func viewDidLoad() {
super.viewDidLoad()
Thread.sleep(forTimeInterval:0.25)
//extends loading screen
name1?.placeholder = "Player 1 Name"
name2?.placeholder = "Player 2 Name"
name3?.placeholder = "Player 3 Name"
name4?.placeholder = "Player 4 Name"
//Creates placeholder text
let tap = UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing))
view.addGestureRecognizer(tap)
//removes keyboard when you tap outside keyboard
}
#IBAction func nextButton(_ sender: UIButton) {
storeNames()
}
You want to
assign a user input name to a label on the next view controller.
But all your labels, and text fields seem to be declared as outlets on the same view controller.
You cannot directly use next / previous view controller's outlets like this.
What you need to do is collect all naems in the first view controller, pass them to the next view controller and set them as label text in the next view controller. If you are using storyboards and segues, you can leverage prepareForSegue:sender:, method to do this.
Since you mention you have just started, and are learning things Here are some online tutorials on passing data between views:
https://www.hackingwithswift.com/example-code/system/how-to-pass-data-between-two-view-controllers
https://learnappmaking.com/pass-data-between-view-controllers-swift-how-to/
https://matteomanferdini.com/how-ios-view-controllers-communicate-with-each-other/
P.S. This is a very basic approach, but not a recommended one. View transitions and data should not be mixed together in the same code. If you are planning to use this for building a robust app it will be a good idea to also start learning SOLID principles, and design patterns.
maybe I am missing something really fundamental here, but after staring at the code for an hour or so, my brain is going trough cycles and I would appreciate a fresh glance at this problem.
I have the following UIView:
import UIKit
protocol DetailViewWillShowUpDelegate {
func sendDetailOpened(_ openedBool: Bool)
}
class locationXIBController: UIView {
#IBOutlet weak var loationLabel: UILabel!
#IBOutlet weak var vsedniOteviraciDobaLabel: UILabel!
#IBOutlet weak var prijmajiKartyLabel: UILabel!
#IBOutlet weak var detailViewButtonOutlet: UIButton!
#IBOutlet weak var backgroundViewButton: UIButton!
let openedBool = true
var detailViewWillShowUpDelegate: DetailViewWillShowUpDelegate?
override func awakeFromNib() {
super.awakeFromNib()
}
#IBAction func vecerkaDetailButtonPressed(_ sender: UIButton) {
detailViewWillShowUpDelegate?.sendDetailOpened(openedBool)
print("pressed")
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let result = detailViewButtonOutlet.hitTest(convert(point, to: detailViewButtonOutlet), with: event) {
return result
}
return backgroundViewButton.hitTest(convert(point, to: backgroundViewButton), with: event)
}
}
Now the problem is, that when I call/press the vecerkaDetailButtonPressed function I get "pressed" output in the console but the protocol for some reason doesn't go trough.
The other side looks like this (stripped for simplicity):
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
let locationXIB = locationXIBController()
let isVecerkaDetailOpened = false
override func viewDidLoad() {
locationXIB.detailViewWillShowUpDelegate = self
}
extension MapViewController: DetailViewWillShowUpDelegate {
func sendDetailOpened(_ openedBool: Bool) {
isVecerkaDetailOpened = openedBool
print("success")
}
}
I know the protocol value at the moment of execution is nil. As I said, any help is appreciated, thanks!
First, a couple of naming convention issues:
The name locationXIBController is a bad choice for a UIView object. It is a view object, not a controller object.
Second, class names in Swift should start with an upper-case letter. So LocationXIBView would be a much better name for that view class.
Next, your code
let locationXIB = locationXIBController()
...is wrong. That creates a brand-new instance of your locationXIBController class that you never install in your view hierarchy. You should make that line an IBOutlet:
#IBOutlet weak var locationXIB: locationXIBController!
And then you should control-drag from the locationXIBController in your StoryBoard onto the outlet in your view controller. That will cause Interface Builder to connect the outlet.
Now when you run your program the variable locationXIB will be connected to the locationXIBController view from your storyboard/XIB when it's loaded.
In addition to the answer of #Duncan C, you might check whether you need super.viewDidLoad() at the top of the viewDidLoad() method in the MapViewController class? Not doing that can lead to quirky things in your app.
I asked:
So does detailViewWillShowUpDelegate actually point at anything, or is it nil?
And you replied:
I just tried debugging and it is actually nil
So that's the problem... you need to set detailViewWillShowUpDelegate to point to a valid delegate object. This is often done in the .xib file or storyboard, and sometimes people forget to make that connection, so check there if it makes sense. Else you'll just need to get a reference to the delegate at some point before the code in question can run and set it up.
Answer to the credit of #Paulw11
I finally managed to get it working by communicating like so:
step 1) 1:1 communication via protocol between MKAnnotation and MKAnnotationView
step 2) 1:1 communication via protocol between MKAnnotationView and MapViewController passing the same data
Finally works like a charm, thanks!
I'm trying to make a UIAlert pop up, but whenever the code calls the function that should do that, a value totally unrelated in the process caused an error saying that it returned nil when it expected to have a value. I solved the issue, but I have no idea why my solution worked.
The original:
var fullFreqHeard: [Double] = []
#IBOutlet weak var fullFreqLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
fullFreqLabel.text = "\(fullFreqHeard)"
}
The solution:
var fullFreqHeard: [Double] = []
#IBOutlet weak var fullFreqLabel: UILabel?
override func viewDidLoad() {
super.viewDidLoad()
fullFreqLabel?.text = "\(fullFreqHeard)"
}
The difference is in the original I force unwrapped the label, whereas in the solution I used a '?'. What is going on here? I was also wondering if it might have anything to do with the alert.
I don't think it has anything to do with the alert. The reason force unwrapping fixes the error is because fullFreqLabel is initially nil. You have to initialize it before setting it's value. For instance, var fullFreqLabel = UILabel(frame: CGRect.zero)
Before I get started, I have to say that this project marks the first in-depth use of Swift and XCode in my life. I started it about a week ago (and am honestly impressed with how far I've gotten). I do not know too much about what I'm doing but I'm willing to learn.
Now, onto my question.
I'm trying to get a text field from one view controller to change a label from another. I thought I did it right, but it kept throwing syntax errors and such at me. After fixing that, I would run the code and get a SIGABRT error. Here's my code.
Here's the label, under BasicViewController (this isn't all that's in BasicViewController, I just cut out what I thought was pertinent)
class BasicViewController: UIViewController {
#IBOutlet weak var NameField: UILabel!
var NameText = String()
override func viewDidLoad() {
super.viewDidLoad()
NameField.text = NameText
}
And here's the text field, under EditCharController. This is where the SIGABRT error happens. (also, same thing with the lack of code.)
class EditCharController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var NameTextField: UITextField!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var NameTextDest : BasicViewController = segue.destinationViewController as! BasicViewController //Specifically, this is the line that it happens at.
NameTextDest.NameText = String(NameTextField.text)
}
}
Currently, XCode is telling me to change the var label to the let label, but even if I do that, it spits out this error.
Could not cast value of type 'UITabBarController'(?!?) (0x10310c8b0) to 'Project.BasicViewController' (0x1019a0060).
Last I checked, I didn't reference the UITabBarController anywhere in the code. Why am I getting this message?
Also any suggestions as to good in-depth tutorials will be very much appreciated.
Try
let index = 0 // change this to the tab index of the BasicViewController.
let NameTextDest : BasicViewController = (segue.destinationViewController as! UITabBarController).viewControllers[index] as! BasicViewController
NameTextDest.NameText = NameTextField.text!