Here's an example. I have other alerts with textfields and others with picker views but doing one with both is a pain.
let gradeTypeTextField = gradeTypeTextField (Doesn't work)
var gradeTypeTextField: UITextField!
#IBAction func newGrade(_ sender: AnyObject) {
When button is tapped, show alert.
alertForGrade()
}
var gradeType = ["Attendance", "Assignment", "Homework Assignment", "Quiz", "Test", "Mid-Term", "Exam", "Other"]
override func viewDidLoad() {
super.viewDidLoad()
Hide extra cells
tableView.tableFooterView = UIView()
}
func alertForGrade() {
Create the alert controller.
alert = UIAlertController(title: "New Grade", message: nil, preferredStyle: .alert);
Add the text field. You can configure it however you need.
alert?.addTextField(configurationHandler: { (gradeTypeTextField) -> Void in
gradeTypeTextField.placeholder = "Type of Grade."
let pickerView = UIPickerView()
pickerView.delegate = self
pickerView.dataSource = self
pickerView.backgroundColor = UIColor.white
pickerView.showsSelectionIndicator = true
gradeTypeTextField.inputView = pickerView
self.view.endEditing(true)
})
alert?.addTextField(configurationHandler: { (assignmentTextField) -> Void in
assignmentTextField.placeholder = "Name of the assignment."
})
alert?.addTextField(configurationHandler: { (gradeTextField) -> Void in gradeTextField.placeholder = "Grade for the assignment."
Grab the value from the text field, and print it when the user clicks OK.
let ok = UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in
let assignmenttextField = (self.alert?.textFields![0])! as UITextField
let gradeTextField = (self.alert?.textFields![1])! as UITextField
let assignment = assignmenttextField.text!
let gradeString = gradeTextField.text!
let grade = Int(gradeString)
self.dictionaryForGradebook.updateValue(grade!, forKey: assignment)
print(self.dictionaryForGradebook)
self.tableView.reloadData()
})
self.alert?.addAction(ok)
self.alert?.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) -> Void in
}))
// 4. Present the alert.
self.present(self.alert!, animated: true, completion: nil)
})
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return gradeType.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return gradeType[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
This prints but if I try " yearTextField.text = years[row]" it crashes.
print("Testinggggg")
self.view.endEditing(true)
}
Where is the isuue?
You need to be able to reference your created UITextField later when you attempt to add text to it when you select a row in the UIPicker. You seem to be forgetting to place that link:
alert?.addTextField(configurationHandler: { (gradeTypeTextField) -> Void in
// Append this:
self.gradeTypeTextField = gradeTypeTextField
gradeTypeTextField.placeholder = "Type of Grade."
let pickerView = UIPickerView()
pickerView.delegate = self
pickerView.dataSource = self
pickerView.backgroundColor = UIColor.white
pickerView.showsSelectionIndicator = true
gradeTypeTextField.inputView = pickerView
self.view.endEditing(true)
})
This will however create a retain-cycle so it is recommend to create an unowned/weak self link by adding [weak self] to the start of your closure.
Related
I'm struggling to implement a UIPickerView inside the UIAlert as an input for the textfield. Here's what I've got (think of a simple TODO app);
import UIKit
class ViewController: UIViewController {
let todoItemCategory = ["Work", "Home", "Friends", "Buy stuff"]
let pickerView = UIPickerView()
override func viewDidLoad() {
super.viewDidLoad()
pickerView.dataSource = self
pickerView.delegate = self
}
#IBAction func addItemButtonPressed(_ sender: Any) {
let alert = UIAlertController(title: "Add a new todo item", message: "Provide the title and the category of the new item.", preferredStyle: .alert)
alert.addTextField { field in
field.placeholder = "Type the item title"
field.returnKeyType = .next
}
alert.addTextField { field in
field.placeholder = "Select category"
field.inputView = self.pickerView
}
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Save", style: .default, handler: { _ in
guard let fields = alert.textFields, fields.count == 2 else {
return
}
let itemTitleField = fields[0]
let categoryField = fields[1]
guard let itemTitle = itemTitleField.text, !itemTitle.isEmpty, let category = categoryField.text, !category.isEmpty else {
print("Invalid entries")
return
}
print(itemTitle)
print(category)
}))
present(alert, animated: true)
}
}
//MARK: - Pickerview datasource & delegate methods
extension ViewController: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return todoItemCategory.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return todoItemCategory[row]
}
}
extension ViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// Update the textfield inside the UIAlert
}
}
What I've done is;
A UIAlert view pops up when the user clicks on the button (addItemButtonPressed).
In the "Select category" textfield, I linked to the PickerView as an input view. The Pickerview with possible categories will then appear as selectable options.
The picker view appears nicely, but whenever the user selects one of the values, nothing happens. I want the textfield to show the selected category, and I know I need to do something inside
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// Update the textfield inside the UIAlert
}
I was going to assign the selected value to a global variable, let's say currentSelectedCategory, but I do not know how to make the UIAlert textfield to read it up again whenever the value gets changed.
Appreciate your help in advance to this poor swift newbie!
Set a UIAlertController object reference outside the button action and access it.
class ViewController: UIViewController {
// Other code
var alert: UIAlertController = UIAlertController()
// Other code
#IBAction func addItemButtonPressed(_ sender: Any) {
alert = UIAlertController(title: "Add a new todo item", message: "Provide the title and the category of the new item.", preferredStyle: .alert)
// Other code
}
}
extension ViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// Update the textfield inside the UIAlert
alert.textFields?.last?.text = todoItemCategory[row]
}
}
Noob with a problem. I'm trying to use an alert controller that will take two inputs: one from a textfield, which will be manually entered text, the other is a selection from a UIPicker. The text portion works fine; the problem I'm having is with the Picker.
The PickerView displays as I want; the problem I am having is registering the selection. As a noob, I full anticipate that is something idiotically stupid that I'm doing (which is why you see all the print statements); however, after many attempts and searches, I can't figure out what that stupid mistake is that I'm missing. I've tried borrowing from other solutions, which is why the code is getting a bit messy, and why I'm now turning here for help.
Thanks for any assistance you can provide
import UIKit
import PMAlertController
class TableViewController: UITableViewController {
var dummyArray = ["A", "B", "C", "D", "E"]
var dummyStores = ["1", "2", "3", "4"]
var inputString: String = ""
var pickerView = UIPickerView()
var selectedStore: String?
var storeTextField: String?
var textLabel = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dummyArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath)
cell.textLabel?.text = dummyArray[indexPath.row]
cell.textLabel?.textAlignment = .center
return cell
}
#IBAction func addButtonPressed(_ sender: UIBarButtonItem) {
print ("Add Button Pressed")
var itemTextField = UITextField()
var storeTextField = UITextField()
let screenSize = UIScreen.main.bounds
let alertVC = PMAlertController(title: "A Title", description: "My Description", image: UIImage(named: "img.png"), style: .alert)
let pickerFrame = UIPickerView(frame: CGRect(x:5, y: 20, width: screenSize.width - 20, height: 140))
pickerFrame.tag = 555
pickerFrame.delegate = self
pickerView.delegate = self
pickerView.dataSource = self
alertVC.addTextField { (textField2) in
textField2?.placeholder = "enter item name here"
itemTextField = textField2!
}
alertVC.addTextField { (textField) in
textField?.placeholder = "select store here"
textField?.inputView = pickerView
pickerView.delegate = self
pickerView.dataSource = self
let toolbar = UIToolbar()
toolbar.barStyle = UIBarStyle.default
toolbar.isTranslucent = true
textField?.inputAccessoryView = toolbar
textField?.inputView = pickerView
storeTextField = textField!
}
alertVC.addAction(PMAlertAction(title: "Cancel", style: .cancel, action: { () -> Void in
print("Capture action Cancel")
}))
alertVC.addAction(PMAlertAction(title: "OK", style: .default, action: { () in
print("Capture action OK")
print(itemTextField.text)
print(storeTextField.text)
self.dummyArray.append(itemTextField.text!)
self.tableView.reloadData()
}))
self.present(alertVC, animated: true, completion: nil)
}
}
extension TableViewController: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return dummyStores.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return dummyStores[row]
}
}
extension TableViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedStore = dummyStores[row]
textLabel.text = selectedStore
}
}
Seems you created a text field textLabel (confusing name by the way, because of UILabel) which you use in the picker view delegate selection method, but when you created the text field that triggers the picker view, you aren't connecting the text field to the class' corresponding property. Instead you have created a local variable storeTextField and set that to the text field.
This won't do anything because you lose the reference to that text field as soon as the context clears. Essentially you could replace:
storeTextField = textField!
with:
self.textLabel = textField!
And you should presumably see what you are aiming to accomplish.
You also have a property called storeTextField but that's a String? for some reason.
Just a tip: you (and others on SO) will find it much easier to debug your code if you use meaningful variable names that are consistent with the types they represent. You also have a lot of redundant / unnecessary code in your code sample, such as when you create a UIPickerView instance called pickerFrame that you never use, when you initialize storeTextField and itemTextField but then immediately replace those variables with new values, or when you set the pickerView dataSource and delegate or set the inputView multiple times.
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 a issue on Thread 1 fatal error on my code. I try to create 2 pickers in one view controller. But the error keep coming. When I tried to run on my phone, everything is okay till this page (picker cannot move). It shows an error on (return subject[row]) on 6 line from bottom)
enter image description here
Heres my code:
Import UIKit
class ThirdViewController: UIViewController, UIPickerViewDelegate,
UIPickerViewDataSource {
#IBOutlet weak var classPickerView: UIPickerView!
#IBOutlet weak var subjectPickerView: UIPickerView!
#IBOutlet weak var nextButton: UIButton!
let subclass = ["AA234", "ASA231", "AA9292", "AAA839", "AA5682", "AAA789"]
let subject = ["Introduction to Database","Introduction to Programming","Mathematics","Multimedia"]
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonPressed(_ sender: Any) {
let picker = UIImagePickerController()
picker.delegate = (self as! UIImagePickerControllerDelegate & UINavigationControllerDelegate)
//select row first
let selectedRow1 = classPickerView.selectedRow(inComponent: 0)
let selectedRow2 = subjectPickerView.selectedRow(inComponent: 0)
let selectedClass = subclass [selectedRow1]
let selectedSubClass = subject [selectedRow2]
let messageToShow = "Class \(selectedClass) with the subject \(selectedSubClass)"
// insert messageToShow in message to appear what u selected
let actionSheet = UIAlertController (title: "Please Confirm Before Scan", message: messageToShow, preferredStyle: .alert)
let okAction = UIAlertAction (title: "Proceed to Scan", style: .default, handler: nil) //{action in
//picker.sourceType = .camera})
let cancelAction = UIAlertAction (title: "Cancel", style: .cancel, handler: nil)
actionSheet.addAction(okAction)
actionSheet.addAction(cancelAction)
present(actionSheet, animated: true, completion: nil)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if (pickerView.tag == 1) {
return subject.count
}
else {
return subclass.count
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == 0 {
return subject[row]
}
else {
return subclass[row]
}
}
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if (component == 0) {
return subject.count
}
else {
return subclass.count
}
}
Change numberOfRowsInComponent code as above
Sometimes the simplest things prove the hardest to work out! The picker is displayed when users select the user location text field but the selectors are not working. Gone through SO and Apple docs but can't work out why this is.
let locationPicker = UIPickerView()
var locationData = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Set textfield delegate
userID.delegate = self
userLocation.delegate = self
// Set up picker view for location selection
locationPicker.frame = CGRect(x:0, y: self.view.bounds.height, width: self.view.bounds.width, height: 140)
locationPicker.showsSelectionIndicator = true
let doneButton = UIBarButtonItem(title: "done", style: .plain, target: self, action: #selector(self.closePicker(sender:)))
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "cancel", style: .plain, target: self, action: #selector(self.closePicker(sender:)))
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.blackOpaque
toolBar.isTranslucent = true
toolBar.setItems([cancelButton, spaceButton, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
userLocation.inputView = locationPicker
userLocation.inputAccessoryView = toolBar
// Load locations into array
let ref: FIRDatabaseReference = FIRDatabase.database().reference().child("venues")
ref.observe(.value, with: { snapshot in
// Pull out the keys to represent locations
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
print("snap key, \(snap.key) and snap value \(snap.value)")
let loc : String = snap.key
self.locationData.append(loc)
}
}
// Now stick the array of locations into the picker view
self.locationPicker.dataSource = self
self.locationPicker.delegate = self
self.locationPicker.reloadAllComponents()
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.endEditing(true)
return false
}
func closePicker(sender: AnyObject) {
print("Close picker")
self.locationPicker.isHidden = true
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if let iText = userID.text, let lText = userLocation.text, !iText.isEmpty, !lText.isEmpty
{
print("We have data")
// loginButton.isUserInteractionEnabled = true
// loginButton.alpha = 1.0
}
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return locationData.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return locationData[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
userLocation.text = locationData[row]
}