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

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).

Related

How to sort AVSpeechSynthesisVoice.speechVoices()

I'm a beginner, and I'm while working on this, I noticed that the picker view isn't sorted
Obviously Chinese does not come after Turkish, so how do I sort it up? I've been looking at my code for hours and can't seem to figure out how to get it sorted. Here's my complete code.
Thanks
import AVFoundation
import UIKit
class SettingsView: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet var pickerView: UIPickerView!
var voicesArray: [String] = []
let speechVoices = AVSpeechSynthesisVoice.speechVoices()
override func viewDidLoad() {
super.viewDidLoad()
pickerView.delegate = self
pickerView.dataSource = self
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return speechVoices.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
let language = Locale.current.localizedString(forLanguageCode: speechVoices[row].language)!
let languageCode = speechVoices[row].language
var regionCode:String = ""
for region in Locale.isoRegionCodes {
// Test if provided region codes has voices or not
if languageCode.contains(region) {
regionCode = region
}
}
let country = Locale.current.localizedString(forRegionCode: String(regionCode))
return "\(language) (\(country ?? ""))"
}
#IBAction func done(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
Chinese (zh) definitely comes after Turkey (tr), so these appear already sorted by region code. If you want to sort by something else (I assume their display name in the locale language), then you can sort them when you set up your list of elements for the picker. For example:
let speechVoices = AVSpeechSynthesisVoice.speechVoices().sorted(by: {
Locale.current.localizedString(forLanguageCode: $0.language)! < Locale.current.localizedString(forLanguageCode: $1.language)!
})

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
}
}

How to create a textfield that can hold multiple values eg.email to: field which contains multiple recepient email address

I have a UITextField which has pickerview as inputview.Pickerview displays data from a string array stored in NSUSERDEFAULTS(eg.["horse","cat","camel","cow","parrot","peacock"]). I need to select multiple values from the picker and display it in a single UITextField like in email to: field.
Could you please provide some inputs to this?
Currently im able to pick one value from picker and set the textfield to that value.Need to know how to select multiple values from a single component of picker and set it to a single textfield.
PFB the code which i tried
class ViewController: UIViewController,UITextFieldDelegate,UIPickerViewDelegate,UIPickerViewDataSource {
var item = String()
var itemsaved = [String]()
#IBOutlet var searchTextField: UITextField!
#IBOutlet var picker: UIPickerView!
let itemArray = ["camel","cow","horse","duck","parrot","peacock"]
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
defaults.set(itemArray, forKey: "stringArray")
self.searchTextField.inputView = picker
picker.removeFromSuperview()
self.picker.translatesAutoresizingMaskIntoConstraints = false
self.searchTextField.delegate = self
searchTextField.addTarget(self, action: #selector(ViewController.editingchanged(_:)),
for: UIControl.Event.editingChanged)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.itemsaved.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return self.itemsaved[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
self.searchTextField.text = self.itemsaved[row]
self.view.endEditing(true)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
func retreivedata(sender:UITextField) {
if let savedItems = defaults.stringArray(forKey: "stringArray"){
for item in savedItems{
item.hasPrefix(sender.text!){
print(item)
itemsaved.append(item)
}
}
}
}
#IBAction func editingchanged(_ sender: UITextField) {
self.picker.dataSource = self
self.picker.delegate = self
retreivedata(sender: self.searchTextField)
}
}

Set Title of UIButton from Selected Row of UIPickerView in swift

