In the last viewcontroller of my app I wrote a code which creates a number of labels which are filled with random characters from a set
Under the labels textfields appear where the user should match the characters from the labels. When he hits the button, his answer is checked. If the answer is right the score is updated.
After that the score label should update, and new text for the labels with characters should be generated and shown, but that's where I get stuck... My instinct would be to add a loop to the code in the viewDidLoad, but as the code to check the answer is in the buttonAction function outside of the viewDidLoad, I don't know how to do this...
Here is some code from my app which might clarify things. I deleted quite a lot of straightforward code and put a comment line in just for this example.
override func viewDidLoad()
{
super.viewDidLoad()
// variables are declared, the number of labels is calculated based on the frame size
// the text for the labels is calculated based on a function in a separate class I wrote
var a = 0
while a < numberOfLabels {
// the UILabels are made and filled
// the corresponding UITextFields are made
a += 1
labelX += labelWidth
}
// then here the button is coded, the last lines are:
myButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
view.addSubview(myButton)
}
#objc func buttonAction(sender: UIButton!) {
var a = 0
var rightAnswer = false
var userInput: [String] = Array()
while a < numberOfLabels.shared.category! {
if let theLabel = self.view.viewWithTag(a) as? UITextField {
let tekstInput = theLabel.text
userInput.insert(tekstInput!, at:a-1)
}
a = a + 1
}
let controle = Controle()
rightAnswer = controle.checkAnswer(userAnswer: userInput)
if rightAnswer {
if var score = PassScore.shared.category {
score += 1
PassScore.shared.category = score
}
else {
var score = 1
PassScore.shared.category = score
}
}
return
}
Have your initialization code in a function
func setupLabels() {
var a = 0
while a < numberOfLabels {
// the UILabels are made and filled
// the corresponding UITextFields are made
a += 1
labelX += labelWidth
}
}
And call it whenever you need it:
override func viewDidLoad()
{
super.viewDidLoad()
setupLabels()
}
#objc func buttonAction(sender: UIButton!) {
//...
setupLabels()
}
Related
This question already has answers here:
Swift Array - Check if an index exists
(12 answers)
Closed 3 years ago.
I'm having issues trying to figure out why am I getting Thread 1 Fatal error: Index out of range on my app. My app displays 8 images show to start image slideshow. For some reason I'm getting this error I can't figure it out. Is there a way to remove this error? Can anyone help me?
Here is my code balow and also a screenshot link here :
var images = [UIImage]()
var counter = 2
var time = Timer()
#IBOutlet weak var menuButton: UIBarButtonItem!
#IBOutlet weak var ImageView: UIImageView!
#IBOutlet weak var Slider1: UISlider!
#IBAction func Slider(_ sender: UISlider) {
_ = 0
let value = Int(sender.value)
ImageView.image = images[value]
}
#IBAction func NextButton(_ sender: Any) {
Slider1.value += 1
ImageView.image = images[Int(Slider1.value)]
self.ImageView.animationImages = self.images
self.ImageView.animationDuration = 15.0
self.ImageView.animationRepeatCount = 0
self.ImageView.startAnimating()
UIView.transition(with: self.ImageView, duration: 5.0, options: .transitionCrossDissolve, animations: {self.ImageView.image = self.ImageView.image}, completion: nil)
}
#IBAction func PrevButton(_ sender: Any) {
Slider1.value -= 1
ImageView.image = images[Int(Slider1.value)]
self.ImageView.animationImages = self.images
self.ImageView.animationDuration = 15.0
self.ImageView.animationRepeatCount = 0
self.ImageView.startAnimating()
UIView.transition(with: self.ImageView, duration: 5.0, options: .transitionCrossDissolve, animations: {self.ImageView.image = self.ImageView.image}, completion: nil)
}
//Set Status Bar to light content (white)
override var preferredStatusBarStyle : UIStatusBarStyle {
return .lightContent
}
override func viewDidLoad() {
//Set Navigation Bar color Example Home, Back button
self.navigationItem.backBarButtonItem?.tintColor = UIColor.white;
time = Timer.scheduledTimer(withTimeInterval: 8, repeats: true) { _ in
self.NextButton(self);
}
super.viewDidLoad()
setup()
images = [#imageLiteral(resourceName: "MainImage1.jpg"), #imageLiteral(resourceName: "MainImage2.jpg"), #imageLiteral(resourceName: "MainPage3.jpg"), #imageLiteral(resourceName: "MainImage4.jpg"), #imageLiteral(resourceName: "MainImage5.jpg"), #imageLiteral(resourceName: "MainImage6.jpg"), #imageLiteral(resourceName: "MainImage7.jpg"), #imageLiteral(resourceName: "MainImage8.jpg")]
sideMenus()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setup(){
self.navigationController?.navigationBar.tintColor = UIColor.white
}
override var prefersStatusBarHidden: Bool{
return false
}
var navigationBarAppearace = UINavigationBar.appearance()
override func viewDidAppear(_ animated: Bool){
}
func sideMenus() {
if revealViewController() != nil {
menuButton.target = revealViewController()
menuButton.action = #selector(SWRevealViewController.revealToggle(_:))
revealViewController().rearViewRevealWidth = 275
revealViewController().rightViewRevealWidth = 160
view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
}
}
The error is telling you that you're accessing an element outside the range of the images array. In viewDidLoad you set the images array with 8 elements. These elements have the range of 0-7. If you try to access an element outside that range, you get the out of range error that you're seeing.
Looking at your code, the problem is that in NextButton and PrevButton, you adjust the slider value and index, but you don't check to make sure you haven't gone past the array bounds. If you're on the first image and click the Prev button, you're out of range. If you're on the last image and click the Next button, you're out of range.
What you have to do is make sure you don't have a negative index in the array or have an index that's greater than or equal to the number of items in the array. From a user interface standpoint, the best solution is to disable the Prev button when you're on the first image and disable the Next button when you're on the last image.
Since you asked for an example, here's some code to put in nextButton after incrementing the slider value:
if slider1.value >= images.count {
return
}
And the code for prevButton after decrementing the slider value:
if slider1.value < 0 {
return
}
Now the function will exit if the array index is out of range.
One last thing to check is that the images array is being filled in viewDidLoad. Set a breakpoint in viewDidLoad and step through the code. If there's a problem loading the MainImage.jpg files, the array is going to be empty, and you'll get out of range errors when you access the array.
I have 5 buttons in an array I want to make random three of these invisible when pressed on button. so the last two buttons will be visible
I tried like this:
#IBAction func eliminateChoiceClicked(_ sender: Any) {
let buttons:[UIButton] = [buttonA,buttonB,buttonC,buttonD,buttonE]
let randomNumber = Int(arc4random_uniform(UInt32(3)))
buttons[randomNumber].isHidden = !buttons[randomNumber].isHidden
}
but it takes first elements [0,1,2] and only 1 button is invisible at each press
You are currently only selecting one button. Change that to a loop to select 3. Remove the selected button from the array and make it invisible. Finally, make the remaining buttons visible:
#IBAction func eliminateChoiceClicked(_ sender: Any) {
var buttons:[UIButton] = [buttonA,buttonB,buttonC,buttonD,buttonE]
// Select 3 buttons randomly and hide them
for _ in 1...3 {
let randomNumber = Int(arc4random_uniform(UInt32(buttons.count)))
let button = buttons.remove(at: randomNumber)
button.isHidden = true
}
// Make the remaining buttons visible
for button in buttons {
button.isHidden = false
}
}
I've just written a neat extension to get a random elements from an array:
extension Array {
func randomItems(count: Int) -> [Element] {
guard count <= self.count else { fatalError() }
var notUsedElements: [Element] = self
var randomElements: [Element] = []
while randomElements.count != count {
randomElements.append(notUsedElements.remove(at: Int(arc4random_uniform(UInt32(notUsedElements.count)))))
}
return randomElements
}
}
Using this you can achieve what you want this way:
#IBAction func eliminateChoiceClicked(_ sender: Any) {
let buttons:[UIButton] = [buttonA, buttonB, buttonC, buttonD, buttonE]
// make sure all are visible at the beginning
buttons.forEach { (button) in
button.isHidden = false
}
// hide random 3:
buttons.randomItems(count: 3).forEach { (button) in
button.isHidden = true
}
}
This is my code, The correct answer randomizes its position in the number of buttons. however, the current code seems to duplicate the 4th answer
It does this for every question and their respective set of answers.
The buttons tags are setup simply, the first one is tagged "1" and the 2nd button is tagged "2" and so on.
If any more info is needed feel free to ask.
I've left the problem code so that others who have similar issues will have a base to see what I did wrong.
Problem Code:
//
// AnimalViewController.swift
// It's Trival
//
// Created by Chris Levely on 12/18/16.
// Copyright © 2016 ZenithNomad. All rights reserved.
//
import UIKit
class AnimalViewController: UIViewController {
let questions = ["What is the fastest fish in the sea?", "Which animal has the most legs?"]
let answers = [["Sailfish", "Tuna", "Swordfish", "Marlin"], ["Millipede", "Spider", "Ant", "Octopus"]]
var currentQuestion = 0
var rightAnswerPlacement : UInt32 = 0
#IBOutlet weak var Question: UILabel!
#IBAction func AnswerQuestion(_ sender: AnyObject)
{
if (sender.tag == Int(rightAnswerPlacement))
{
print("Right")
}
else
{
print("Wrong")
}
if (currentQuestion != questions.count)
{
newQuestion()
}
}
func newQuestion()
{
Question.text = questions[currentQuestion]
rightAnswerPlacement = arc4random_uniform(4)+1
var button : UIButton = UIButton()
var x = 1
for i in 1...4
{
button = view.viewWithTag(i) as! UIButton
if (i == Int(rightAnswerPlacement))
{
button.setTitle(answers[currentQuestion][0], for: .normal)
}
else
{
button.setTitle(answers[currentQuestion][x], for: .normal)
x = 3
}
}
currentQuestion += 1
}
override func viewDidAppear(_ animated: Bool) {
newQuestion()
}
}
New Fixed Code:
//
// AnimalViewController.swift
// It's Trival
//
// Created by Chris Levely on 12/18/16.
// Copyright © 2016 ZenithNomad. All rights reserved.
//
import UIKit
class AnimalViewController: UIViewController {
let questions = ["What is the fastest fish in the sea?", "Which animal has the most legs?"]
let answers = [["Sailfish", "Tuna", "Swordfish", "Marlin"], ["Millipede", "Spider", "Ant", "Octopus"]]
var currentQuestion = 0
var rightAnswerPlacement : UInt32 = 0
#IBOutlet weak var Question: UILabel!
#IBAction func AnswerQuestion(_ sender: AnyObject)
{
if (sender.tag == Int(rightAnswerPlacement))
{
print("Right")
}
else
{
print("Wrong")
}
if (currentQuestion != questions.count)
{
newQuestion()
}
}
func newQuestion()
{
Question.text = questions[currentQuestion]
rightAnswerPlacement = arc4random_uniform(4)+1
var button : UIButton = UIButton()
var x = 1
for i in 1...4
{
button = view.viewWithTag(i) as! UIButton
if (i == Int(rightAnswerPlacement))
{
button.setTitle(answers[currentQuestion][0], for: .normal)
}
else
{
button.setTitle(answers[currentQuestion][x], for: .normal)
x += 1
}
}
currentQuestion += 1
}
override func viewDidAppear(_ animated: Bool) {
newQuestion()
}
}
It is because you hardcode x = 3, you should randomize x as well.
Here is my approach, I test it using UILabel, but it is the same concept
func newQuestion()
{
questionLabel.text = questions[currentQuestion]
var label : UILabel = UILabel()
var xArray = [0, 1, 2, 3]
var x = Int(arc4random_uniform(UInt32(xArray.count)))
for i in 1...4
{
label = view.viewWithTag(i) as! UILabel
label.text = answers[currentQuestion][xArray[x]]
if answers[currentQuestion][xArray[x]] == answers[currentQuestion][0]
{
print(answers[currentQuestion][xArray[x]])
//this is the answer, do something when user click this button
}
//remove the index x because you don't want to get a same number next time
xArray.remove(at: x)
x = Int(arc4random_uniform(UInt32(xArray.count)))
}
currentQuestion += 1
}
Imagine that before you set the title of the correct answer to a random button you first set the title of all the buttons in the same order of the array of possible answers, no problem there, now you set the string of the right answer to a random button, if this random position is not the same as the position of the right answer in the array (0), you end up with a duplicate string, that is what is actually happening.
What you need to do is to switch the answer at position 0 (right answer) with another answer at a random position in the array or even better shuffle the array of answers, how? I think there isn't a built-in function, but you could create a new empty array and remove a random answer from the original array and add it to the new one until all the answers switch from the original array to the new one in a random order.
Hope it helps.
My solution was a little more simple than randomizing the x value. In my sample code above I have it written as x =1 to start and then in the newQuestion function as x = 3,
The solution that I have gone with, I now have it as x+=1 so it now goes through the array for each loop instead of sticking with the 4th answer in the array. may not be the most elegant of solutions but it is all that is required for this simple app
I currently have 2 UITextFields, one needs to return a date and the other needs to return a time.
The issue(s) I'm having is that if I click on the first text field (i.e. "Date of birth:"), the date I select is returned (obviously this is exactly what I want to happen) however, if I click on the second text field (i.e. "Time of first meal:"), the second text field isn't updated but the first text field is (this is my first problem).
The next issue I'm having is that when I click on my second text field (i.e. "Time of first meal:"), the date is shown on the selector, not the time (I want the time to show).
I assume the two issues are related but I can't figure out what it is that I'm missing to distinguish the two pickers from one another.
The plan is to then have the third text field (i.e. "Time of last meal") also display a time (different to the second text field display) so I guess once I can get these two issues I'm currently having sorted, I can apply the same solution to the third text field.
As you can imagine (based on the amount of text fields I've got) I've cut down the code to only the bits that relate to the date pickers.
Picture of text fields from my storyboard
Picture of date picker (the same picker shows up for both text fields)
import UIKit
class ProfileCreator: UIViewController, UIPickerViewDelegate, UITextFieldDelegate, UIPickerViewDataSource {
var output: Int = 0
var output2: Int = 0
// -------------------------------------------------------------------------------------- //
// DOB Selector From Text Field Start 1 //
#IBOutlet weak var DOBSelectorTextField: UITextField!
func textFieldDidBeginEditing(DOBtextField: UITextField) {
let DOBPicker = UIDatePicker()
DOBtextField.inputView = DOBPicker
DOBPicker.datePickerMode = UIDatePickerMode.Date
DOBPicker.addTarget(self, action: "DOBpickerchanged:", forControlEvents: .ValueChanged)
}
func textFieldShouldReturn(DOBtextField: UITextField) -> Bool {
DOBtextField.resignFirstResponder()
return true
}
func DOBpickerchanged(DOBsender: UIDatePicker) {
let DOBformatter = NSDateFormatter()
DOBformatter.dateFormat = "dd MMMM yyyy"
DOBSelectorTextField.text = DOBformatter.stringFromDate(DOBsender.date)
}
// DOB Selector From Text Field End 1 //
// -------------------------------------------------------------------------------------- //
// First Meal Time Text Field Start 1 //
#IBOutlet weak var FirstMealTimeTextField: UITextField!
#IBAction func FirstMealTimeTextField(FirstMealTimeSender: UITextField) {
let FirstMealTimePicker = UIDatePicker()
FirstMealTimeSender.inputView = FirstMealTimePicker
FirstMealTimePicker.datePickerMode = UIDatePickerMode.Time
FirstMealTimePicker.addTarget(self, action: "FirstMealTimeChanged:", forControlEvents: .ValueChanged)
}
func FirstMealTimeChanged(FirstMealTimesender: UIDatePicker) {
let FirstMealtimeFormatter = NSDateFormatter()
FirstMealtimeFormatter.dateFormat = "HH:mm"
FirstMealTimeTextField.text = FirstMealtimeFormatter.stringFromDate(FirstMealTimesender.date)
}
// First Meal Time Text Field End 1 //
// -------------------------------------------------------------------------------------- //
override func viewDidLoad() {
super.viewDidLoad()
// -------------------------------------------------------------------------------------- //
// DOB Selector From Text Field Start 2 //
DOBSelectorTextField.delegate = self
// DOB Selector From Text Field End 2 //
// -------------------------------------------------------------------------------------- //
// First Meal Time Text Field Start 2 //
FirstMealTimeTextField.delegate = self
// First Meal Time Text Field End 2 //
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Here is my entire code that relates to my "Create Profile" page. Maybe it will help with the understanding of what I've done so far. I'm fairly confident with creating pickers where the data comes from an array, the thing I'm struggling with is doing multiple date pickers on the same page.
import UIKit
class ProfileCreator: UIViewController, UIPickerViewDelegate, UITextFieldDelegate, UIPickerViewDataSource {
var output: Int = 0
var output2: Int = 0
// -------------------------------------------------------------------------------------- //
// DOB Selector From Text Field Start 1 //
#IBOutlet weak var DOBSelectorTextField: UITextField!
func textFieldDidBeginEditing(DOBtextField: UITextField) {
let DOBPicker = UIDatePicker()
DOBtextField.inputView = DOBPicker
DOBPicker.datePickerMode = UIDatePickerMode.Date
DOBPicker.addTarget(self, action: "DOBpickerchanged:", forControlEvents: .ValueChanged)
}
func textFieldShouldReturn(DOBtextField: UITextField) -> Bool {
DOBtextField.resignFirstResponder()
return true
}
func DOBpickerchanged(DOBsender: UIDatePicker) {
let DOBformatter = NSDateFormatter()
DOBformatter.dateFormat = "dd MMMM yyyy"
DOBSelectorTextField.text = DOBformatter.stringFromDate(DOBsender.date)
}
// DOB Selector From Text Field End 1 //
// -------------------------------------------------------------------------------------- //
// Gender Selector From Text Field Start 1 //
#IBOutlet weak var GenderSelectorTextField: UITextField!
var GenderSelectorData = ["Male", "Female"]
var GenderSelectorPicker = UIPickerView()
// Gender Selector From Text Field End 1 //
// -------------------------------------------------------------------------------------- //
// Height Whole Number Text Field Start 1 //
#IBOutlet weak var HeightWholeNumberTextField: UITextField!
// Height Whole Number Text Field Start 1 //
// -------------------------------------------------------------------------------------- //
// Height Whole Number Unit Selector Text Field Start 1 //
#IBOutlet weak var HeightWholeNumberUnitSelectorTextField: UITextField!
var HeightWholeNumberUnitSelectorData = ["m", "ft"]
var HeightWholeNumberUnitSelectorPicker = UIPickerView()
// Height Whole Number Unit Selector Text Field Start 1 //
// -------------------------------------------------------------------------------------- //
// Height Decimal Number Text Field Start 1 //
#IBOutlet weak var HeightDecimalTextField: UITextField!
// Height Decimal Number Text Field End 1 //
// -------------------------------------------------------------------------------------- //
// Height Decimal Number Unit Selector Text Field Start 1 //
#IBOutlet weak var HeightDecimalNumberUnitSelectorTextField: UITextField!
var HeightDecimalNumberUnitSelectorData = ["cm", "inch"]
var HeightDecimalNumberUnitSelectorPicker = UIPickerView()
// Height Decimal Number Unit Selector Text Field End 1 //
// -------------------------------------------------------------------------------------- //
// Current Weight Whole Number Text Field Start 1 //
#IBOutlet weak var CurrentWeightWholeNumberTextField: UITextField!
// Current Weight Whole Number Text Field Start 1 //
// -------------------------------------------------------------------------------------- //
// Current Weight Whole Number Unit Selector Text Field Start 1 //
#IBOutlet weak var CurrentWeightWholeNumberUnitSelectorTextField: UITextField!
var CurrentWeightWholeNumberUnitSelectorData = ["kg", "lb"]
var CurrentWeightWholeNumberUnitSelectorPicker = UIPickerView()
// Current Weight Whole Number Unit Selector Text Field Start 1 //
// -------------------------------------------------------------------------------------- //
// Current Weight Decimal Number Text Field Start 1 //
#IBOutlet weak var CurrentWeightDecimalNumberTextField: UITextField!
// Current Weight Decimal Number Text Field End 1 //
// -------------------------------------------------------------------------------------- //
// Current Weight Decimal Number Unit Selector Text Field Start 1 //
#IBOutlet weak var CurrentWeightDecimalNumberUnitSelectorTextField: UITextField!
var CurrentWeightDecimalNumberUnitSelectorData = ["g", "oz"]
var CurrentWeightDecimalNumberUnitSelectorPicker = UIPickerView()
// Current Weight Decimal Number Unit Selector Text Field End 1 //
// -------------------------------------------------------------------------------------- //
// Current Fitness Goal Selector Text Field Start 1 //
#IBOutlet weak var CurrentFitnessGoalSelectorTextField: UITextField!
var CurrentFitnessGoalSelectorData = ["Lose Fat", "Gain Muscle", "Maintain Physique"]
var CurrentFitnessGoalSelectorPicker = UIPickerView()
// Current Fitness Goal Selector Text Field End 1 //
// -------------------------------------------------------------------------------------- //
// Weekly Activity Level Text Field Start 1 //
#IBOutlet weak var WeeklyActivityLevelTextField: UITextField!
var WeeklyActivityLevelTextFieldData = ["I am sedentary (little or no exercise)", "I am lightly active (light exercise/sports 1-3 days per week)", "I am moderately active (moderate exercise/sports 3-5 days per week)", "I am very active (hard exercise/sports 6-7 days a week)", "I am extra active (very hard exercise/sports & physical job or 2 x training"]
var WeeklyActivityLevelSelectorPicker = UIPickerView()
// Weekly Activity Level Text Field End 1 //
// -------------------------------------------------------------------------------------- //
// First Meal Time Text Field Start 1 //
#IBOutlet weak var FirstMealTimeTextField: UITextField!
#IBAction func FirstMealTimeTextField(FirstMealTimeSender: UITextField) {
let FirstMealTimePicker = UIDatePicker()
FirstMealTimeSender.inputView = FirstMealTimePicker
FirstMealTimePicker.datePickerMode = UIDatePickerMode.Time
FirstMealTimePicker.addTarget(self, action: "FirstMealTimeChanged:", forControlEvents: .ValueChanged)
}
func FirstMealTimeChanged(FirstMealTimesender: UIDatePicker) {
let FirstMealtimeFormatter = NSDateFormatter()
FirstMealtimeFormatter.dateFormat = "HH:mm"
FirstMealTimeTextField.text = FirstMealtimeFormatter.stringFromDate(FirstMealTimesender.date)
}
// First Meal Time Text Field End 1 //
// -------------------------------------------------------------------------------------- //
// Last Meal Time Text Field Start 1 //
// Last Meal Time Text Field End 1 //
// -------------------------------------------------------------------------------------- //
// Close keyboard start //
func closekeyboard() {
self.view.endEditing(true)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
closekeyboard()
}
// Close keyboard end //
// -------------------------------------------------------------------------------------- //
override func viewDidLoad() {
super.viewDidLoad()
// -------------------------------------------------------------------------------------- //
// DOB Selector From Text Field Start 2 //
DOBSelectorTextField.delegate = self
// DOB Selector From Text Field End 2 //
// -------------------------------------------------------------------------------------- //
// Gender Selector From Text Field Start 2 //
GenderSelectorPicker.delegate = self
GenderSelectorPicker.dataSource = self
GenderSelectorTextField.inputView = GenderSelectorPicker
// Gender Selector From Text Field End 2 //
// -------------------------------------------------------------------------------------- //
// Height Whole Number Unit Selector Text Field Start 2 //
HeightWholeNumberUnitSelectorPicker.delegate = self
HeightWholeNumberUnitSelectorPicker.dataSource = self
HeightWholeNumberUnitSelectorTextField.inputView = HeightWholeNumberUnitSelectorPicker
// Height Whole Number Unit Selector Text Field End 2 //
// -------------------------------------------------------------------------------------- //
// Height Decimal Unit Selector Text Field Start 2 //
HeightDecimalNumberUnitSelectorPicker.delegate = self
HeightDecimalNumberUnitSelectorPicker.dataSource = self
HeightDecimalNumberUnitSelectorTextField.inputView = HeightDecimalNumberUnitSelectorPicker
// Height Decimal Unit Selector Text Field End 2 //
// -------------------------------------------------------------------------------------- //
// Current Weight Whole Number Unit Selector Text Field Start 2 //
CurrentWeightWholeNumberUnitSelectorPicker.delegate = self
CurrentWeightWholeNumberUnitSelectorPicker.dataSource = self
CurrentWeightWholeNumberUnitSelectorTextField.inputView = CurrentWeightWholeNumberUnitSelectorPicker
// Current Weight Whole Number Unit Selector Text Field End 2 //
// -------------------------------------------------------------------------------------- //
// Current Weight Decimal Unit Selector Text Field Start 2 //
CurrentWeightDecimalNumberUnitSelectorPicker.delegate = self
CurrentWeightDecimalNumberUnitSelectorPicker.dataSource = self
CurrentWeightDecimalNumberUnitSelectorTextField.inputView = CurrentWeightDecimalNumberUnitSelectorPicker
// Current Weight Decimal Unit Selector Text Field End 2 //
// -------------------------------------------------------------------------------------- //
// Current Fitness Goal Selector Text Field Start 2 //
CurrentFitnessGoalSelectorPicker.delegate = self
CurrentFitnessGoalSelectorPicker.dataSource = self
CurrentFitnessGoalSelectorTextField.inputView = CurrentFitnessGoalSelectorPicker
// Current Fitness Goal Selector Text Field End 2 //
// -------------------------------------------------------------------------------------- //
// Weekly Activity Level Selector Text Field Start 2 //
WeeklyActivityLevelSelectorPicker.delegate = self
WeeklyActivityLevelSelectorPicker.dataSource = self
WeeklyActivityLevelTextField.inputView = WeeklyActivityLevelSelectorPicker
// Weekly Activity Level Selector Text Field End 2 //
// -------------------------------------------------------------------------------------- //
// First Meal Time Text Field Start 2 //
FirstMealTimeTextField.delegate = self
// First Meal Time Text Field End 2 //
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// -------------------------------------------------------------------------------------- //
// Selectors Start 1 //
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView == GenderSelectorPicker {
output = 1
} else if pickerView == HeightWholeNumberUnitSelectorPicker {
output = 2
} else if pickerView == HeightDecimalNumberUnitSelectorPicker {
output = 3
} else if pickerView == CurrentWeightWholeNumberUnitSelectorPicker {
output = 4
} else if pickerView == CurrentWeightDecimalNumberUnitSelectorPicker {
output = 5
} else if pickerView == CurrentFitnessGoalSelectorPicker {
output = 6
} else if pickerView == WeeklyActivityLevelSelectorPicker {
output = 7
}
switch output{
case 1:
return GenderSelectorData.count
case 2:
return HeightWholeNumberUnitSelectorData.count
case 3:
return HeightDecimalNumberUnitSelectorData.count
case 4:
return CurrentWeightWholeNumberUnitSelectorData.count
case 5:
return CurrentWeightDecimalNumberUnitSelectorData.count
case 6:
return CurrentFitnessGoalSelectorData.count
case 7:
return WeeklyActivityLevelTextFieldData.count
default:
return 1
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView == GenderSelectorPicker {
output2 = 1
} else if pickerView == HeightWholeNumberUnitSelectorPicker {
output2 = 2
} else if pickerView == HeightDecimalNumberUnitSelectorPicker {
output2 = 3
} else if pickerView == CurrentWeightWholeNumberUnitSelectorPicker {
output2 = 4
} else if pickerView == CurrentWeightDecimalNumberUnitSelectorPicker {
output2 = 5
} else if pickerView == CurrentFitnessGoalSelectorPicker {
output2 = 6
} else if pickerView == WeeklyActivityLevelSelectorPicker {
output2 = 7
}
switch output2{
case 1:
return GenderSelectorData[row]
case 2:
return HeightWholeNumberUnitSelectorData[row]
case 3:
return HeightDecimalNumberUnitSelectorData[row]
case 4:
return CurrentWeightWholeNumberUnitSelectorData[row]
case 5:
return CurrentWeightDecimalNumberUnitSelectorData[row]
case 6:
return CurrentFitnessGoalSelectorData[row]
case 7:
return WeeklyActivityLevelTextFieldData[row]
default:
return ""
}
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == GenderSelectorPicker {
GenderSelectorTextField.text = GenderSelectorData[row]
} else if pickerView == HeightWholeNumberUnitSelectorPicker {
HeightWholeNumberUnitSelectorTextField.text = HeightWholeNumberUnitSelectorData[row]
} else if pickerView == HeightDecimalNumberUnitSelectorPicker {
HeightDecimalNumberUnitSelectorTextField.text = HeightDecimalNumberUnitSelectorData[row]
} else if pickerView == CurrentWeightWholeNumberUnitSelectorPicker {
CurrentWeightWholeNumberUnitSelectorTextField.text = CurrentWeightWholeNumberUnitSelectorData[row]
} else if pickerView == CurrentWeightDecimalNumberUnitSelectorPicker {
CurrentWeightDecimalNumberUnitSelectorTextField.text = CurrentWeightDecimalNumberUnitSelectorData[row]
} else if pickerView == CurrentFitnessGoalSelectorPicker {
CurrentFitnessGoalSelectorTextField.text = CurrentFitnessGoalSelectorData[row]
} else if pickerView == WeeklyActivityLevelSelectorPicker {
WeeklyActivityLevelTextField.text = WeeklyActivityLevelTextFieldData[row]
}
}
}
First of all - creating a textfield in a textfield delegate callback doesn't make any sense. I see IBOutlet declaration so I assume you are using storyboards.
If that is so, then you already have all the textfield instances in your scene.
Just create an outlet from EACH textfield to the controller so that you can reference them in code if necessary.
Next, all the textfield will call the same delegate or target/action methods. So inside EACH of those methods, you need to find out WHICH textfield is calling it and deal with it particularly.
func textFieldDidBeginEditing(textfield: UITextField) {
if textField == self.textField1 {
//do something to textfield1
}
else if textfield == textfield2 {
//do something to textfield2
}
etc....
}
I had the same exact issue and went down the same exact path you did. Using the resolution Earl Grey gave, I was able to resolve my conflict. Take a look at my code and you should be able to figure yours out. My best advice to you is to not think about the fields as two separate bits of code like you've done, but rather, one bit of code with the use of IF statements.
import UIKit
class AddEventViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var eventStartDateTimeText: UITextField!
#IBOutlet var eventEndDateTimeText: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
eventStartDateTimeText.delegate = self
eventEndDateTimeText.delegate = self
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: TextField Delegate
func datePickerChanged(sender: UIDatePicker) {
let formatter = DateFormatter()
formatter.dateStyle = .full
formatter.timeStyle = .short
eventStartDateTimeText.text = formatter.string(from: sender.date)
}
func datePickerChangedEnd(sender: UIDatePicker) {
let formatter = DateFormatter()
formatter.dateStyle = .full
formatter.timeStyle = .short
eventEndDateTimeText.text = formatter.string(from: sender.date)
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == self.eventStartDateTimeText {
let datePicker = UIDatePicker()
textField.inputView = datePicker
datePicker.addTarget(self, action: #selector(datePickerChanged(sender:)), for: .valueChanged)
} else if textField == self.eventEndDateTimeText {
let datePicker = UIDatePicker()
textField.inputView = datePicker
datePicker.addTarget(self, action: #selector(datePickerChangedEnd(sender:)), for: .valueChanged)
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if textField == self.eventStartDateTimeText {
eventStartDateTimeText.resignFirstResponder()
} else if textField == self.eventEndDateTimeText {
eventEndDateTimeText.resignFirstResponder()
}
return true
}
// MARK: Helper Methods
func closekeyboard() {
self.view.endEditing(true)
}
// MARK: Touch Events
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
closekeyboard()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
I have two UILabels with two UITapGestureRecognizers in a UITableViewCell.
cell.Username.tag = indexPath.row
cell.SharedUser.tag = indexPath.row
let tapGestureRecognizer2 = UITapGestureRecognizer(target:self, action:"GoToProfil:")
let tapGestureRecognizer3 = UITapGestureRecognizer(target:self, action:"GoToProfil:")
cell.Username.userInteractionEnabled = true
cell.Username.addGestureRecognizer(tapGestureRecognizer2)
cell.SharedUser.userInteractionEnabled = true
cell.SharedUser.addGestureRecognizer(tapGestureRecognizer3)
func GoToProfil (sender: AnyObject!) {
self.performSegueWithIdentifier("GoToProfilSegue", sender: sender)
}
I'm using a Segue to push another UIViewController, and I'm overriding the PrepareSegue function to send the needed information corresponding to the Sender tag.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let ProfilView = segue.destinationViewController as! Profil
ProfilView.hidesBottomBarWhenPushed = true
ProfilView.title = posts[sender.view!.tag].User?.objectForKey("Name") as? String
ProfilView.User = posts[sender.view!.tag].User
}
My problem is that I want to know which UILabel was pressed, knowing that I'm already using tag.
Your GoToProfile: function should be written properly. The parameter isn't the "sender", it's the gesture recognizer.
func GoToProfil (gestureRecognizer: UITapGestureRecognizer) {
}
From there, you can determine the label by using the view property of the gesture recognizer.
But you seem to have two conflicting requirements. You want to know which of the two labels was tapped and you want to know which row the label is in.
Normally you would use the label's tag to know which of the two labels was tapped. But you are using their tags to track the row.
The solution I recommend is to use the tag to differentiate the two labels. Then you can calculate the row based on the frame of the label.
See the following answer for sample code that translates the frame of a cell's subview to the cell's indexPath.
Making the following assumptions:
You are trying to uniquely identify the label using UIView.tag
You want different behaviour for Username & SharedUser
I recommend the following, first define your tags below your #imports
#define kUsername 1
#define kSharedUser 2
Then assign them to your views
cell.Username.tag = kUsername
cell.SharedUser.tag = kSharedUser
Then in your prepareSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
int tag = [sender.view!.tag]
if (tag == kUsername) {
//Username logic
} else if(tag == kSharedUser) {
//Shared User Logic
}
}
This way you can easily and simply determine tap, Note this might have different results if you have more then 1 Username & SharedUser labels. Then you will either need more #defines or change how you generate your tags.
You can add a property to UILabel to track the label's type. (I used an enum since there's just 2 cases, but it could be a string, etc.)
enum LabelDest : String
{
case Username = "Username"
case SharedUser = "SharedUser"
}
extension UILabel
{
struct Static {
static var key = "labelDest"
}
var labelDest:LabelDest? {
set { objc_setAssociatedObject( self, &Static.key, newValue?.rawValue, .OBJC_ASSOCIATION_COPY_NONATOMIC )
}
get {
guard let val = objc_getAssociatedObject( self, &Static.key ) as? String else { return nil }
return LabelDest( rawValue:val )
}
}
}
Now you can just do this:
let label = UILabel()
label.labelDest = .Username
Later:
switch label.labelDest
{
case .Some(.Username):
// handle user name
break
...
If you want to use the .tag field on your labels you can use a different technique to find the table row associated with a label: (again using class extensions)
extension UIView
{
var enclosingTableViewCell:UITableViewCell? {
return superview?.enclosingTableViewCell
}
var enclosingTableView:UITableView? {
return superview?.enclosingTableView
}
}
extension UITableViewCell
{
var enclosingTableViewCell:UITableViewCell? {
return self
}
}
extension UITableView
{
var enclosingTableView:UITableView? {
return self
}
}
extension UIView {
var tableRow:Int? {
guard let cell = self.enclosingTableViewCell else { return nil }
return self.enclosingTableView?.indexPathForCell( cell )?.row
}
}
Now, from your gesture recognizer action:
func goToProfil( sender:UIGestureRecognizer! )
{
guard let tappedRow = sender.view?.tableRow else { return }
// handle tap here...
}
You can access the sender data, and read the tag of the object that send you, like in this sample code.
To uniquely identify each row and each label, you can use something like this:
cell.Username.tag = (indexPath.row*2)
cell.SharedUser.tag = (indexPath.row*2)+1
With this, if you have a even tag, its the Username, odd will be the SharedUser. Dividing by the floor of 2 you can have the row back.
#IBOutlet weak var test1: UILabel!
#IBOutlet weak var test2: UILabel!
override func viewWillAppear(animated: Bool) {
test1.tag = 1
test2.tag = 2
test1.userInteractionEnabled = true
test2.userInteractionEnabled = true
self.test1.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "handleSingleTap:"))
self.test2.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "handleSingleTap:"))
}
func handleSingleTap(sender: UITapGestureRecognizer) {
print(sender.view?.tag)
}