Set Title of UIButton from Selected Row of UIPickerView in swift - 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)
}
...

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

Pass chose value into label inside a table view cell

I'm relatively new into swift and I'm still struggling with some basics. But well thank you all fo helping me in this. Ok when a UITableViewCellis selected a PopUpViewController is presented with a datepicker so the user can select a desire date and when dissmisButtonis pressed the current view is dismissed and as soon that happens I want the selected value to show on the label embedded on the selected UITableViewCell.
Code on PopUpViewController
import UIKit
class PopUpColchonesViewController: UIViewController {
var tipoColchonArray = [String](arrayLiteral: "Individual", "Matrimonial", "King Size")
#IBOutlet weak var tipoColchonLb: UILabel!
#IBOutlet weak var tipoColchonPicker: UIPickerView!
#IBOutlet weak var guardarTipoColchon: UIButton!
var tipoSeleccionado : String?
override func viewDidLoad() {
super.viewDidLoad()
tipoColchonPicker.delegate = self
tipoColchonPicker.dataSource = self
// Do any additional setup after loading the view.
}
#IBAction func guardarTipo(_ sender: Any) {
NotificationCenter.default.post(name: .guardarTipo, object: nil)
dismiss(animated: true, completion: nil)
}
}
extension PopUpColchonesViewController : UIPickerViewDelegate, UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return tipoColchonArray.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return tipoColchonArray[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let selectedItem = tipoColchonArray[row]
tipoSeleccionado = selectedItem
}
}
extension Notification.Name {
static let guardarTipo = Notification.Name(rawValue: "guardarTipo")
}
What I've try so far
var observer : NSObjectProtocol?
var cellServicio = UITableViewCell()
override func viewDidLoad() {
super.viewDidLoad()
servicioHogarTB1.delegate = self
servicioHogarTB1.dataSource = self
servicioHogarTB1.register(UINib(nibName: "ServicioCell", bundle: nil), forCellReuseIdentifier: "servicioCell")
servicioHogarTB1.separatorStyle = .none
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(handlePopupClosing), name: .guardarTipo, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let observer = observer {
NotificationCenter.default.removeObserver(observer)
}
print(servicioSeleccionado)
}
#objc func handlePopupClosing (notification : NSNotification) {
let tipoVC = notification.object as? PopUpColchonesViewController
let index = servicioHogarTB1.indexPathForSelectedRow
if let cell = servicioHogarTB1.cellForRow(at: index!) as? ServicioCell {
cell.servicioTipoLb.text = tipoVC?.tipoSeleccionado
}
}
The results so far are error while unwrapping an optional value and the text on label is not shown.
Change your method like this and check ... hopefully it will work
#IBAction func guardarTipo(_ sender: Any) {
dismiss(animated: true, completion: {
NotificationCenter.default.post(name: .guardarTipo, object: nil)
})
}

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

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

found nil while unwrapping an ViewController Optional

I am building a very simple swift application that uses two view controllers - "ViewControler" and "AnimalChooserViewControler". First has a simple label and a toolbar with one item, which transfers the user to the second screen. In the second, have custom picker. The whole purpose of the app is to display whatever the user has selected from the second view controler to the first one using the output UILabel. I have to mention that I am usong xCode 6.4 (ios8)
My problem is when I try to cast the presentedViewController as! ViewController the app crashes with "fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)" exceptions. How to solve this problem, any suggestions?
Here is my code in the ViewControler:
class ViewController: UIViewController {
#IBOutlet weak var outputLabel: UILabel!
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 displayAnimal(choosenAnimal: String, withSound choosenSound: String, fromComponent choosenComponent: String){
self.outputLabel.text = "You changed \(choosenComponent) (\(choosenAnimal)(and the sound \(choosenSound))"
}
}
And this is my code in AnimalChooserViewControler
class AnimalChooserViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate{
let kComponentCount: Int = 2
let kAnimalComponent: Int = 0
let kSoundComponent: Int = 1
var animalNames: [String] = []
var animalSounds: [String] = []
var animalImages: [UIImageView] = []
#IBAction func dismisAnimalChooser(sender: AnyObject) {
dismissViewControllerAnimated(true, completion: nil)
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return kComponentCount
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == kAnimalComponent{
return animalNames.count
} else {
return animalSounds.count
}
}
func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView!) -> UIView {
if component == kAnimalComponent {
let choosenImageView: UIImageView = animalImages[row]
let workarroundImageView: UIImageView = UIImageView(frame: choosenImageView.frame)
workarroundImageView.backgroundColor = UIColor(patternImage: choosenImageView.image!)
return workarroundImageView
} else {
let soundLabel: UILabel = UILabel(frame: CGRectMake(0, 0, 100, 32))
soundLabel.text = animalSounds[row]
return soundLabel
}
}
func pickerView(pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
return 55.0
}
func pickerView(pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
if component == kAnimalComponent{
return 75.0
} else {
return 150.0
}
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let initialView : ViewController = presentedViewController as! ViewController
if component == kAnimalComponent{
let choosenSound: Int = pickerView.selectedRowInComponent(self.kSoundComponent)
initialView.displayAnimal(animalNames[row], withSound: animalSounds[choosenSound], fromComponent: "the Animal")
} else {
let choosenAnimal: Int = pickerView.selectedRowInComponent(kAnimalComponent)
initialView.displayAnimal(animalNames[choosenAnimal], withSound: animalSounds[row], fromComponent: "the Sound")
}
}
override func viewDidLoad() {
super.viewDidLoad()
animalNames = ["Mouse","Goose","Cat","Dog","Snake","Bear","Pig"]
animalSounds = ["Oink","Rawr","Sss","Meow","Honk","Squeak"]
animalImages = [UIImageView(image: UIImage(named: "mouse.png")),
UIImageView(image: UIImage(named: "goose.png")),
UIImageView(image: UIImage(named: "cat.png")),
UIImageView(image: UIImage(named: "dog.png")),
UIImageView(image: UIImage(named: "snake.png")),
UIImageView(image: UIImage(named: "bear.png")),
UIImageView(image: UIImage(named: "pig.png"))]
preferredContentSize = CGSizeMake(340,380)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let initialView: ViewController = presentedViewController as! ViewController
initialView.displayAnimal(animalNames[0], withSound: animalSounds[0], fromComponent: "nothing yet...")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
I found what was causing the exception. I have used "presentedViewController" instead of "presentingViewController". Silly mistake but it took me a good hour to find it out :)