I am trying to set my Button title from a selected row from uipickerview. However, I can't seem to find a valid solution.
I have set the variables to be global thus I can access the Selected Variable. However I cant seem to be able to pinpoint exactly when the user clicks the exit button and able to update my button title.
Things I have tried:
Global Functions ( couldn't work because you cant specify the button you want to change the name of )
Dispatchqueue.main.async - Did not update my title. The idea here is for it to constantly check for a string change.
The solution I found here is in Obj-C. I tried converting it to swift https://objectivec2swift.com/#/converter/code/ with no luck.
Set Title of UIButton from Selected Row of UIPickerView
#IBAction func CountryPickButton(_ sender: UIButton) {
Global.setPickerDataSource(data:["Test","test2","test3"])
Global.setPickerCompletionHandler(int:0)
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PickerViewPopUpId") as! PickerViewController
//CountryPickButtonDetector = sender as? UIButton
self.addChild(popOverVC)
popOverVC.view.frame = self.view.frame
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParent: self)
popOverVC.callback { value in
//CountryPickButton.title.text = value
sender.setTitle(value, for: UIControl.State)
}
}
import UIKit
var test = ""
class PickerViewController: UIViewController {
#IBOutlet weak var PickerView: UIPickerView!
var callback : ((String) -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
//self.view.backgroundColor = UIColor.black.withAlphaComponent(0.8)
PickerView.dataSource = self
PickerView.delegate = self
// Do any additional setup after loading the view.
}
#IBAction func ExitButton(_ sender: Any) {
switch Global.pickerCompletionHandler {
case 0:
callback?(Global.pickerResult ?? "")
Global.setProfileCountry(string:
Global.pickerResult ?? "")
default:
print("nothing")
}
self.view.removeFromSuperview()
}
}
extension PickerViewController: UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1 // can be more than 1 component like time/date/year
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return Global.pickerDataSource?.count ?? 1
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
//Global.pickerResult = Global.pickerDataSource?[row]
Global.setPickerResult(result: Global.pickerDataSource?[row] ?? "Test" )
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return Global.pickerDataSource?[row]
}
}
Globals
import Foundation
struct Global {
static var pickerDataSource:[String]? = nil
static var pickerResult:String? = nil
static var pickerCompletionHandler:Int? = 0
static var profileCountry:String? = nil
static func setPickerDataSource(data:[String]) {
Global.pickerDataSource = data
}
static func setPickerResult(result:String) {
Global.pickerResult = result
}
static func setPickerCompletionHandler(int: Int) {
Global.pickerCompletionHandler = int
}
static func setProfileCountry(string: String)
{
Global.profileCountry = string
}
}
I don't know what all that Global stuff is, the easiest solution might be to extend pickerCompletionHandler, but here is another (easy) one:
In PickerViewController add a callback passing a String value and no return value
var callback : ((String) -> Void)?
and call it in exitButton (please, please variable and function names are supposed to start with a lowercase letter)
#IBAction func exitButton(_ sender: Any) {
switch Global.pickerCompletionHandler {
case 0:
callback?(Global.pickerResult ?? "")
Global.setProfileCountry(string:
Global.pickerResult ?? "")
default:
print("nothing")
}
self.view.removeFromSuperview()
}
In countryPickButton (again: lowercase letter) set the callback and set sender to the real type. The callback sets the title
#IBAction func countryPickButton(_ sender: UIButton) {
Global.setPickerDataSource(data:["Test","test2","test3"])
Global.setPickerCompletionHandler(int:0)
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PickerViewPopUpId") as! PickerViewController
popOverVC.callback = { value in
sender.setTitle(value, for: .normal)
}
...

Open new view controller page according to value chosen from UIPickerView (Swift 4)

I would like to add a function in my app where the user has to choose a category from a UIPickerView and upon clicking a button, the program would open the view selected from the categories in the UIPickerView. For example, if one of the chosen options on the UIPickerView is "About Page", I would like to have a program that would open the "About Page" upon a click of the button.
This is what the UI would look like.
But instead of the states - I would have the categories, I'm not sure how to set the values in the UIPickerView and open specific views.
Implement UIPickerViewDelegate method and give each VC a stroryboard Identifier
var lastId:String?
var ids = ["secondVC","thirdVC"] // picker data source and identifier names
func pickerView(_ pickerView: UIPickerView,
didSelectRow row: Int,
inComponent component: Int) {
lastId = ids[row]
}
#IBAction func goClicked(_ sender:UIButton) {
if let id = lastId {
let vc = storyboard.instantiateViewController(withIdentifier:id)!
self.present(vc, animated: true, completion: nil) // or push
}
else {
// select a vc alert
}
}
If you want to create the picker view programmatically - which I recommend the most - this solution should work for you:
class MyViewController : UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
let pickerView = UIPickerView()
let titles = ["view1", "view2"]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
pickerView.delegate = self
pickerView.dataSource = self
view.addSubview(pickerView)
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return 2
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return titles[row]
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if row == 0 {
let view1 = viewController1()
self.present(view1, animated: true, completion: nil)
}
if row == 1 {
let view2 = viewController2()
self.present(view2, animated: true, completion: nil)
}
}
}
class viewController1 : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
}
}
class viewController2 : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
}
}
hope that this works for you too ;)