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.
Related
I'm using Material Design Component i.e. MDCTextField but facing some issues while making its corners more rounded like a capsule.
I tried to set the border radius but it doesn't work for me.
Connection outlet:
#IBOutlet weak var userNameTextField: MDCTextField!
#IBOutlet weak var passwordTextField: MDCTextField!
#IBOutlet weak var mobileNumberTextField: MDCTextField!
var userNameController: MDCTextInputControllerOutlined?
var passwordController: MDCTextInputControllerOutlined?
var mobileNumberController: MDCTextInputControllerOutlined?
ViewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
userNameController = MDCTextInputControllerOutlined(textInput: userNameTextField)
passwordController = MDCTextInputControllerOutlined(textInput: passwordTextField)
mobileNumberController = MDCTextInputControllerOutlined(textInput: mobileNumberTextField)
userNameController?.borderRadius = 50
}
Scenarios:
Text field with round corner showing perfectly when there is no input or text
Text field distorted when we try to enter something
Please let me know, what I'm doing wrong.
Thanks.
Use pod 'MaterialComponents/TextFields'
This will solve the issue and make your MDCTextField rounded.
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.
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.
I have a custom UITableViewCell and one of its subviews is a button (with the title "RSVP"):
It is connected to the following code:
class SelectedEventsTableViewCell: UITableViewCell {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var attendanceLabel: UILabel!
#IBOutlet weak var attendanceButton: UIButton!
}
I am trying to change the background color of the button by adding this code to the SelectedEventsTableViewCell class:
override func awakeFromNib() {
super.awakeFromNib()
attendanceButton.backgroundColor = UIColor.greenColor()
}
However, it doesn't work. The button gets a white background:
Does anybody know why this is happening? It also doesn't work when I set the button's background color programmatically when the button is pressed.
Your help is appreciated.
Check somewhere you set the button background color white.
attendanceButton.backgroundColor = UIColor.whiteColor()
Check where you use SelectedEventsTableViewCell's object in TableView's data source or delegate methods in viewController.
ex.
cell.attendanceButton.backgroundColor = UIColor.whiteColor()
There are several possible reasons.
1.Did you make that outlet accessible? If not, CTRL-DRAG from storyboard.
2.Did you change that after awakeFromNib()? Maybe you set it again in cellForIndexPath(table view data source).
3.Cell was reused but awake once. It means you should recover its state after reuse. prepareForReuse() is designed for this.
I know this issue is old, but I ran into the same thing on iOS 15. The solution is to use the new UIButton.Configuration struct.
if #available(iOS 15.0, *) {
var config = UIButton.Configuration.filled()
config.baseBackgroundColor = UIColor.greenColor()
attendanceButton.configuration = config
} else {
attendanceButton.backgroundColor = UIColor.greenColor()
}
Run something like that in your prepareForReuse function or after you dequeue the cell and it should start working again. There are a lot more customisation options in iOS 15 using that configuration struct so have a dig into it for configuring all other aspects of your buttons.
This question already has answers here:
Instance member cannot be used on type
(8 answers)
Closed 6 years ago.
Basically, I am trying to reset some NSConstraints when the user taps a button.
My constraint outlets:
#IBOutlet weak var firstButtonHeight: NSLayoutConstraint!
#IBOutlet weak var secondButtonheight: NSLayoutConstraint!
#IBOutlet weak var thirdButtonHeight: NSLayoutConstraint!
Example action:
#IBAction func firstButtonHeight(sender: UIButton) {
firstButtonHeight.constant = 100
}
I thought I could put them into an array (below) and iterate through them before running firstButtonHeight.constant = 100:
var constraints: [NSLayoutConstraint] = [firstButtonHeight, secondButtonheight, thirdButtonHeight]
for constraint in constraints {
constraint.constant = 50 //Original constraint value
}
However, I get the error Instance member 'firstButtonHeight' cannot be used on type 'ViewController'. When did firstButtonHeight become a ViewController?
One major problem is that in your constraints initializer you mention the names of other instance variables. You can't do that, because at instance variable initialization time, the instance doesn't exist yet — it is what we are initializing!
You could solve this easily by moving the code into a function that runs after initialization time:
class ViewController: UIViewController {
#IBOutlet weak var firstButtonHeight: NSLayoutConstraint!
#IBOutlet weak var secondButtonheight: NSLayoutConstraint!
#IBOutlet weak var thirdButtonHeight: NSLayoutConstraint!
func someFunction() {
var constraints: [NSLayoutConstraint] = [firstButtonHeight, secondButtonheight, thirdButtonHeight]
for constraint in constraints {
constraint.constant = 50 //Original constraint value
}
}
}
Look carefully at the difference between that and what you've been doing.
A common solution to the problem where you need to mention an instance variable in another instance variable's initializer is to declare the second instance variable lazy. Note that you must use self when you do this (and in general I would recommend always using it). Thus, this compiles at top level of your view controller:
#IBOutlet weak var firstButtonHeight: NSLayoutConstraint!
#IBOutlet weak var secondButtonheight: NSLayoutConstraint!
#IBOutlet weak var thirdButtonHeight: NSLayoutConstraint!
lazy var constraints : [NSLayoutConstraint] = [self.firstButtonHeight, self.secondButtonheight, self.thirdButtonHeight]
But the rest, the for loop, still needs to live inside a function body.