I want to keep the same placeholder text in my UITextField which is "Location". Once the UITextField is touched then I want the first value of the UIPickerView to be displayed. The way it is now the user has to scroll down then back up for the value to be displayed in the UITextField. I want the value to be displayed in the UITextField as soon as the UIPickerView is open. unselected, selected-UITextField still shows no value
override func viewDidLoad() {
let thePicker = UIPickerView()
thePicker.delegate = self
location.inputView = thePicker
thePicker.selectRow(1, inComponent: 0, animated: true)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickOptions.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return pickOptions[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
location.text = pickOptions[row]
}
Implement the textFieldDidBeginEditing text field delegate method and set the text field's text if it doesn't already have a value.
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField.text!.isEmpty {
// set the text as needed
}
}
I'm going to build on rmaddy's answer and add more nuances and answers to questions in the comments.
Main answer lays in the first extension, the rest of the code outlines class structure and components that need to be present for everything to work.
class ViewController {
private var selectedItem: String = ""
private let pickerList = [
"First item",
"Second item",
"Third item"
]
override func viewDidLoad() {
super.viewDidLoad()
pickerTextField.delegate = self
//...
// Layout setup
//...
}
private let pickerTextField: UITextField = {
let textField = UITextField()
textField.tintColor = .clear // hides cursor
return textField
}()
//...
// UI Elements including picker
//...
}
extension ViewController: UITextFieldDelegate {
internal func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == pickerTextField, let textFieldText = textField.text, textFieldText.isEmpty {
pickerTextField.text = pickerList.first // fills textfield before any selection is made
}
}
}
extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource {
//...
// PickerView delegate methods
//...
}
import UIKit
class ActivityPersonDetail : UIViewController,UITextFieldDelegate,UIPickerViewDataSource, UIPickerViewDelegate {
let salutations = ["1", "2", "3","4","5","6","7","8","9","10","11","12"]
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return salutations.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return salutations[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
pickerTF.text = salutations[row]
picker.isHidden = true
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == self.pickerTF {
picker.isHidden = false
textField.endEditing(true)
}
}
lazy var picker: UIPickerView = {
let pickerView = UIPickerView()
pickerView.delegate = self
pickerView.translatesAutoresizingMaskIntoConstraints = false
pickerView.backgroundColor = .white
pickerTF.inputView = pickerView
return pickerView
}()
let arrowImage: UIImageView = {
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.image = #imageLiteral(resourceName: "down_arrow")
image.contentMode = .scaleAspectFit
return image
} ()
lazy var pickerTF: UITextField = {
let tf = UITextField()
tf.delegate = self
tf.translatesAutoresizingMaskIntoConstraints = false
tf.textColor = .black
tf.font = .systemFont(ofSize: 24, weight: .medium)
tf.placeholder = salutations[0]
return tf
}()
//
// MARK :- viewDidLoad ============================================================================
//
private var pickerView: UIPickerView!
override func viewDidLoad() {
super .viewDidLoad()
view.backgroundColor = .white
setupNavigationBar()
setupViews()
setupAutoLayout()
}
// ================ setupNavigationBar =============
func setupNavigationBar(){
title = ""
hideBackTitle()
navigationController?.navigationBar.barTintColor = .navBar
}
func hideBackTitle() {
let backbarItem = UIBarButtonItem()
backbarItem.title = ""
navigationItem.backBarButtonItem = backbarItem
}
// ================ setupViews =============
func setupViews(){
view.addSubview(pickerTF)
pickerTF.addSubview(arrowImage)
view.addSubview(picker)
picker.isHidden = true
}
// ================ setupAutoLayout =============
func setupAutoLayout() {
pickerTF.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 80).isActive = true
pickerTF.topAnchor.constraint(equalTo: view.topAnchor, constant: 80).isActive = true
pickerTF.heightAnchor.constraint(equalToConstant: 60).isActive = true
pickerTF.widthAnchor.constraint(equalToConstant: 70).isActive = true
arrowImage.rightAnchor.constraint(equalTo: pickerTF.rightAnchor, constant: -15).isActive = true
arrowImage.topAnchor.constraint(equalTo: pickerTF.topAnchor, constant: 16).isActive = true
arrowImage.widthAnchor.constraint(equalToConstant: 22).isActive = true
arrowImage.heightAnchor.constraint(equalToConstant: 22).isActive = true
picker.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
picker.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
picker.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
picker.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
}
#IBOutlet weak var tF: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
tF.delegate = self
}
func textFieldDidEndEditing(_ textField: UITextField) {
textField.text = pickOptions[row]
}
Related
I have a problem with Xcode and Swift.
In scenario, I have three uitextfields and one uipickerview, the data of uipickerviews change for each textfield, and that work fine, but the problem is that for display the first uipickerview i must click on the first uitextfield then on UIView then again on uitextfield for display a uipickerview, then for display the picker for second uitextfield, i had to click on the second uitextfield then on UIView then again on the second uitextfield for display uipickerview, and the same for the third uitextfield (uitextfield then UIView then uitextfield) for display the uipickerview, each uitextfield have its datasource of uipickerview different from the other.
This is the short code:
var level = [String]()
let pickerSchool = UIPickerView()
var lastPressedTextField: UITextField?
var currentIndex = 0
let toolBar = UIToolbar()
#IBOutlet weak var cityTextField: UITextField!
#IBOutlet weak var levelSchoolTextField: UITextField!
#IBOutlet weak var schoolTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
toolBar.sizeToFit()
let buttonDone = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(closePicker))
toolBar.setItems([buttonDone], animated: true)
}
#IBAction func textFieldDidBeginEditing(_ textField: UITextField) {
lastPressedTextField = textField
lastPressedTextField?.inputAccessoryView = toolBar
lastPressedTextField!.inputView = pickerSchool
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if lastPressedTextField == cityTextField {
return RequestService.gettenCity[row]
} else if lastPressedTextField == levelSchoolTextField {
return level[row]
} else if lastPressedTextField == schoolTextField {
return RequestService.gettenSchool[row]
}
return ""
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if lastPressedTextField == cityTextField {
return RequestService.gettenCity.count
} else if lastPressedTextField == levelSchoolTextField {
return level.count
} else if lastPressedTextField == schoolTextField {
return RequestService.gettenSchool.count
}
return 0
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
currentIndex = row
if lastPressedTextField == cityTextField {
self.cityTextField.text = RequestService.gettenCity[row]
}
else if lastPressedTextField == levelSchoolTextField {
self.levelSchoolTextField?.text = level[row]
}
else {
self.schoolTextField.text = RequestService.gettenSchool[row]
}
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
pickerSchool.isHidden = false
textField.inputView = pickerSchool;
return false
}
func textFieldDidEndEditing(_ textField: UITextField) {
pickerSchool.isHidden = true
}
#objc func closePicker(){
if lastPressedTextField == cityTextField {
lastPressedTextField?.text = RequestService.gettenCity[currentIndex]
} else if lastPressedTextField == levelSchoolTextField {
// lastPressedTextField?.text = level[currentIndex]
//
let newParameters : Parameters = [
"city": cityTextField.text!,
"level" : levelSchoolTextField.text!
]
api = URL(string: "http://localhost/mesamies/getschools.php")
repository.schoolSelect(url: api!, method: .post, parameters: newParameters) { dataResponse in
switch dataResponse {
case .success(let schools):
let school = schools.count
for i in 0...school-1{
RequestService.gettenSchool.append(schools[i].name!)
RequestService.gettenSchoolId.append(Int(schools[i].id!)!)
}
case .failure(let error):
print(error)
}
} //
}
else if lastPressedTextField == schoolTextField {
schoolTextField?.text = RequestService.gettenSchool[currentIndex]
}
lastPressedTextField?.resignFirstResponder()
view.endEditing(true)
}
If you want see all the code, this is the link on github:
https://github.com/mdolwan/Projet_12/blob/master/MesAmies/Controllers/SettingViewController.swift
Please review How to Ask -- when asking a question, you should post only the code necessary to clearly demonstrate the issue.
However, taking a look at your complete project...
In your SettingViewController class, you have this function:
#IBAction func textFieldDidBeginEditing(_ textField: UITextField) {
lastPressedTextField = textField
lastPressedTextField?.inputAccessoryView = toolBar
lastPressedTextField!.inputView = pickerSchool
}
connected to each text field's Editing Did End event.
You almost certainly want it to be called on Editing Did Begin.
I'm a complete beginner to swift & iOS dev in general, so be easy on me :)
In my app, I have a horizontal StackView.
Within that StackView - I have a label and a button, and now I would like to add a PickerView that would be populated from some list of options.
I've been googling and reading threads, but the closest I've gotten was getting the PickerView to show its position (using some background color) but with no actual values inside.
This is the code where I create and customize my StackView's components:
class SingleReportInputStackView: UIStackView {
... // creating and customizing my StackView
private func getObjects() -> (UILabel, UIButton, UIPickerView) {
let myLabel: UILabel = {
... // creating UILabel
}()
let myButton: UIButton = {
... // creating UIButton
}()
class MyPicker: NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
let dataArray = ["English", "Maths", "History", "German", "Science"]
let UIPicker: UIPickerView = UIPickerView()
override init() {
super.init()
self.UIPicker.delegate = self
self.UIPicker.dataSource = self
self.UIPicker.backgroundColor = UIColor.white
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return dataArray.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
let row = dataArray[row]
return row
}
}
let myPicker = MyPicker()
return (myLabel, myButton, myPicker.UIPicker)
}
...
}
Then, I add those components to my Horizontal StackView by calling setupSingleInput():
class SingleReportInputStackView: UIStackView {
...
private func setupSingleInput() {
let (myLabel, myButton, myPicker) = getObjects()
self.addArrangedSubview(myLabel)
self.addArrangedSubview(myButton)
self.addArrangedSubview(myPicker)
self.translatesAutoresizingMaskIntoConstraints = false
}
...
}
As I've said, I can see the label, the button and the PickerView's white background (looks like an empty, white rectangle).
BTW, I don't have a storyboard (if that wasn't obvious already) - I'm creating the UI programatically.
Can someone help me out? Why is my PickerView not being properly populated by my dataArray?
I'm a complete beginner to swift & iOS dev in general ...
I'd recommend starting a bit simpler... embedding a class inside a func is almost certainly not the way to go here.
The biggest problem is that you create an instance of MyPicker inside your getObjects() func, but then you return a UI element from that class, and the class instance goes away -- it goes out of scope:
private func getObjects() -> (UILabel, UIButton, UIPickerView) {
// ... all the stuff you're doing in here
let myPicker = MyPicker()
// as soon as you return, myPicker no longer exists!!!
return (myLabel, myButton, myPicker.UIPicker)
}
So, you have returned a UIPickerView, but it no longer has any code (its Delegate and DataSource) backing it.
Here's a quick modification:
class SingleReportInputStackView: UIStackView {
private var myPicker: MyPicker!
override init(frame: CGRect) {
super.init(frame: frame)
setupSingleInput()
}
required init(coder: NSCoder) {
super.init(coder: coder)
setupSingleInput()
}
private func setupSingleInput() {
let (myLabel, myButton) = getObjects()
myPicker = MyPicker()
self.addArrangedSubview(myLabel)
self.addArrangedSubview(myButton)
self.addArrangedSubview(myPicker.UIPicker)
self.translatesAutoresizingMaskIntoConstraints = false
}
private func getObjects() -> (UILabel, UIButton) {
let myLabel: UILabel = {
let v = UILabel()
v.text = "The Label"
return v
}()
let myButton: UIButton = {
let v = UIButton(type: .system)
v.setTitle("The Button", for: [])
return v
}()
return (myLabel, myButton)
}
private class MyPicker: NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
let dataArray = ["English", "Maths", "History", "German", "Science"]
let UIPicker: UIPickerView = UIPickerView()
override init() {
super.init()
self.UIPicker.delegate = self
self.UIPicker.dataSource = self
self.UIPicker.backgroundColor = UIColor.white
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return dataArray.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
let row = dataArray[row]
return row
}
}
}
and a sample view controller to try it:
class UriYakirViewController: UIViewController {
let singleReportStack = SingleReportInputStackView()
override func viewDidLoad() {
super.viewDidLoad()
// a yellow-ish background so we can see the white picker view frame
view.backgroundColor = UIColor(red: 1.0, green: 0.8, blue: 0.5, alpha: 1)
view.addSubview(singleReportStack)
singleReportStack.axis = .vertical
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
singleReportStack.centerXAnchor.constraint(equalTo: g.centerXAnchor),
singleReportStack.centerYAnchor.constraint(equalTo: g.centerYAnchor),
])
}
}
That code will give you this result:
I created PickerView, but this does not open the PickerView I created. Where do I make a mistake? Why PickerView doesn't open. I added picker view in the text field but it doesn't work. I added it to viewDidLoad, but when I click a text field, picker view does not open.
class EnergyChart: UIViewController , UIPickerViewDelegate, UIPickerViewDataSource {
var picker = UIPickerView()
var gradePickerValues1 : [String] = ["...", ...]
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return gradePickerValues1.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return gradePickerValues1[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){
getDevice.text = gradePickerValues1[row]
switch row {
case 0: break
default: break
}
self.view.endEditing(true)
}
#objc func cancelTapped() {
self.view.endEditing(true)
}
let getDevice: UITextField = {
let textFieldframe = CGRect()
let textField2 = SkyFloatingLabelTextFieldWithIcon(frame: textFieldframe, iconType: .image)
...
return textField2
}()
#objc func GetDevice() {
self.getDevice.text = self.gradePickerValues1[0]
}
override func viewDidLoad() {
super.viewDidLoad()
picker.backgroundColor = .white
picker.showsSelectionIndicator = true
picker.delegate = self
picker.dataSource = self
getDevice.inputView = picker
GetDevice()
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
toolBar.tintColor = UIColor(..)
toolBar.sizeToFit()
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "İptal", style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancelTapped))
toolBar.setItems([cancelButton, spaceButton], animated: false)
toolBar.isUserInteractionEnabled = true
getDevice.inputAccessoryView = toolBar
view.addSubview(getDevice)
getDevice.snp.makeConstraints { (make) in
make.centerX.equalTo(view).offset(50)
make.top.equalTo(view).offset(510)
make.height.equalTo(50)
make.width.equalTo(200)
}
I have two UIPickerviews that appear on one ViewController. I have tried to follow the following tutorial.
I have followed the suggestion in that I have tagged each of the UITextFields (I have 4 in total). See the the image.
Below is the code for the UIPickerViews.
extension DriverViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView.tag == 0 {
return tracks.count
} else {
return drivers.count
}
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView.tag == 0 {
return "\(tracks[row])"
} else {
return "\(drivers[row])"
}
}
The problem I am having is that the tracks array appear on all of the UITextfields.
Below is the code for each of the Arrays and the title of the UIPickerView.
override func viewDidLoad() {
super.viewDidLoad()
tracks = ["Melbourne", "Manama", "Shanghai", "Baku",
"Barcelona", "Monaco", "Montreal","Le Castellet","Spielberg",
"Silverstone","Hockenheim","Budapest","Francorchamps","Monza","Singapore","Sochi","Suzuka","Austin","Interlagos","Abu Dhabi"]
drivers = ["Lewis Hamilton","Antonio Giovinazzi","Kimi Raikkonen","Charles Leclerc","Sebastian Vettel","Romain Grosjean","Kevin Magnussen","Lando Norris",
"Carlos Sainz","Valtteri Bottas","Sergio Perez","Lance Stroll","Pierre Gasly","Max Verstappen","Nico Hulkenberg","Daniel Ricciardo","Alexander Albon","Daniil Kvyat","Robert Kubica","George Russell"]
createTrackPicker()
createDriverPicker()
createToolBar()
// Do any additional setup after loading the view.
}
func createTrackPicker() {
let trackPicker = UIPickerView()
trackPicker.delegate = self
TrackTextField.inputView = trackPicker
}
func createDriverPicker() {
let driverPicker = UIPickerView()
driverPicker.delegate = self
firstTextField.inputView = driverPicker
secondTextField.inputView = driverPicker
thirdTextField.inputView = driverPicker
}
func createToolBar() {
let toolBar = UIToolbar()
toolBar.sizeToFit()
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(ViewController.dismissKeyboard))
toolBar.setItems([doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
TrackTextField.inputAccessoryView = toolBar
firstTextField.inputAccessoryView = toolBar
secondTextField.inputAccessoryView = toolBar
thirdTextField.inputAccessoryView = toolBar
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
You forgot the tag.
func createDriverPicker() {
let driverPicker = UIPickerView()
driverPicker.tag = 1 // <—-
Hello I am trying to pop a UIPickerView view programmatically when user clicks on textfield. I have tried this but it doesn't doing anything, it isn't working. Nothing is happening when I click the textfield
class UserProfileTableViewController: UITableViewController,UITextFieldDelegate,UIPickerViewDelegate,UIPickerViewDataSource {
var itemPicker: UIPickerView! = UIPickerView()
#IBOutlet weak var genderTxtField: UITextField!
var gender = ["MALE","FEMALE"]
override func viewDidLoad() {
super.viewDidLoad()
genderTxtField.delegate = self
itemPicker!.delegate = self
itemPicker!.dataSource = self
itemPicker!.backgroundColor = UIColor.blackColor()
self.genderTxtField.inputView = itemPicker
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int{
return 1
}
// returns the # of rows in each component..
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int{
return gender.count
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return gender[row]
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
{
genderTxtField.text = gender[row]
//.hidden = true;
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
itemPicker.hidden = false
return false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The issue in your current code is in this method:
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
itemPicker.hidden = false
return false // <--- this is not letting the textField become editable
}
I would suggest removing this method entirely.
Ok so I have dug out something similar I wrote, It uses a datepicker but the general code is the same.
var datePicker: UIDatePicker!
#IBOutlet var dateFrom: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.setUpDatePicker()
}
func setUpDatePicker() {
datePicker.datePickerMode = UIDatePickerMode.Date
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: 0, height: 44))
let doneBtn = UIBarButtonItem(title: "Select", style: UIBarButtonItemStyle.Done, target: self, action: "selectDate")
let space = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
toolBar.setItems([space, doneBtn], animated: false)
YOURTEXTFIELD.inputAccessoryView = toolBar
YOURTEXTFIELD.inputView = datePicker
}
Then you add in a function to handle the done button press
func selectDate() {
let formatter = NSDateFormatter()
formatter.dateFormat = "dd-MM-yyyy";
YOURTEXTFIELD.text = formatter.stringFromDate(datePicker.date)
YOURTEXTFIELD.resignFirstResponder()
}
Just replace YOURTEXTFIELD with your version genderTxtField and change the date picker to your picker.
Dont forget to check if everything is linked up
itemPicker!.backgroundColor = UIColor.blackColor()
change to
itemPicker!.backgroundColor = UIColor.whiteColor()
then remove
func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
itemPicker.hidden = false
return false
}
good luck!
func textFieldShouldBeginEditing(textField: UITextField) -> Bool
{
itemPicker.hidden = false
return true
}
You just have to change false to true in func textFieldShouldBeginEditing.