I'm using CoreData and I'm trying to have 2 different columns in my UIPickerView with Stores and the Item Type
When I run it, the items are "there" but they appear as a "?"
here is my code for my titleForRow function:
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> (String?, String?) {
//title
let store = stores[row]
let itemType = itemTypes[row]
// select store out of array of stores
return (store.name, itemType.type)
}
something to note is Xcode is giving me a warning saying:
Instance method 'pickerVIew(titleForRow:forComponent:)' nearly matches
optional requirement 'pickerView(titleForRow:forComponent:)' of
protocol UIPickerView
You can return only String type, not tuple. If you want to add two columns, you need to set
pickerView.numberOfComponents = 2
and check component parameter like:
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return component == 0 ? stores[row].name : itemTypes[row].type
}
Related
I have encountered some synchronisation/graphic update problems with my UIPickerView.
I want a view with 2 components, where the content of the second component depends on the selected row of the first component.
My code is inspired from: Swift UIPickerView 1st component changes 2nd components data
However, while it seems to work, sometimes (not every time) there are some visual problems, as seen on the screenshots below. (on the second screenshot, you can see that the rows of the second component are not really correct, and are a mix of the rows from the first and the second component)
Here is the code:
import UIKit
class AddActivityViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var typePicker: UIPickerView!
var pickerData: [(String,[String])] = []
override func viewDidLoad() {
super.viewDidLoad()
self.typePicker.delegate = self
self.typePicker.dataSource = self
pickerData = [("sport",["bike", "run", "soccer", "basketball"]),
("games",["videogame", "boardgame", "adventuregame"])]
// not sure if necessary
typePicker.reloadAllComponents()
typePicker.selectRow(0, inComponent: 0, animated: false)
// pickerData = [("sport",["bike", "run", "soccer"]),
// ("games",["videogame", "boardgame", "adventuregame"])]
}
// number of columns in Picker
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}
// number of rows per column in Picker
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
print("function 1 called")
if component == 0 {
return pickerData.count
} else {
let selectedRowInFirstComponent = pickerView.selectedRow(inComponent: 0)
return pickerData[selectedRowInFirstComponent].1.count
}
}
// what to show for a specific row (row) and column (component)
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
print("function 2 called with values: component: \(component), row: \(row)")
if component == 0 {
// refresh and reset 2nd component everytime another 1st component is chosen
pickerView.reloadComponent(1)
pickerView.selectRow(0, inComponent: 1, animated: true)
// return the first value of the tuple (so the category name) at index row
return pickerData[row].0
} else {
// component is 1, so we look which row is selected in the first component
let selectedRowInFirstComponent = pickerView.selectedRow(inComponent: 0)
// we check if the selected row is the minimum of the given row index and the amount of elements in a given category tuple array
print("---",row, (pickerData[selectedRowInFirstComponent].1.count)-1)
let safeRowIndex = min(row, (pickerData[selectedRowInFirstComponent].1.count)-1)
return pickerData[selectedRowInFirstComponent].1[safeRowIndex]
}
//return pickerData[component].1[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// This method is triggered whenever the user makes a change to the picker selection.
// The parameter named row and component represents what was selected.
}
}
Is this a problem with my code or generally a complicated aspect of UIPickers that can not be trivially solved?
Additionally, is there a nicer way to develop this functionality?
I solved the error, however I do not understand why this solves it.
The solution is to imlement the func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)method, which I did not believe to be necessary just to show the fields.
In other words, just add this to my existing code:
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if component == 0 {
pickerView.reloadComponent(1)
} else {
let selectedRowInFirstComponent = pickerView.selectedRow(inComponent: 0)
print(pickerData[selectedRowInFirstComponent].1[row])
}
}
I don't know what I've done to anger the gods but my UIPickerView is behaving abnormally. It has 2 components, for month and year values, and selecting a value on the right component (year) causes the left component (month) to change value as well.
I initially create the UIPickerView using the following code:
pickerView = UIPickerView(frame: ...)
pickerView.delegate = self
pickerView.dataSource = self
Then I implement some simple delegate / data source methods to handle the picker view, as follows:
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if component == 0 {
expirationMonth = months[row]
} else {
expirationYear = years[row]
}
expirationTextField?.text = "\(expirationMonth ?? "")/\(expirationYear?.substring(from: 2) ?? "")"
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == 0 {
return months[row]
} else {
return years[row]
}
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == 0 {
return months.count
} else {
return years.count
}
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}
Additionally, the UIPickerView data source method numberOfComponents is being called even when I don't set the picker view's data source.
I have no idea what's causing this behavior. I've tried pretty much everything and nothing is stopping it from changing both column's values. Does anyone have any ideas to fix this?
Turns out it's a bug in the iOS simulator, because when running it on my iPhone it worked like a charm. I thought I was going crazy there for a second!
I'm going to file a bug complaint in the morning...Good luck to anyone else with this issue.
I'm a newbie developer, trying to return 2 variables but without any success. Here are what I have already tried:
I tried to put these 2 variables (I'm not sure if those are variables or they have different name) into the array and then call return, but the error was:
"cannot convert return expression of type [Int] to return type int"
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
var typeAndStoreArray = [Int]()
typeAndStoreArray.append(stores.count)
typeAndStoreArray.append(types.count)
return typeAndStoreArray
}
I tried to put stores.count into variable called sc and types.count into variable called tc, but here as well I had an error
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
let sn = stores.count
let tn = types.count
return (sn, tn)
}
Try to understand the functionality of this delegate method.
The method is called multiple times for each component. It passes the index of the component and expects to return the corresponding number of rows.
So if your stores array is component 0 and types is component 1 you have to write
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == 0 {
return stores.count
} else {
return types.count
}
}
You are misusing picker view. To have two wheels of values, you need to return counts for each wheel separately. A component in picker view is like the wheel. So, to have the first wheel (component) displaying stores and then second wheel displaying types, you need to return counts separately like this
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == 0 {
return stores.count
} else
return types.count
}
}
I'm working with XCode 8.2.1, Swift 3 and iOS10.
I've a list of items with the following format:
ID | Name
---------
1 | John
2 | Maria
3 | Peter
4 | Roger
The code looks like this:
var formsList = [1:"John", 2:"Maria", 3:"Peter", 4:"Roger"]
What I want to do is to set that data into an UIPickerView, so when someone chooses for example John, the ID 1 is returned, or if someone chooses Peter, the ID 3 is returned.
I do other stuff once I get that ID, that's why I need it.
Any idea or suggestion on how I can achieve this?
Thanks!
You just need to sort your dictionary by its keys and use it as your picker data source:
let formsList = [1:"John", 2:"Maria", 3:"Peter", 4:"Roger"]
let dataSource = formsList.sorted{$0.key<$1.key}
This way you have all your dictionary names sorted in an array including their IDs. Your picker should look something like this:
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var pickerView: UIPickerView!
#IBOutlet weak var label: UILabel!
let formsList = [1:"John", 2:"Maria", 3:"Peter", 4:"Roger"]
var dataSource: [(key: Int, value: String)] = []
override func viewDidLoad() {
super.viewDidLoad()
dataSource = formsList.sorted{$0.key<$1.key}
pickerView.delegate = self
pickerView.dataSource = self
label.text = "id: " + String(dataSource[pickerView.selectedRow(inComponent: 0)].key) + " - " + "name: " + dataSource[pickerView.selectedRow(inComponent: 0)].value
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return dataSource.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return dataSource[row].value
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
label.text = "id: " + String(dataSource[row].key) + " - " + "name: " + dataSource[row].value
}
}
sample
Your approach of using a dictionary of your data structure is problematic. Dictionaries are, by design, unordered.
There are lots of ways to do this. Most involve creating an array of some sort containing items for each entry from which you want the user to pick.
For example, create an array of tuples:
typealias NameTuple = (id: Int, name: String)
var namesArray: [NameTuple]
Feed your picker view with the name field of each entry in your array. When the user selects an item, use the selected index to fetch that tuple and then get the ID.
You could also use an array of structs, or an array of name objects.
I have problem with a picker view that appears empty when I make it in second view controller.(image1).
When I make it in the first view controller that work fine.
two view controller and pickerview
code:
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate {
var civilite = ["Madame","Monsieur","Mademoiselle"]
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return civilite.count
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String!{
return civilite[row]
}
}
Xcode 6.4
I found the solution.
I add new file "Cocoa touch class" (Formulaire.swift) I change the custom class of the second view controller to be "Formulaire" [Custom class][1]
[1]: http://i.stack.imgur.com/H4Nip.png
I deplace the code in this file and is work fine.
Thanks