Refreshing UIPicker from user input - swift

I am new to Xcode/Swift and working on an assignment for which I need some help. I have a simple program that displays a list of names when started. I want to take a new name, from the UITextField, and add it to the data for the picker after the item that is currently selected. The new added name should be shown in the picker as the selected item (so the previously selected item would be immediately above it on the picker’s display at that point). I have the program working thus far, however, my "picker.reloadAllComponents()" function seems to be breaking something. When I leave this in place, none of the data in the picker is being shown. When I remove it, I don't know if my insertButton action is working. Where can I refresh the picker data such that the picker data is being seen in my simulator? Thanks.
import UIKit
class ViewController: UIViewController,
UIPickerViewDelegate, UIPickerViewDataSource {
var characterNames = [
"Luke", "Leia", "Han", "Chewbacca", "Artoo",
"Threepio", "Lando"]
#IBOutlet weak var userInput: UITextField!
#IBOutlet weak var picker: UIPickerView!
#IBAction func selectButton(_ sender: UIButton) {
let row = picker.selectedRow(inComponent: 0)
let selected = characterNames[row]
let title = "You selected \(selected)!"
let alert = UIAlertController(
title: title,
message: "Thank you for choosing",
preferredStyle: .alert)
let action = UIAlertAction(
title: "You're welcome",
style: .default,
handler: nil)
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
#IBAction func insertButton(_ sender: UIButton) {
characterNames.insert(userInput.text!, at: picker.selectedRow(inComponent: 0))
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK:-
// MARK: Picker Data Source Methods
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView,
numberOfRowsInComponent component: Int) -> Int {
return characterNames.count
}
// MARK: Picker Delegate Methods
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
picker.reloadAllComponents()
return characterNames[row]
}
}

Don't call picker.reloadAllComponents() from within the titleRorRow method as I think this may be invoking an infinite loop - this delegate method should return the title for that row only, as your second line is doing correctly. So remove it from there.
Instead, place it in your insertButton method after you have updated the source. This will cause the data to be reloaded, and the data source methods called again to repopulate the picker.
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return characterNames[row]
}
#IBAction func insertButton(_ sender: UIButton) {
characterNames.insert(userInput.text!, at: picker.selectedRow(inComponent: 0))
picker.reloadAllComponents()
}

Related

How do I confirm my selection in UIPickerView with a done button?

I apologise for this question, other questions are either outdated or are about embedding buttons within a UIPickerView
I have a UIPickerView which displays 3 strings; once my user has selected the string he wishes to select, I want him to hit a done button (which is not embedded in the UIPickerView itself; but appears separately).
let strings = ["1", "2", "3"]
fileprivate let pickerView: UIPickerView = {
let pv = UIPickerView()
pv.translatesAutoresizingMaskIntoConstraints = false
return pv
}()
override func viewDidLoad() {
pickerView.delegate = self
pickerView.dataSource = self
}
let button: UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("Done", for: .normal)
btn.addTarget(self, action: #selector(donePressed), for: .touchUpInside)
return btn
}()
#objc func donePressed(_ sender: UIButton) {
print("Done pressed")
//Pressing this should print whichever row is currently selected
}
extension ViewController: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return strings.count
}
}
extension ViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
strings[row]
}
#objc func donePressed(_ sender: UIButton) {
print("Done pressed")
print(strings[pickerView!.selectedRow(inComponent: 0)])
}
Start by looking for something useful in the documentation. Ah, here it is:
https://developer.apple.com/documentation/uikit/uipickerview/1614369-selectedrow
So. Have your button call the picker view's selectedRow(inComponent:). There is only one component so you will pass 0. The return value is the index (the selected row). Use that as an index into strings if you want to know the actual value (the string).

Swift UIPickerView defaults to last option regardless of selected option

I have a UIPickerView that when tapping the done button, keeps selecting the last option of the Array. I can tap on either the UITextField or a button I'm using that assigns the UITextField as the first responder. My code is as follows:
Edit. I should also mention that I'm using Xcode 11 GM Seed if that was to make a difference. I tested a clean version of the code shown below on a separate project with nothing else and it is displaying the same behavior.
#IBOutlet weak var sortTypeTextField: UITextField!
var selectedSortOption = String()
let sortOptions = [
USER_REVIEWS_SORT_OPTION_DATE,
USER_REVIEWS_SORT_OPTION_LIKES,
USER_REVIEWS_SORT_OPTION_DISLIKES
]
sortOptions above is an array of Strings located in a different file but for the purpose of this question I’ll add here:
let USER_REVIEWS_SORT_OPTION_DATE = "Date"
let USER_REVIEWS_SORT_OPTION_LIKES = "Number of Likes"
let USER_REVIEWS_SORT_OPTION_DISLIKES = "Number of Dislikes"
let DONE_TOOLBAR_BUTTON_TITLE = "Done"
I have a function setupUI which I call in viewDidLoad along with the value for the sortTypeTextField. The value is the first String in the array when the viewController first loads.
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
func setupUI() {
sortTypeTextField.text = sortOptions[0]
createSortPicker()
createPickerToolbar()
}
func createSortPicker() {
let sortPicker = UIPickerView()
sortPicker.delegate = self
sortTypeTextField.inputView = sortPicker
}
func createPickerToolbar() {
let toolBar = UIToolbar()
toolBar.sizeToFit()
let doneButton = UIBarButtonItem(title: DONE_TOOLBAR_BUTTON_TITLE, style: .plain, target: self, action: #selector(dismissKeyboard))
toolBar.setItems([doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
sortTypeTextField.inputAccessoryView = toolBar
}
#objc override func dismissKeyboard() {
view.endEditing(true)
}
I’m also using a button to display the UIPicker as shown here:
#IBAction func sortRatingsButtonWasTapped(_ sender: Any) {
sortTypeTextField?.becomeFirstResponder()
}
As shown below, I want for the sortTypeTextField value to change to the selected option but for some reason when tapping the Done button in the toolbar, the text shown is always the third String in the array regardless if I have chosen the first, the second or the third.
extension ReviewsViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
sortOptions.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return sortOptions[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedSortOption = sortOptions[row]
sortTypeTextField.text = selectedSortOption
}
}
It seems to be a bug in the simulator. I tested the my code above and the code provided by Abdul Karim Khan on my device and the work as they should. The label updates according to the option selected in the picker view.
This is how I created pickerView,
class ViewController: UIViewController {
#IBOutlet weak var selectaday: UITextField!
let days = ["monday","wednesday"]
var selectedDay: String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
createDayPicker()
}
func createDayPicker(){
let dayPicker = UIPickerView()
dayPicker.delegate = self
selectaday.inputView = dayPicker
}
}
extension ViewController: UIPickerViewDelegate, UIPickerViewDataSource{
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return days.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return days[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedDay = days[row]
selectaday.text = selectedDay
}
}

