So I am making a quiz app, and I am using the following code to display to the user if the answer is correct or wrong.
The app looks like this:
Image here
This is the code to display:
#IBAction func checkAnswer(_ sender: UIButton) {
if let question = currentQuestion, let answer = sender.titleLabel?.text {
if (question.validateAnswer(to: answer)) {
score.incrementCorrectAnswers()
currentScore = currentScore + 1
feedbackLabel.textColor = UIColor(red:0.15, green:0.61, blue:0.61, alpha:1.0)
feedbackLabel.text = "Correct!"
scoreLabel.text = "\(currentScore)"
sender.backgroundColor = hexStringToUIColor(hex: "213F4B")
} else {
score.incrementIncorrectAnswers()
feedbackLabel.textColor = UIColor(red:0.82, green:0.40, blue:0.26, alpha:1.0)
feedbackLabel.text = "Wrong, correct answer is: \n\(currentQuestion!.getAnswer())"
sender.shake()
sender.backgroundColor = hexStringToUIColor(hex: "3F2F4B")
}
answer1Button.isEnabled = false
answer2Button.isEnabled = false
answer3Button.isEnabled = false
answer4Button.isEnabled = false
nextQuestionButton.isEnabled = true
feedbackLabel.isHidden = false
}
}
When using this, if the user tap the wrong button, the button turns red, and if the user taps the correct button, the button turns green. How can I make it so if the user taps the wrong button, the answer on the correct button turns green at the same time as the wrong one goes red? Let me know if any other code from project is necessarily.
You could loop through all UIButton's and check for sender.titleLabel?.text like this:
for view in self.view.subviews as [UIView] {
if let btn = view as? UIButton {
if btn.titleLabel?.text == currentQuestion!.getAnswer() {
btn.backgroundColor = hexStringToUIColor(hex: "213F4B") // Green Color
}
}
}
I'm not on my computer so won't be able to test it, so try it out and let me know how it works.
Related
The code and question represents an Xcode project in Swift. I have a a group of buttons that display options according to which text is presented on a label.
The label text is derived from the keys of a dictionary and the button texts are derived from the values of that same dictionary. The dictionary type is [String: [String]. The keys and values are both placed in arrays. I currently display the correct data but some values differ in length than others.
For example one key has 3 values and another key has 5. I want to hide the buttons if there is not text to send to it. So if a key is presented in a label and has 3 values I only want to display 3 buttons and so. What would be the best way to achieve this functionality? Here is my code that is not achieving want I want to accomplish:
func startSurvey() {
if surveyQuestions.isEmpty {
surveyQuestions = Array(SampleSurvey().surveyquestions.keys)
print(surveyQuestions)
}
let rand = Int(arc4random_uniform(UInt32(surveyQuestions.count)))
questionTitle.text = surveyQuestions[rand]
var choices = SampleSurvey().surveyquestions[surveyQuestions[rand]]!
print(choices)
print(choices.count)
surveyQuestions.remove(at: rand)
var button = UIButton()
var x = 0
// var choicePool = choices.count
if choices.count == 2 {
for index in 1...2 {
button = view.viewWithTag(index) as! UIButton
button.setTitle(choices[x], for: .normal)
x += 1
if button.titleLabel?.text.isEmpty == true {
button.isHidden = true
}
}
}
else if choices.count == 4 {
for index in 1...4 {
button = view.viewWithTag(index) as! UIButton
button.setTitle(choices[x], for: .normal)
x += 1
if button.titleLabel?.text.isEmpty == true {
button.isHidden = true
}
}
}
Here is a screenshot of the simulator, as you can see this particular key has only 2 values so there are 3 blank buttons, I want to hide the buttons that are blank:
UPDATE: The following code has granted me the functionality I was aiming for:
var button = UIButton()
var x = 0
let buttonTags = [0,1,2,3,4]
if choices.count == 2 {
for idx in buttonTags {
button = surveyChoices[idx]
if idx < choices.count {
button.setTitle(choices[x], for: .normal)
x += 1
} else {
button.isHidden = true
}
}
}
If the text in the button's titleLabel is nil, then your condition will become false.
Try to revise your code like this:
for index in 1...4 {
button = view.viewWithTag(index) as! UIButton
button.setTitle(choices[x], for: .normal)
x += 1
if (button.titleLabel?.text ?? "").isEmpty == true {
button.isHidden = true
}
}
This will check if the text is nil then it will return a "" which is empty.
You can try this:
SWIFT 4
button.isHidden = button.titleLabel?.text == nil || button.titleLabel?.text == ""
Personally I would add the buttons dynamically, one for each choice in the survey. You could easily do this using any of UITableView, UICollectionView or UIStackView by adding a row/cell that contains a button (tableView or collectionView) or just by adding a button to a vertical stack view.
For your specific code, your only iterating over the number of choices, So in the example below (from your code) you are only working on two buttons and not touching the others
if choices.count == 2 {
for index in 1...2 {
button = view.viewWithTag(index) as! UIButton
button.setTitle(choices[x], for: .normal)
x += 1
if button.titleLabel?.text.isEmpty == true {
button.isHidden = true
}
}
}
for index in 1...2... your not doing anything with the other 3.
You should loop through all buttons and if there is a choice for it, set the title, otherwise hide the button
Here is a working example in a Playground:
let question = ["are you expecting a child": ["yes", "no"]]
let choices = question["are you expecting a child"]!
let buttonTags = [0, 1, 2, 3, 4]
let buttons = [
UIButton(),
UIButton(),
UIButton(),
UIButton(),
UIButton()
]
for idx in buttonTags {
let button = buttons[idx]
if idx < choices.count {
button.setTitle(choices[idx], for: .normal)
} else {
button.isHidden = true
}
}
buttons.map {
print($0.titleLabel?.text)
print($0.isHidden)
}
OUPUT
Optional("yes")
false
Optional("no")
false
nil
true
nil
true
nil
true
I'm still new with Swift 4 and i found this on the internet & tried to make this Button to show "something" when the image button is A. But when i clicked the Button again, only its image button changed but the "something" still not hidden. Can someone help ?
I already done with other button that using this animation but the button is showing another button from Library.
But this 1 is different, not showing another button from Library but showing ChromaColorPicker
var sizeOff = UIImage(named: "Brush-Size")
var sizeOn = UIImage(named: "Brush-Size-On")
============================ somewhere else ======================
extension ViewController {
#IBAction func BrushColourClicked(_ sender: UIButton) {
if sender.currentImage == ColourOn {
sender.setImage(ColourOff, for: .normal)
} else {
sender.setImage(ColourOn, for: .normal)
} // Image first set
configureUI()
}
func configureUI() {
let colorPicker = ChromaColorPicker(frame: CGRect(x: 25.0, y: 410.0, width: 140.0, height: 140.0)) // Position & Size of Color Picker
ColourButtonCenter = colorPicker.center
colorPicker.center = BrushColour.center
colorPicker.delegate = self
colorPicker.padding = 5.0
colorPicker.stroke = 3.0
colorPicker.hexLabel.isHidden = true
colorPicker.layout()
view.addSubview(colorPicker)
colorPicker.alpha = 0
if BrushColour.currentImage == ColourOn {
UIView.animate(withDuration: 0.35, animations: {
colorPicker.alpha = 1
colorPicker.center = self.ColourButtonCenter
})
} // Animation show
else {
UIView.animate(withDuration: 0.35, animations: {
colorPicker.alpha = 0
colorPicker.center = self.BrushColour.center
})
} // Animation hide
}
}
i dont know if
colorPicker.alpha = 0
is working or not
Every time you call configureUI you're adding a new colour picker to the view, then animating the alpha on it.
So when you're trying to hide the colorPicker what's actually happening is that you're adding a new colour picker with alpha 0 and animating it to alpha 0 (doing nothing), the previous colorPicker will still be visible so it will look as though nothing as changed.
Create a variable for colorPicker once and add it to the view, then configure that instance in your configureUI function.
My question is: When the UITextField is empty, how do I click the "Backspace" button to go to the previous UITextField? I have been struggling trying to do this in my code below?
Second Question: How do I only allow 1 character to get entered in the UITextField?
I am new at Swift code and trying to learn. Any help would be great.
What I am trying to do is have the user be able to type in a code in the 6 UITextFields and be able to click the "Backspace" button on any one of the UITextFields with only allowing the user to enter one number in each UITextField.
Code Below:
#objc func textFieldDidChange(textfield: UITextField) {
let text = textfield.text!
if text.utf16.count == 0 {
switch textfield {
case textField2:
textField1.becomeFirstResponder()
textField1.backgroundColor = UIColor.blue
textField1.tintColor = .clear
case textField3:
textField2.becomeFirstResponder()
textField2.backgroundColor = UIColor.blue
textField2.tintColor = .clear
case textField4:
textField3.becomeFirstResponder()
textField3.backgroundColor = UIColor.blue
textField3.tintColor = .clear
case textField5:
textField4.becomeFirstResponder()
textField4.backgroundColor = UIColor.blue
textField4.tintColor = .clear
case textField6:
textField5.becomeFirstResponder()
textField5.backgroundColor = UIColor.blue
textField5.tintColor = .clear
textField6.resignFirstResponder()
textField6.backgroundColor = UIColor.blue
textField6.tintColor = .clear
default:
break
}
}
else if text.utf16.count == 1 {
switch textfield {
case textField1:
textField1.backgroundColor = UIColor.black
textField1.textColor = .white
textField1.tintColor = .clear
textField2.becomeFirstResponder()
textField2.backgroundColor = UIColor.black
textField2.textColor = .white
textField2.tintColor = .clear
case textField2:
textField3.becomeFirstResponder()
textField3.backgroundColor = UIColor.black
textField3.textColor = .white
textField3.tintColor = .clear
case textField3:
textField4.becomeFirstResponder()
textField4.backgroundColor = UIColor.black
textField4.textColor = .white
textField4.tintColor = .clear
case textField4:
textField5.becomeFirstResponder()
textField5.backgroundColor = UIColor.black
textField5.textColor = .white
textField5.tintColor = .clear
case textField5:
textField6.becomeFirstResponder()
textField6.backgroundColor = UIColor.black
textField6.textColor = .white
textField6.tintColor = .clear
case textField6:
textField6.resignFirstResponder()
default:
break
}
}
}
I'd just like to point out that I'm still relatively new to iOS and Swift in general, but even with just a few minutes of searching, I was able to find some seeds of ideas which provided me with the suggested solution.
Based on your (improved) question, I believe a different approach is required. What you really don't want to use a text component. "Why"?
I here you ask. Because they don't actually provide you with the functionality that you want and come with a considerable overhead.
For this, what you really want is more control. You want to know when a key is pressed and you want to respond to it (I know, sounds like a text component, but) and be notified when more extended functionality occurs, like the delete key is pressed.
After a few minutes of research, some trial and error, I found that the UIKeyInput is more along the lines of what you want.
It will tell you when text is inserted and, more importantly, will tell you when Delete is pressed
The added benefit is, you can filter the input directly. You can take the first character from the String and ignore the rest or auto fill the following elements with the remaining text. You can perform validation (for numerical only content) and what ever else you might want to do
So, I started a really new project, added a UILabel to the UIViewController in the storyboard, bound it to the source and implemented the UIKeyInput protocol as such...
class ViewController: UIViewController {
override var canBecomeFirstResponder: Bool {
return true
}
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
becomeFirstResponder()
}
}
extension ViewController: UIKeyInput {
var hasText: Bool {
return true
}
func insertText(_ text: String) {
print(text)
label.text = text
}
func deleteBackward() {
print("Delete backward")
}
}
I ran the project and when a key was typed, the label was updated with the new key and when delete was pressed, the Delete backward text was printed to console.
Now. You have some choices to make. To use a single UIViewController and (maybe) a series of UILabels and manage interactions within it, so when a key is typed, you present the next label as the input focus (and when delete is pressed, you move back) or do you create a series of UIControls which represent each digit and manage via some delegate call back process.
You may also need to implement the UITextInputTraits protocol, which will allow you to control the keyboard presented
You might also like to have a read through Responding to Keyboard Events on iOS, CustomTextInputView.swift and Showing the iOS keyboard without a text input which were just some of the resources I used to hobble this basic example together with.
you can use this extension for your second question:
import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {
#IBInspectable var maxLength: Int {
get {
guard let length = maxLengths[self] else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
addTarget(
self,
action: #selector(limitLength),
for: UIControlEvents.editingChanged
)
}
}
#objc func limitLength(textField: UITextField) {
guard let prospectiveText = textField.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
text = prospectiveText.substring(to: maxCharIndex)
selectedTextRange = selection
}
}
when you add this extension to your project you can see an extra attribute in "Attribute Inspector" tab and you can set the max length of UITextField.
I'm trying to replicate the scroll selector at the bottom of the emoji keyboard, where you can select the category by tapping on the appropriate icon and it will also show you in which category you are if you scroll. However, I'm having trouble getting the selected button to appear highlighted for some reason. I can get the scroll to work fine, but when I click on the icon it won't stay highlighted. Is this to do with some property of the touchUpInside trigger and the highlighted property?
My Code
var categoryButtons = [UIButton]()
var categoryDistances = [CGFloat]()
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var curIndex = -1
for d in categoryDistances {
if (scrollView.contentOffset.x + 5 >= d) {
curIndex += 1
}
}
for b in categoryButtons {
b.isHighlighted = false
}
if (curIndex == -1) {
curIndex = 0
}
categoryButtons[curIndex].isHighlighted = true
}
func shortcutSelected(_ sender: UIButton) {
snapSelector.contentOffset.x = categoryDistances[categoryButtons.index(of: sender)!]
for b in categoryButtons {
b.isHighlighted = false
}
sender.isHighlighted = true
}
Got it to work the way I wanted by applying a tint color instead of using the isHighlighted or isSelected properties:
let tintedImg = origImg?.withRenderingMode(UIImageRenderingMode.alwaysTemplate)
let shortcut = UIButton()
shortcut.setBackgroundImage(tintedImg, for: .normal)
shortcut.tintColor = UIColor.gray
I want to be able to check if all buttons' background color is UIColor.whiteColor. I'm determining whether a view is in search mode or in normal mode by the button state.
I want to do something similar with the following but contains() checks if array contains certain value. It wouldn't work in my case because UIColor.whiteColor is a property of UIButton.
if contains(categoryScrollView.subviews, UIColor.whiteColor) {
inSearchMode = false
}
Then if I put it in the following way, I do not know how I can make sure all button's background color is white as it will pass validation as soon as any button's background color is white which is not what I need.
for button in categoryScrollView.subviews {
if button.backgroundColor == UIColor.whiteColor() {
inSearchMode = false
}
}
How can I check is background color of all buttons?
I'd enclose this check in a function, like this (note that I'm including a check that each view is a button):
func allWhiteButtons(view: UIView)-> Bool{
for view in view.subViews {
if let button = view as? UIButton {
if button.backgroundColor != UIColor.whiteColor() {
return false
}
}
}
return true
}
var allWhite = true
for button in categoryScrollView.subviews {
if button.backgroundColor != UIColor.whiteColor() {
allWhite = false
}
}
inSearchMode = !allWhite
But IMHO, this is not a good way to do it at all. You should have a code to do state transition and make the buttons white or not white based on this state.
If you don't have a lot of UIButtons and that they are already declared as IBOutlets you can create a function that loops through all your UIButtons :
for button in [yourButton1, yourButton2, yourButton2] {
if button.backgroundColor == UIColor.whiteColor {
// Do something
} else {
// Do something else
}
}
Or you can also loop through all the buttons of your self.view :
for view in self.view.subviews as [UIView] {
if let button = view as? UIButton {
if button.backgroundColor == UIColor.whiteColor {
// Do something
} else {
// Do something else
}
}
}
Add a counter like whiteBtnCount in the loop where you are checking the backgroundcolor.Increment that counter if it matches the color and break the loop once counter reaches the button count. Voila,now you know whether all buttons are white color or not.
var whiteBtnCount: Int = 0
for button in categoryScrollView.subviews {
if button.backgroundColor == UIColor.whiteColor() {
whiteBtnCount += 1
if whiteBtnCount == btnCount { //ensure btnCount variable holds the number of buttons
inSearchMode = false
break
}
}
}