I'm making a GPA calculator using swift and I've run into some problems. I created a picker view with all the letter grades (A to F). I've put 7 text fields (one for each course). I want the user to tap on a textfield and the pickerview should appear, and the grade selected would appear in the textfield. I've managed to do that for the first textfield but I dont know how to write the code that allows me to pick the grade for each textfield. I've tried different methods but I always end up changing the first textfield only.
Any help is appreciated :-)
Here is my code (gradeOne is a textfield for course 1, gradeTwo for course 2, etc.):
import UIKit
class GPA: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet var gradeOne: UITextField!
#IBOutlet var gradeTwo: UITextField!
//Grades array
var grades = ["A+/A (85-100)", "A- (80-84)", "B+ (77-79)", "B (73-76)", "B- (70-72)", "C+ (67-69)", "C (63-66)", "C- (60-62)", "D+ (57-59)", "D (53-56)", "D- (50-52)", "F (0-49)"]
//Grade 1 Picker
func numberOfComponentsInPickerView(gradePickerView: UIPickerView) -> Int {
return 1
}
func pickerView(gradePickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return grades.count
}
func pickerView(gradePickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return grades[row]
}
func pickerView(gradePickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
gradeOne.text = grades[row]
}
//Make keyboard disappear
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let gradePickerView = UIPickerView()
gradePickerView.delegate = self
gradeOne.inputView = gradePickerView
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
//Causes the view (or one of its embedded text fields) to resign the first responder status.
view.endEditing(true)
}
}
You should set gradePickerView as the inputView for all your textfields
gradeTwo.inputView = gradePickerView
gradeThree.inputView = gradePickerView
// set for rest of textfields
and then in your pickerView:didSelectRow: method you need to set the text to the active text field at the time. I suggest setting the active textfield using UITextFieldDelegate
var activeField: UITextField?
override func viewDidLoad() {
// Set VC as textfield's delegate
gradeTwo.delegate = self
gradeThree.delegate = self
// Set for rest of textfields
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
activeField = textField
}
func pickerView(gradePickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
activeField.text = grades[row]
}
Use the delegate for UITextField to set all inputviews to pickerView. Make sure you set delegate for all textfields
let gradePickerView = UIPickerView()
override func viewDidLoad() {
super.viewDidLoad()
// set delegates for all textfields
gradePickerView.delegate = self
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tap)
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
textField.inputView = gradePickerView
}
I think you want to tap on 7 textfiel. Each time the pickerView will show. You picker a cell in pickerView and the textfiel(that you just tap) will show your select? If it's true. You should do fowllow this step:
1: Add tag to each textFiel: yourtextFiel.view.tag = 0 (or 1,2,3,4,...)
2: When show pickerView: Add pickerView.tag = yourtextFiel.view.tag
3: in fucion pickerView didSelectRow : get your textFiel's tag will show your selected: let mytag = pickerView.tag.
Find your textFiel with "mytag" and change your text
Googluck
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I have a problem when I tried to read data from my UITextFields.
I have 3 IBOutlets for textFields (Sex, Goal and number of Meals per day). Data for all of that textFields will be displayed by UIPickerView. I Prepared 3 dataSets, different for all textFields. When I tap on 1st textField (Sex) I have PickerView with Data (Male and Female), after that when I click on 2nd textField (Goal) dataSet on UIPickerView wasn't reloaded but, when I tapped on 3rd TextField (number of meals) pickerView data is reloaded with data for 2nd textField. When I choose something from that pickerView 2nd textFieled are setted (after tapping on 3rd textField!). It always work like the same.
Any idea?
You can set a pickerView as the textField.inputView.
The example below is for two text fields sharing a single picker view, but you can extend it easily by checking the identity of the activeTextField in the picker view delegate methods.
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var myTextFieldOne: UITextField!
#IBOutlet weak var myTextFieldTwo: UITextField!
let pickerDataOne = ["A", "B", "C", "D", "E", "F"]
let pickerDataTwo = ["1", "2", "3", "4", "5", "6"]
// Holds a reference to current text field
var activeTextField = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
let myPicker = UIPickerView()
myPicker.delegate = self
myPicker.dataSource = self
myTextFieldOne.inputView = myPicker
myTextFieldTwo.inputView = myPicker
}
// MARK: UITextField Delegate
func textFieldDidBeginEditing(textField: UITextField!) {
// Sets tapped textField to active one
self.activeTextField = textField
if let picker = self.activeTextField.inputView as? UIPickerView {
// This is one way to get a reference to the picker view
picker.reloadAllComponents()
}
}
// MARK: UIPickerView Delegate
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
// You can have a different data source for each text field
if activeTextField === myTextFieldOne {
return pickerDataOne.count
} else if activeTextField === myTextFieldTwo {
return pickerDataTwo.count
}
}
func pickerView( _ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
// Gets the title for each row in picker
if activeTextField === myTextFieldOne {
return pickerDataOne[row]
} else if activeTextField === myTextFieldTwo {
return pickerDataTwo[row]
}
}
func pickerView( _ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// Populates the text field
if activeTextField === myTextFieldOne {
myTextFieldOne.text = pickerDataOne[row]
} else if activeTextField === myTextFieldTwo {
myTextFieldTwo.text = pickerDataTwo[row]
}
}
}
You can then access the text in any text field by getting myTextFieldOne.text for example.
Have a look at the UITextView delegate. You can use textFieldDidBeginEditing(_ textField: UITextField) to detect if text field has been selected (became first responder).
Then use the textField parameter to detect which text field has been selected and load picker data based on that.
I have a UIPickcerView placed on my Xib. when I run the app pickerView is showing on the bottom of screen, no matter where I place it on the Xib its always showing in the bottom of the screen.
Here as you can see its placed on top of screen.
But when I run the app,its on the bottom
Here is my code so far
func setupPIckerView(){
self.pickerView.dataSource = self
self.pickerView.delegate = self
pickerView.removeFromSuperview()
countryTextField.inputView = pickerView
}
// MARK: Delegate Methods
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
{
return "Text"
}
// MARK: Data Source Methods
func numberOfComponentsInPickerView(colorPicker: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return 20
}
What am I missing here ?
You are getting pickerView always down because you have set the pickerView as an inputView of countryTextField. Change your setupPIckerView like this
func setupPIckerView(){
self.pickerView.dataSource = self
self.pickerView.delegate = self
}
Add the delegate method of UITextField
func textFieldDidBeginEditing(textField: UITextField) {
if (textField == self.countryTextField) {
textField.inputView = UIView()
self.pickerView.hidden = false
}
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
if (textField == self.countryTextField) {
self.pickerView.hidden = true
}
textField.resignFirstResponder()
return true
}
Hope this will help you.
Set the UIpickerview frame programatically with reference to your text field.
pickerView.frame = CGRectMake(0,textfield.frame.origin.y+20,self.view.frame.size.width,300);
because you used the textField's inputView, the textField's inputView is always showing on the bottom of the screen.
If you want to show the inputView on other locations, you can implement a CustomView: UIView to replace the inputView, then you can show the view's location on your custom location.
I have a ViewController with a UIPickerView as a single control myPickerView which is of a class MyPickerView which I created as a sub-class of UIPickerView. I invoke myPickerView in ViewController viewDidLoad by myPickerView.viewDidLoad. However, this does not execute the source functions of MyPickerView.
I need a clarification of how I can make this work. My reason for MyPickerView is that it has a lot of special code that I did not want to clutter up the main ViewController. See the example code below:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myPickerView: MyPickerView!
override func viewDidLoad() {
super.viewDidLoad()
myPickerView.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
import UIKit
var gSep = ","
class MyPickerView: UIPickerView , UIPickerViewDataSource, UIPickerViewDelegate {
var pickerData = [[" "],[" "]]
var category = [""]
var subCategory = [""]
var dictMenuList = [String:String]()
//MARK:- category/subcategory picker
func viewDidLoad() {
println("MyPickerView: viewDidLoad")
dictMenuList = ["Medical":"Sub-Cat 1.1,Sub-Cat 1.2,Sub-Cat 1.3,Sub-Cat 1.4,Sub-Cat 1.5,Sub-Cat 1.6,Sub-Cat 1.7",
"Taxes": "Sub-Cat 2.1,Sub-Cat 2.2,Sub-Cat 2.3,Sub-Cat 2.4",
"Bills": "Sub-Cat 3.1,Sub-Cat 3.2,Sub-Cat 3.3,Sub-Cat 3.4,Sub-Cat 3.5,Sub-Cat 3.6,Sub-Cat 3.7"]
println("MyPickerView dictMenuList: \(dictMenuList)")
self.reloadAllComponents()
let firstKey = self.loadPickerWithCategory(0)
self.loadPickerWithSubCategory(firstKey)
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
println("MyPickerView: numberOfComponentsInPickerView \(pickerData.count)")
return pickerData.count
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerData[component].count
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if component == 0 {
let selectedKey = category[row]
loadPickerWithSubCategory(selectedKey)
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
return pickerData[component][row]
}
func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView!) -> UIView
{
var pickerLabel = UILabel()
pickerLabel.textColor = UIColor.blackColor()
pickerLabel.text = pickerData[component][row]
pickerLabel.font = UIFont(name: pickerLabel.font.fontName, size: 17)
pickerLabel.textAlignment = NSTextAlignment.Center
return pickerLabel
}
func loadPickerWithCategory (row: Int) -> String{
println("loadPickerWithCategory")
category = [String](dictMenuList.keys)
println("MyPickerView: category: \(category)")
if category.isEmpty {
return ""
}
let n1 = dictMenuList.count
pickerData[0].removeAll(keepCapacity: true)
for i in 0 ..< n1
{
pickerData[0].append(category[i])
}
return category[row]
}
func loadPickerWithSubCategory (key: String) {
println("MyPickerView: loadPickerWithSubCategory")
let x = dictMenuList[key]
subCategory = x!.componentsSeparatedByString(gSep)
let n1 = subCategory.count
pickerData[1].removeAll(keepCapacity: true)
if subCategory.isEmpty {
return
}
for i in 0 ..< n1
{
pickerData[1].append(subCategory[i])
}
self.reloadAllComponents()
}
}
The method viewDidLoad is a view controller method, not a view method. A UIPickerView is a subclass of UIView, not UIViewController, so the system will not call your viewDidLoad method.
You need to override one or more of the init methods.
If you're loading your picker view from a Storyboard or XIB, you probably want to override initWithCoder.
If you're creating your picker in code, you probably want to override initWithFrame.
I sometimes create a method setup that I call from both initWithCoder: and from initWithFrame:. That way my setup code gets called regardless of how the view object is loaded.
I vaguely remember reading that there is a better way of handling this dueling initializers problem in Swift, but I don't remember what it is. (I'm still learning Swift.)
EDIT:
It just occurs to me that you can use the method awakeFromNib to do setup after your view has been loaded and all of it's outlets are set up. That's roughly equivalent to the viewDidLoad call for view controllers. I should have thought of that sooner.
(awakeFromNib is a method of NSObject, so it's a bit hard to find if you don't know it exists.)
First of all viewDidLoad() is a method of the UIViewController class and is called after the controller's view is loaded into the memory. Read more here. You can not use it in views.
So you should implement an init method inside your custom picker class. I'd recommend to override initWithFrame and initWithCoder and set up your component there.
And you will initialize your custom picker like this:
myPickerView = MyPickerView(frame: yourFrame)
I hope the title makes some sense but what I am trying to do is to set a field value to the item selected variable from my dataPicker. I have been able to make this work when there is only one field to set but my project will have multiple fields on each view that will call data from the dataPicker based on what field it is. I hope that is clear. Maybe as you look at the code it will.
I have set up a test project to limit things to this issue only. So my variable to tell the view what array to populate in the dataPicker is either season or sport. the field that will receive the data from the season/sport array is enterSeason and enterSport. When the picker has returned a value from season, I want to combine it with enter to create the var enterSeason to set that == itemSelected. This language is very new to me so I am trying the only way I have used before to combine text and variables in one value. It is obviously not working. Help is appreciated.
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UIGestureRecognizerDelegate {
#IBOutlet var enterSeason: UITextField!
#IBOutlet var enterSport: UITextField!
var dataPickerView = UIPickerView()
var season = ["2013", "2014", "2015"] //multi-season
//var season = ["2015"] //single-season
var sport = ["Baeball", "Football", "Basketball", "Hokey"]
var activeField = []
override func viewDidLoad() {
super.viewDidLoad()
enterSeason.inputView = dataPickerView
dataPickerView.delegate = self
dataPickerView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return activeField.count
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
return activeField[row] as! String
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
var itemSelected = activeField[row] as! String
self.enter"\activeField".text = itemSelected
}
}
EDIT : How do you show and hide the picker? Your code anly shows variable declarations and the delegate methods... answers could vary accordingly..
Since you show the picker as text field's input view, set UITextFieldDelegate for each of these text fields .. and in the textFieldDidBeginEditing check which field becomes active with simple if else
func textFieldDidBeginEditing(textField: UITextField) {
if textField === enterSeason {
activeField = season
}
else if textField === enterSport {
activeField = sport
}
}
And in the picker selector, set value of the relevant text field as per current activeField object
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if activeField === season {
enterSeason.text = season[row] as! String
}
else if activeField === sport {
enterSeason.text = sport[row] as! String
}
}
Setting the delegate for your text fields in storboard/xib :
P.S.
- Rename activeField to activeDataArray or somethiong more appropriate
EDIT 2 : As you mentioned, second approach i have mentioned below is not suitable for you because there are too many of these fields i am still keeping it as part of the answer as it may help someone else
But what you are trying to achieve is very simple and approach is too convoluted / weird. So heres another way you can implement the whole thing..
The easiest (but still probably not the best) way is to have two instances of the UIPickerView for each field. you can directly check pickerView == seasonPickerView OR pickerView == sportPickerViewin an if else block and do the conditional programming and you wont need the activeField variable..
I am currently working on a small project and i have a viewController that has 4 textFields which 3 work ok. They take String objects. However, the 4th textField is supposed to bring up a UIPickerView with 4 selectable items.
So far this is what i have in my controller that implements this:
#IBOutlet var pickerTextfield: UITextField!
#IBOutlet var itemPicker: UIPickerView! = UIPickerView()
The pickerTextfield is the UITextField object that is the 4th field.
The itemPicker is an unlinked UIPickerView that i want to create programatically.
Right below these properties, i have an array of items for the UIPickerView object:
var seasonalItems = ["Spring", "Summer", "Fall", "Winter"]
In my viewDidLoad method i have this as follow:
itemPicker.hidden = true;
pickerTextfield.text = seasonalItems[0]
pickerTextfield.delegate = self
And the rest of the implementation:
// Below these lines is the implementation of the Picker
func numberOfComponentsInPickerView(pickerView: UIPickerView!) -> Int{
return 1
}
// returns the # of rows in each component..
func pickerView(pickerView: UIPickerView!, numberOfRowsInComponent component: Int) -> Int{
return seasonalItems.count
}
func pickerView(pickerView: UIPickerView!, titleForRow row: Int, forComponent component: Int) -> String! {
return seasonalItems[row]
}
func pickerView(pickerView: UIPickerView!, didSelectRow row: Int, inComponent component: Int)
{
pickerTextfield.text = seasonalItems[row]
itemPicker.hidden = true;
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
itemPicker.hidden = false
return false
}
So the end result from this is when i tap the pickerTextfield object in the app, it shows the first item of the array (Spring) but in text within the UITextField object but it does not show the UIPickerView object with the other selectable items where i could select one and then hide it when selected.
My question is, where or what am i doing wrong here? i been trying to figure this out on my own but i do not seem to get good clear examples with Swift and storyboards. I much rather not drag a UIPickerView in the storyboard but rather the way i attempted to implement. Thanks
You can give UIPickerView as inputView for your TextField in which you want to show picker view.
You also do not need to initially hide picker view in this case.
pickerTextfield.inputView = itemPicker
When you use UIPickerView as inputView of any UITextField then when you tap on the TextField instead of default keypad PickerView will show.