How to sort AVSpeechSynthesisVoice.speechVoices() - swift

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

Related

Display Firebase data inside PickerView

CODE HAS BEEN UPDATED AND IS WORKING AS EXPECTED
I have a View Controller with a text field and a PickerView. I want to display the data i have stored in Firebase inside the PickerView. I'm able to retrieve and print the data from Firebase but I can't find a way to display it inside the PickerView. Here is my code:
import UIKit
import Firebase
class pickerVC: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate {
#IBOutlet weak var labelTxt: UITextField!
#IBOutlet weak var infoPickerViewer: UIPickerView!
var dbRef: CollectionReference!
var pickerView: UIPickerView?
var itemsClass = [ItemInfo]()
override func viewDidLoad() {
super.viewDidLoad()
let pickerView = UIPickerView()
infoPickerViewer.delegate = self
infoPickerViewer.dataSource = self
dbRef = Firestore.firestore().collection(ITEMS_REF)
labelTxt.inputView = pickerView
labelTxt.delegate = self
self.infoPickerViewer = pickerView
self.infoPickerViewer?.delegate = self
self.infoPickerViewer?.dataSource = self
self.infoPickerViewer.reloadAllComponents()
getItems()
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func textFieldDidBeginEditing(_ textField: UITextField) {
labelTxt = textField
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
self.pickerView?.reloadAllComponents()
return true
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if labelTxt.isFirstResponder {
return self.itemsClass.count
}
return 0
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if labelTxt.isFirstResponder {
return itemsClass[row].itemName
}
return nil
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if labelTxt.isFirstResponder {
let item = itemsClass[row].itemName
labelTxt.text = item
}
}
func getItems() {
dbRef.getDocuments { (snapshot, error) in
if let err = error {
debugPrint("error fetching docs: \(err)")
} else {
self.infoPickerViewer.reloadAllComponents()
let snap = snapshot
for document in snap!.documents {
let data = document.data()
let itemCode = data[ITEMS_CODE] as? String ?? ""
let itemName = data[ITEMS_NAME] as? String ?? ""
let t = ItemInfo(itemCode: itemCode, itemName: itemName)
self.itemsClass.append(t)
print("ITEMS_CODE", itemCode as Any)
print("ITEMS_NAME", itemName as Any)
}
}
}
}
}
The Firebase DB is structured as follow:
collection/AutoID/itemCode: "item1"
itemName: "item2"
collection/AutoID/itemCode: "item3"
itemName: "item4"
I only need to display the itemName inside the PickerView, the itemCode I'm going to use it to run a query depending on the selection in the PickerView.
Any help with this is greatly appreciated.

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

How to use multiple UIPickerViews in one view controller

I am trying to set up 2 UIPickerViews in 1 view controller. I have not been able to find resources that solve my problem.
I have tried following the answer from Diavel Rider on How to use 2 UIPickerView's in one View Controller?. I got some errors from that. I have also tried adding tags but was not able to successfully do that. I am using Swift 4.
Here is the code of my view controller:
import UIKit
class FirstViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ foodTypeUIPickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
var countrows : Int = nutritionPickerData.count
if pickerView == foodTypeUIPickerView { **[Error Here: "Binary operator '==' cannot be applied to operands of type '_' and 'UIPickerView'"]**
countrows = self.foodTypePickerData.count
}
return countrows
}
func pickerView(_ foodTypeUIPickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView == nutritionUIPickerView { **[Error Here: "Binary operator '==' cannot be applied to operands of type '_' and 'UIPickerView'"]**
let titleRow = nutritionPickerData[row]
return titleRow
} else if pickerView == foodTypeUIPickerView { **[Error Here: "Binary operator '==' cannot be applied to operands of type '_' and 'UIPickerView'"]**
let titleRow = foodTypePickerData[row]
return titleRow
}
return ""
}
#IBOutlet weak var nutritionUIPickerView: UIPickerView!
#IBOutlet weak var foodTypeUIPickerView: UIPickerView!
var nutritionPickerData = ["Protein", "Fiber", "Iron", "Fat", "Sodium", "Calcium/Vitamin D", "Energy", "Carbohydrates", "Cholestorol"]
var foodTypePickerData = ["Fruits", "Vegetables", "Legumes and Beans", "Grain", "Meat", "Dairy"]
#IBAction func submitUIButton(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.nutritionUIPickerView.delegate = self
self.nutritionUIPickerView.dataSource = self
self.foodTypeUIPickerView.delegate = self
self.foodTypeUIPickerView.dataSource = self
}
}
I need to use 2 picker views in 1 view controller. How do I do this?
You are referencing a non-existent variable named pickerView. You should update the parameter name to pickerView to avoid issues.
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView == foodTypeUIPickerView {
return self.foodTypePickerData.count
} else {
return self. nutritionPickerData.count
}
}
Make a similar change to the other delegate/data source methods.
You should separate your data sources and delegates and not put them inside your view controller. Set those as the delegate and datasource. Code written in swift 4.2 xcode 10.1
class ViewController: UIViewController {
#IBOutlet weak var foodPicker: UIPickerView!
#IBOutlet weak var nutritionPicker: UIPickerView!
var foodData = FoodData()
var nutritionData = NutritionData()
override func viewDidLoad() {
super.viewDidLoad()
foodPicker.delegate = foodData
foodPicker.dataSource = foodData
nutritionPicker.delegate = nutritionData
nutritionPicker.dataSource = nutritionData
}
#IBAction func actionButton(_ sender: UIButton) {
let food = foodPicker.delegate?.pickerView!(foodPicker, titleForRow: foodPicker.selectedRow(inComponent: 0), forComponent: 0)
let nutrition = nutritionPicker.delegate?.pickerView!(nutritionPicker, titleForRow: nutritionPicker.selectedRow(inComponent: 0), forComponent: 0)
print("You picked: \(food) and \(nutrition)")
}
}
class FoodData: NSObject, UIPickerViewDelegate, UIPickerViewDataSource {
var foodTypePickerData = ["Fruits", "Vegetables", "Legumes and Beans", "Grain", "Meat", "Dairy"]
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return foodTypePickerData.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return foodTypePickerData[row]
}
}
class NutritionData: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
var nutritionPickerData = ["Protein", "Fiber", "Iron", "Fat", "Sodium", "Calcium/Vitamin D", "Energy", "Carbohydrates", "Cholestorol"]
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return nutritionPickerData.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return nutritionPickerData[row]
}
}