Using picker view with UIButton to open a new TableView

I have a picker view and tableviews for each picker row programmed and I'd like to connect them via a UIButton. Any suggestions for a swift function to connect the Pickerview row with the botton to open that picker's table? (code below)
Thanks!
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var Picker1: UIPickerView!
var Array = ["G: Classroom friendly", "PG-13: Swear words OK(no sexual innuendo)", "R: Anything goes!"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Picker1.delegate = self
Picker1.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return Array[row]
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return Array.count
}
public func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
#IBAction func Submit(_ sender: Any) {
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
}
#IBAction func ContinueBotton(_ sender: UIButton) {
}
}
class ViewController1: UITableViewController {
#IBOutlet var ViewController1: UITableView!
var GRating = [GN]()

beginner with Swift with an error UIPicker

I am building a simple app that has a PickerView with 3 selections that should link to 3 viewControllers each named FirstViewController, SecondViewController and ThirdViewController.
But I am getting this error
> NSInvalidArgumentException', reason: 'Receiver (<Scroller.ViewController: 0x7ff7b5e0b670>) has no segue with identifier 'SecondSegue'
I have three options in the picker and a button to go to that selection's viewController:
My code below works on first loading then crashes
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBAction func continueButtonPressed(_ sender: AnyObject) {
self.performSegue(withIdentifier: "\(chosenState)Segue", sender: nil)
print("no chance")
}
#IBOutlet weak var pickerView: UIPickerView!
var pickerData = ["First","Second","Third"]
var chosenState = ""
override func viewDidLoad() {
super.viewDidLoad()
print("Help")
pickerView.delegate = self
pickerView.dataSource = self
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// The number of columns of data
// The number of rows of data
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerData.count
}
// The data to return for the row and component (column) that's being passed in
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return pickerData[row]
}
//Called when the user changes the selection...
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
chosenState = pickerData[row]
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
}
can anyone help me understand how to fix this ?
Have I named the Segues ok? They are all named the same - is that correct?
You have to set the segue identifier to second view controller as SecondSegue
It seems your SecondSegue to your second UIViewController has not been named properly.
Be careful, it is case sensitive, it must be named precisely SecondSegue. And after having changed the name, don't forget to validate your input with the return key.

How can I link a to a second view controller using a button determined by a variable?

I have created a UIPickerView which has an array of services inside. I have used the didSelectRow to display my selected service below the UIPickerView. That's all fine and works perfectly.
I want the service which as been selected to take me to another view controller named that service. For example, Coffee shops as been selected in the Picker, "coffee shops" is displayed below the picker and you click a button which will bring you to a page which displays the given information about coffee shops.
#Pbush25 thanks for the quick reply. Please would you take a look at my code and tell me where I am going wrong as I still can't get it to work.
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate {
#IBOutlet weak var SelectedService: UILabel!
#IBAction func Find(sender: AnyObject) {
}
#IBOutlet weak var ItemLabel: UILabel!
var services = ["Cafe","Coffee Shops","Bar","Takeaway","Sunday Roast","Shoe Mender","Craft Shops","Electrical"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
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 services.count
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String!{
return services[row]
}
}
As #JAL said, there is a method for the UIPickerViewDelegate that I belive is didSelectRow or something to that extent, which will give you the index path of the row that was selected. When your button is pressed to go to the next screen, all you have to do in your prepareForSegue method is get the element at that row and use a switch statement to determine which new view controller to activate the segue on based on what value you receive.
EDIT: Added code for clarity.
//save the picked data to a variable
func pickerView(pickerView: UIPickerView!, didSelectRow row: Int, inComponent component: Int)
{
selection = "\(pickerViewData[row])"
}
//use this variable to decide which segue to take
func prepareForSegue(sender: UIStoryboardSegue){
switch selection {
case: "Coffee Shops"
performSegueWithIdentifier("goToCoffeeShops", sender: self)
case: "Some other selection"
performSegueWithIdentifier("someOtherSegue", sender: self)
}
}