I need a multiple component pickerview, but I need to add a new column to the far left of it. So far it only has two of the three I need. Here is the current code. I have addeded arrays, wrapped an array inside an array, added variables as a column and also changed the outputs.
import UIKit
class Country {
var cats: String
var country: String
var cities: [String]
init(country:String, cities:[String]) {
self.cats = cat
self.country = country
self.cities = cities
}
}
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var pickerView: UIPickerView!
#IBOutlet weak var countryLbl: UILabel!
var countries = [Country]()
override func viewDidLoad() {
pickerView.delegate = self
pickerView.dataSource = self
cats.apppend(Cat(cat: "furry",
countries.append(Country(country: "India", cities: ["Delhi", "Ahmedabad", "Mumbai", "Pune"]))
countries.append(Country(country: "USA", cities: ["New York", "DC", "Fairfax"]))
countries.append(Country(country: "Austrailia", cities: ["Sydney", "Melbourne"]))
super.viewDidLoad()
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 3
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == 0 {
return countries.count
}
else {
let selectedCountry = pickerView.selectedRow(inComponent: 0)
return countries[selectedCountry].cities.count
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == 0 {
return countries[row].country
}
else {
let selectedCountry = pickerView.selectedRow(inComponent: 0)
return countries[selectedCountry].cities[row]
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
pickerView.reloadComponent(1)
let selectedCountry = pickerView.selectedRow(inComponent: 0)
let selectedCity = pickerView.selectedRow(inComponent: 1)
let country = countries[selectedCountry].country
let city = countries[selectedCountry].cities[selectedCity]
countryLbl.text = "Country: \(country)\nCity: \(city)"
}
}
I added the vars but do I add more brackets and then I don't know how to identify the self like why does country = country but cat can't = cat? Also, the first column I'm going to add (far left in the row) will only have 2 choices and won't effect the other choices.
Since the cats component has nothing to do with the countries, do not add cats to your Countries class (which should be a struct, by the way).
Just have a separate cats array in your view controller. And you need to update all of the picker view methods.
struct Country {
let country: String
let cities: [String]
}
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var pickerView: UIPickerView!
#IBOutlet weak var countryLbl: UILabel!
let cats = ["Cat1", "Cat2"]
var countries = [Country]()
override func viewDidLoad() {
pickerView.delegate = self
pickerView.dataSource = self
countries.append(Country(country: "India", cities: ["Delhi", "Ahmedabad", "Mumbai", "Pune"]))
countries.append(Country(country: "USA", cities: ["New York", "DC", "Fairfax"]))
countries.append(Country(country: "Austrailia", cities: ["Sydney", "Melbourne"]))
super.viewDidLoad()
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 3
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == 0 {
return cats.count
} else if component == 1 {
return countries.count
} else {
let selectedCountry = pickerView.selectedRow(inComponent: 1)
return countries[selectedCountry].cities.count
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == 0 {
return cats[row]
} else if component == 1 {
return countries[row].country
} else {
let selectedCountry = pickerView.selectedRow(inComponent: 1)
return countries[selectedCountry].cities[row]
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if component == 0 {
// do something with the new cat
} else if component == 1 || component == 2 {
if component == 1 {
pickerView.reloadComponent(2)
}
let selectedCountry = pickerView.selectedRow(inComponent: 1)
let selectedCity = pickerView.selectedRow(inComponent: 2)
let country = countries[selectedCountry].country
let city = countries[selectedCountry].cities[selectedCity]
countryLbl.text = "Country: \(country)\nCity: \(city)"
}
}
}
Related
I tried a lot but it just shows me "?" in the rows of the four components.
Is it possible to put a String at the first place in the Array Like as titles for the 4 components? I had the idea to change all the Integers in the arrays into Strings but it would takes me too long...
let componentOne = [1...1000]
let componentTwo = [1...59]
let componentThree = [1...59]
let componentFour = [1...3]
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 4
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == 0 {
return 1000
}
if component == 1 {
return 59
}
if component == 2 {
return 59
}
if component == 3 {
return 3
}
return component
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> Int {
if component == 0 {
let row = componentOne[row]
return 1000
}
if component == 1 {
let row = componentTwo[row]
return 59
}
if component == 2 {
let row = componentThree[row]
return 59
}
if component == 3 {
let row = componentFour[row]
return 10
}
return row
}
```
You can't just change the delegate method. This is the correct one:
func pickerView(_ pickerView: UIPickerView,
titleForRow row: Int,
forComponent component: Int) -> String?
You have:
func pickerView(_ pickerView: UIPickerView,
titleForRow row: Int,
forComponent component: Int) -> Int
You must return a String? no matter what -- if you change the function's return type to Int, it doesn't match the protocol requirement.
The good thing is that you can easily make a String from an Int. Just use String interpolation like this:
/// Inside pickerView(_:titleForRow:forComponent:)
let row = componentOne[row]
return "\(1000)"
}
I have two (or more) text fields in an app, and I want each textField to pop up its own Pickerview wheel when selected... Here's what I've got so far:
var pickerView = UIPickerView()
#IBOutlet weak var serviceType: UITextField!
#IBOutlet weak var brandsField: UITextField!
var serviceArray = ["Services", "..."]
var brandsArray = ["Apple", "Samsung"]
override func viewDidLoad() {
super.viewDidLoad()
pickerView.delegate = self
pickerView.dataSource = self
}
func updatePicker(){
self.pickerView.reloadAllComponents()
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if serviceType.isFirstResponder{
return serviceArray.count
} else if brandsField.isFirstResponder{
return brandsArray.count
}
} //ERROR HERE
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if serviceType.isFirstResponder{
return serviceArray[row]
}else if brandsField.isFirstResponder{
return brandsArray[row]
}
} //ERROR HERE
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if serviceType.isFirstResponder{
let itemselected = serviceArray[row]
serviceType.text = itemselected
}else if brandsField.isFirstResponder{
let itemselected = brandsArray[row]
brandsField.text = itemselected
}
}
}
but it keeps giving me the error saying "Missing return in a function expected to return String?" and "Missing return in a function expected to return int" at where I've Commented. What can I do?
You can use this UITextField subclass if you want to use multiple textfields with pickerview.
class PickerTextField: UITextField,UIPickerViewDelegate,UIPickerViewDataSource {
let pickerView = UIPickerView()
var itemList = [String]()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
#objc func textEdited(_ sender:PickerTextField)
{
self.text = itemList[pickerView.selectedRow(inComponent: 0)]
}
override func draw(_ rect: CGRect) {
super.draw(rect)
self.tintColor = UIColor.clear
self.addTarget(self, action: #selector(textEdited(_:)), for: .editingChanged)
pickerView.showsSelectionIndicator = true
pickerView.delegate = self
pickerView.dataSource = self
self.inputView = pickerView
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
toolBar.tintColor = .black
toolBar.sizeToFit()
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(doneBtnAction(_:)))
let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(doneBtnAction(_:)))
toolBar.items = [cancelButton, spaceButton, doneButton]
self.inputAccessoryView = toolBar
}
#objc func doneBtnAction(_ sender:UIBarButtonItem) {
resignFirstResponder()
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return itemList.count
}
func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
let title = itemList[row]
return NSAttributedString(string: title, attributes: [NSAttributedStringKey.foregroundColor:UIColor.black])
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
self.text = itemList[row]
}
}
And assign the options list in viewDidLoad
class ViewController: UIViewController {
#IBOutlet weak var serviceType: PickerTextField!
#IBOutlet weak var brandsField: PickerTextField!
override func viewDidLoad() {
super.viewDidLoad()
serviceType.itemList = ["Services", "others"]
brandsField.itemList = ["Apple", "Samsung"]
}
}
Assign subclass to textfield.
Result
You need to return the else case.
if serviceType.isFirstResponder{
return serviceArray.count
} else if brandsField.isFirstResponder{
return brandsArray.count
} else {
// insert your else case here
return 0 // for example
}
Refactor these 3 methods to this , as the compiler won't know what to return if all if cases are not hit , so it gives this error
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if serviceType.isFirstResponder{
return serviceArray.count
} else {
return brandsArray.count
}
} //ERROR HERE
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if serviceType.isFirstResponder{
return serviceArray[row]
}else {
return brandsArray[row]
}
} //ERROR HERE
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if serviceType.isFirstResponder{
let itemselected = serviceArray[row]
serviceType.text = itemselected
}else {
let itemselected = brandsArray[row]
brandsField.text = itemselected
}
}
I have this problem: I have two text fields, one pickerView containing an array and another text field where you need to put in a specific code depending on the selection you made in the pickerView to be able to press the button. This is what I got
var SchoolsArray = ["Option 1",
"Option 2",
"Option 3",
"Option 4"]
var code1 = "zxy" // code for Option 1
var code2 = "gbv" // code for Option 2
var code3 = "jwn" // code for Option 3
var code4 = "hqc" // code for Option 4
#IBOutlet weak var firstNameTxtField: UITextField!
#IBOutlet weak var schoolNameTxtField: UITextField!
#IBOutlet weak var schoolCodeTxtField: UITextField!
#IBAction func createAccountBtnPressed(_ sender: Any) {
if firstNameTxtField.text != nil && schoolNameTxtField.text != nil && schoolCodeTxtField.text != nil {
if schoolNameTxtField.text == "Option 1" && schoolCodeTxtField.text == code1 {
//do something here
} else {
}
} else {
}
}
As you can see this only works if you select Option 1. How can I make this work so if you select "Option 1" and in schoolCodeTxtField put in "zxy" it will proceed and if you select "Option 2" and put in "gbv" it will also proceed and so on. I hope you understand what I mean. I appreciate all help
Just like SchoolArray, you can use an array for the codes as well, and use following method:
var CodesArray = ["zxy", "gbv", "jwn", "hqc"]
#IBAction func createAccountBtnPressed(_ sender: Any) {
guard
firstNameTxtField.text != nil,
let option = schoolNameTxtField.text,
let index = SchoolsArray.index(where: { $0 == option }),
CodesArray[index] == schoolCodeTxtField.text
else {
return
}
// Code & Option both matched
}
How about use a dictionary that contains your options as keys and your codes as values. Something like this:
var SchoolsOptions = ["Option 1": "zxy",
"Option 2": "gbv",
"Option 3": "jwn",
"Option 4": "hqc"]
#IBAction func createAccountBtnPressed(_ sender: Any) {
if firstNameTxtField.text != nil && schoolNameTxtField.text != nil && schoolCodeTxtField.text != nil {
for (option, code) in SchoolsOptions {
if schoolNameTxtField.text == option && schoolCodeTxtField.text == code {
//do something here
// You only get here if the option and code match for that given school. If you need specific logic for each school you'll have to check which option you're on.
}
}
} else {
}
}
This is the cleanest solution I can think of right off the bat.
#IBOutlet weak var firstNameTxtField: UITextField!
#IBOutlet weak var schoolNameTxtField: UITextField!
#IBOutlet weak var schoolCodeTxtField: UITextField!
var codeArray: [String] = [
"zxy",
"gbv",
"jwn",
"hqc"
]
var selectedCode: String!
override func viewDidLoad() {
super.viewDidLoad()
setupPickerViewAndAssignItsDelegateAndDatasource()
guard let selectedCode = codeArray.first else { return }
self.selectedCode = selectedCode
}
public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return codeArray.count
}
public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return "Option \(row + 1)"
}
public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedCode = codeArray[row]
}
#IBAction func createAccountBtnPressed(_ sender: Any) {
if firstNameTxtField.text != nil && schoolNameTxtField.text != nil && schoolCodeTxtField.text != nil {
if schoolNameTxtField.text == selectedCode {
//do something here
} else {
}
} else {
}
}
i get data from a web service and my pickerData Array don't save values when i want to use it outside of the Json Parsing bloc.
here's my code
var pickerData: [String] = [String]()
var mag : String!
override func viewDidLoad() {
super.viewDidLoad()
NomMAG.alpha = 0
// \(detectionString)
let str = "http://vps43623.ovh.net/yamoinscher/api/getAllMag"
let url = NSURL(string: str)!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(urlContent, options: [])
if let jsonResult = jsonObject as? [String:AnyObject] {
if let Pick = jsonResult["magasin"] as? [[String:String]] {
for categorie in Pick {
self.mag = categorie["libelle"]!
self.pickerData.append(magasin)
//self.pickerData = [(self.produits[0].magasin)]
}
print(self.pickerData)
dispatch_async(dispatch_get_main_queue()) {
self.picker1.reloadInputViews()
// print(self.produits.count)
}
}
}
} catch {
print("JSON serialization failed", error)
}
} else if let connectionError = error {
print("connection error", connectionError)
}
}
task.resume()
//print(produits.count)
//pickerData = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "autre"]
//print(self.pickerData)
self.picker1.delegate = self
self.picker1.dataSource = self
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// The number of columns of data
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
// The number of rows of data
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.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 self.pickerData[row]
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerData[row] == "autre"
{
//print(row)
NomMAG.alpha = 1
}
else
{
//print(row.description)
NomMAG.alpha = 0
}
}
I want to get my PickerView full with the data i gained from the JsonParsing and the PickerData Array is null outside the block of code of the Json
Connect delegate and dataSource of the picker view in Interface Builder and replace viewDidLoad with
var pickerData = [String]()
override func viewDidLoad() {
super.viewDidLoad()
let str = "http://vps43623.ovh.net/yamoinscher/api/getAllMag"
let url = NSURL(string: str)!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(urlContent, options: [])
if let jsonResult = jsonObject as? [String:AnyObject],
magasin = jsonResult["magasin"] as? [[String:String]] {
// filter valid items, map them to an array and filter empty strings
self.pickerData = magasin.filter { $0["libelle"] != nil }.map { $0["libelle"]! }.filter { !$0.isEmpty}
}
dispatch_async(dispatch_get_main_queue()) {
self.picker1.reloadAllComponents()
}
} catch {
print("JSON serialization failed", error)
}
} else if let connectionError = error {
print("connection error", connectionError)
}
}
task.resume()
}
You have to add the NomMAG line
My "picker2" pickerview does not show data from "modelArray" array. When I dup the array I have data in it. But no data shown in pickerview at all.
I did everything on mind and did my research how to do it. But no success.
Any help will be highly appreciated :)
import UIKit
class MakeViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
#IBOutlet weak var txt_make: UITextField!
#IBOutlet weak var txt_model: UITextField!
var makeArray = [String]()
var modelArray = [String]()
var picker = UIPickerView()
var picker2 = UIPickerView()
var numberOfRowsMake = 0
var numberOfRowsModel = 0
override func viewDidLoad() {
super.viewDidLoad()
picker.delegate = self
picker.dataSource = self
picker2.delegate = self
picker2.dataSource = self
txt_make.inputView = picker
txt_model.inputView = picker2
txt_make.clearButtonMode = .Always
txt_model.clearButtonMode = .Always
parseJSON2()
}
func parseJSON2() {
let path : String = NSBundle.mainBundle().pathForResource("json2", ofType: "json")! as String
let jsonDATA = NSData(contentsOfFile: path) as NSData!
numberOfRowsMake = readableJSON["Cars"].count
for var i = 0; i < numberOfRowsMake; ++i {
let make = readableJSON["Cars"][i]["Manufacturer"].stringValue
makeArray.append(make)
}
}
func parseJSON3() {
let path : String = NSBundle.mainBundle().pathForResource("json2", ofType: "json")! as String
let jsonDATA = NSData(contentsOfFile: path) as NSData!
numberOfRowsMake = readableJSON["Cars"].count
for var i = 0; i < numberOfRowsMake; ++i {
let make = readableJSON["Cars"][i]["Manufacturer"].stringValue
let model = readableJSON["Cars"][i]["Model"].stringValue
print(txt_make.text)
if make == txt_make.text {
modelArray.append(model)
}
}
dump(modelArray)
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView == picker {
return numberOfRowsMake
}
if pickerView == picker2 {
return numberOfRowsModel
}
return 1
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == picker {
txt_make.text = makeArray[row]
}
if pickerView == picker2 {
self.picker2.reloadAllComponents()
parseJSON3()
txt_model.text = modelArray[row]
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView == picker {
return makeArray[row]
}
if pickerView == picker2 {
return modelArray[row]
}
return ""
}
}
Here is the local JSON file.
{
"Cars" :
[{
"Manufacturer": "ABARTH1",
"Model": "500, 2012 onwards",
}, {
"Manufacturer": "ABARTH1",
"Model": "500, 2012 onwards",
}, {
"Manufacturer": "ABARTH1",
"Model": "500, 2012 onwards",
}, {
"Manufacturer": "ABARTH2",
"Model": "500, 2012 onwards",
}, {
"Manufacturer": "ABARTH2",
"Model": "500, 2012 onwards",
}, {
"Manufacturer": "ABARTH2",
"Model": "500, 2012 onwards",
}
]
}