how to get the value (or index) from a picker and compare it to the value of an attribute of an entity?

I have a problem in returning the value from a picker and comparing it to the value of an attribute. Basically, i have a Floor entity which has a attribute "floorNumber". From the picker I need to get the floor number and match it to the correct entity object so I can start assigning rooms to individual floors. I have implemented the delegate and the data source for the picker and looked at other questions on this site but I'm getting a bit confused.
Please have look at my implementation and please tell me what am I doing wrong. I have an optional "pickedFloor" which is nil even though it is being set and if I try to unwrap it it will crash it. Thank you for your help
class SetNumberOfRoomsPerFloor: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
//MARK: - Properties
#IBOutlet private weak var floorPicker: UIPickerView!
#IBOutlet private weak var numberOfRoomsPerFloor: UITextField!
private var managedObjectContext: NSManagedObjectContext!
internal var floorValue: Int16?
private var convertedFloorValues = [String]()
private var storedFloors = [Floors]()
private var pickedFloor: Int16?
private var roomNumberValue: Int16 {
get {
return Int16(numberOfRoomsPerFloor.text!)!
}
}
override func viewDidLoad() {
super.viewDidLoad()
managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
floorPicker.delegate = self
floorPicker.dataSource = self
loadFloorData()
spinnerItems()
}
#IBAction func setTheFloors(_ sender: UIButton) {
if storedFloors.count > 0 {
if storedFloors.first?.floorNumber == pickedFloor {
storedFloors.first?.numberOfRooms = roomNumberValue
print("\(storedFloors.first?.floorNumber) + \(storedFloors.first?.numberOfRooms)")
}
}
}
#IBAction func nextStep(_ sender: UIButton) {}
private func loadFloorData() {
let floorRequest: NSFetchRequest<Floors> = Floors.fetchRequest()
do {
storedFloors = try managedObjectContext.fetch(floorRequest)
} catch {
print("could not load data from core \(error.localizedDescription)")
}
}
private func spinnerItems() {
for i in 0...floorValue! - 1 {
convertedFloorValues.append(String(i))
}
}
public func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return convertedFloorValues.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return convertedFloorValues[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let selection = Int16(convertedFloorValues[row]) // not sure I have to do this part.
pickedFloor = selection
}
}
Inside this function, you should do this
private func spinnerItems() {
for i in 0...floorValue! - 1 {
convertedFloorValues.append(String(i))
}
if convertedFloorValues.count >= 1 {
pickedFloor = Int16(convertedFloorValues[0])
}
}
Like I said in the comments, didSelectRow never gets called if you don't actually move the picker view.
If the user hasn't selected anything in pickerview then pickedFloor will be nil.Try this.
#IBAction func setTheFloors(_ sender: UIButton) {
if storedFloors.count > 0 {
if storedFloors.first?.floorNumber == convertedFloorValues[floorPicker.selectedRow(inComponent: 0)] {
storedFloors.first?.numberOfRooms = roomNumberValue
print("\(storedFloors.first?.floorNumber) + \(storedFloors.first?.numberOfRooms)")
}
}
}