Accessing information from another viewController - swift

I am new to swift and I am trying to repopulate a pickerview using an array of names from another class. I have this class Notes that creates notes and each note has a name/title, I am trying to access those names/title and put them in pickerview, can anyone help me.
import UIKit
var allNotes: [Note] = []
var currentNoteindex: Int = -1
var noteTable: UITableView?
let kAllNotes:String = "notes"
class Note: NSObject
{
var date:String
var note:String
var name:String
override init()
{
date = NSDate().description
name = ""
note = ""
}
func dictionary() -> NSDictionary
{
return ["note": note, "date": date, "name": name]
}
I have two different pickerview and one works it's just the one with the names doesn't work.
override func viewDidLoad()
{
super.viewDidLoad()
fileNamePicker.dataSource = self
fileNamePicker.delegate = self
gradePicker.dataSource = self
gradePicker.delegate = self
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
{
if pickerView == gradePicker
{
return grades.count
}
else
{
return allNotes.count
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
{
if pickerView == gradePicker
{
return grades[row]
}
else
{
//This is where the Problem is, but I don't know how to fix it.
let nameFiles = allNotes[currentNoteindex].name
return nameFiles[row]
}
}
func numberOfComponents(in pickerView: UIPickerView) -> Int
{
return 1
}

nameFiles is accessing a name from a note.
nameFiles[row] doesn't work because nameFiles is not an array.
If you want to return a string, simply do return nameFiles. Check first to make sure that nameFiles actually has a value, though!

Looks like your allNotes array is empty. You have initilaize but does not seems to append the object. Create initiliaze method inside Note class
init(_ date: String, note: String, name: String) {
self.date = date
self.note = note
self.name = name
}
In your viewcontroller class try appending like this. Now below you can access inside picker view method:-
//You can append by doinf iteration based on your data
allNotes.append(Note(date: "value1", note: "Value", name: "name"))

Related

Swift - Passing data from model to controller using delegate design pattern

i am a swift learner, and need assistance in passing data
Here i have ViewController and CoinManager model. What i am trying to do is call didUpdatePrice method that's declared inside my ViewController from CoinManager, this function is responsible to update the UI. I have implemented CoinManagerDelegate protocol in ViewController, but every time i am trying to call didUpdatePrice method it throws an error like this:
Type '(any CoinManagerDelegate)?' has no member 'didUpdatePrice'
When i tried to declare delegate in my ViewController like this:
coinManager.delegate = self
it also returns an error like this:
Cannot assign value of type 'ViewController' to type '(any
CoinManagerDelegate)?.Type'
Here's my ViewController code:
import UIKit
class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate, CoinManagerDelegate {
var coinManager = CoinManager()
override func viewDidLoad() {
super.viewDidLoad()
coinManager.delegate = self
currencyPicker.dataSource = self
currencyPicker.delegate = self
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return coinManager.currencyArray.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return coinManager.currencyArray[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let selectedCurrency = coinManager.currencyArray[row]
coinManager.getCoinPrice(for: selectedCurrency)
}
#IBOutlet weak var currencyPicker: UIPickerView!
#IBOutlet weak var currencyLabel: UILabel!
#IBOutlet weak var bitcoinLabel: UILabel!
func didUpdatePrice(price: String, currency: String) {
DispatchQueue.main.async {
self.bitcoinLabel.text = price
self.currencyLabel.text = currency
}
}
}
And here's my CoinManager model code:
import Foundation
protocol CoinManagerDelegate: NSObjectProtocol {
func didUpdatePrice(price: String, currency: String)
}
struct CoinManager {
var delegate = CoinManagerDelegate?.self
let baseURL = "https://rest.coinapi.io/v1/exchangerate/BTC"
let apiKey = "#API_KEY"
let currencyArray = ["AUD", "BRL","CAD","CNY","EUR","GBP","HKD","IDR","ILS","INR","JPY","MXN","NOK","NZD","PLN","RON","RUB","SEK","SGD","USD","ZAR"]
func getCoinPrice(for currency: String){
let urlString = "\(baseURL)/\(currency)?apikey=\(apiKey)"
if let url = URL(string: urlString) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
print("error")
}
if let safeData = data {
let data = parseJSON(safeData)
let dataString = String(format: "%.2f", data!)
self.delegate.didUpdatePrice(price: dataString, currency: currency)
}
}
task.resume()
}
}
func parseJSON(_ data: Data) -> Double? {
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(CoinData.self, from: data)
let lastPrice = decodedData.rate
return lastPrice
} catch {
return nil
}
}
}
Any help is much appreciated!
On the internet, this method should works fine. But it didn't work on my machine.
I've tried to surf the internet and implemented their answers, but it didn't help me, i tried to ask chatGPT as well but the result is nil TwT. Thanks a lot fellow coders!
change the following in your code this should work at your end
in ViewController change coinManager.delegate = ViewController to coinManager.delegate = self
and in CoinManager update the following
var delegate = CoinManagerDelegate?.self
to
var delegate : CoinManagerDelegate?
on call back with delegate first make sure delegate connection is successfully connect
if let del = self.delegate
{
del.didUpdatePrice(price: dataString, currency: currency)
}

How to add a special character in a UIPicker view of integers

Still Learning swift
What Im trying to do is add a "°C" and "°F"
after an integer, I have a created and array of ints and gave them a specific range
that should be in the pickerView, I got the values to display Im just struggling with how I could get that "special char " to display after the integer
I was thinking of maybe creating a for loop and load in the the values like that and append the "°C" and "°F" using the unicode scalar.
I was wondering if anyone has any recommendations of how i could get this done ?
class tempConversionTabViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
var celsius: [Int] = []
var fahrenheitTemps: [Int] = []
override func viewDidLoad() {
super.viewDidLoad()
fahrenheitTemps += -129...134
celsius += -90...57
// for fahrenheitTemps in -129...134{
// tempPicker.append
//}
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
switch
segmentControl.selectedSegmentIndex{
case 0:
return fahrenheitTemps.count
default:
return celsius.count
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int , forComponent component: Int) -> String?{
switch segmentControl.selectedSegmentIndex {
case 0:
var selected = fahrenheitTemps[tempPicker.selectedRow(inComponent: 0)]
selected = (selected - 32) * 5/9
convertedLabel.text = String("\(selected) \u{2103}")
let myString = fahrenheitTemps.map { String($0)}
return myString[row]
default:
var selected = celsius[tempPicker.selectedRow(inComponent: 0)]
selected = (selected * 9/5) + 32
convertedLabel.text = String("\(selected) \u{2109}")
let myString = celsius.map { String($0)}
return myString[row]
}
}
Image of how I want it to display
Just use string concatenation to add the character:
Change:
return myString[row]
to:
return myString[row] + "\u{2109}"

Build a Dictionary Class with Static Data (Swift)

I need some help to build a Class that will serve as basis for all static content my app will use like States, Cities, Countries, Company classification among others. I'm not expecting those to change over time (or there would be very little change).
As a reference, I'll consider for now Countries and Cities (the logic would be the same for all others)
Class Country:
import UIKit
class Country {
var name: String
var cities: [City]
init(name:String, cities:[City]) {
self.name = name
self.cities = cities
}
}
Class City:
import UIKit
class City {
var name: String
init(name:String) {
self.name = name
}
}
In my existing code, I use the reference to those 2 classes but I feed the data in my ViewController.
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var pickerView: UIPickerView!
#IBOutlet weak var countryLbl: UILabel!
var countries = [Country]()
override func viewDidLoad() {
super.viewDidLoad()
pickerView.delegate = self
pickerView.dataSource = self
// Adding Countries and Cities
countries.append(Country(name: "UK", cities: [City(name: "London"),City(name: "Manchester"), City(name: "Bristol")]))
countries.append(Country(name: "USA", cities: [City(name: "New York"),City(name: "Chicago")]))
countries.append(Country(name: "China", cities: [City(name: "Beijing"),City(name: "Shanghai"), City(name: "Shenzhen"), City(name: "Hong Kong")]))
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}
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].name
} else {
let selectedCountry = pickerView.selectedRow(inComponent: 0)
return countries[selectedCountry].cities[row].name
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
pickerView.reloadAllComponents()
let selectedCountry = pickerView.selectedRow(inComponent: 0)
let selectedCity = pickerView.selectedRow(inComponent: 1)
let cityR = countries[selectedCountry].cities[selectedCity].name
countryLbl.text = "The right answer was: in \(selectedCountry) in \(cityR)"
}
}
To make my code cleaner, I wanted to move code below to another class and call it from my ViewController
// Adding Countries and Cities
countries.append(Country(name: "UK", cities: [City(name: "London"),City(name: "Manchester"), City(name: "Bristol")]))
countries.append(Country(name: "USA", cities: [City(name: "New York"),City(name: "Chicago")]))
countries.append(Country(name: "China", cities: [City(name: "Beijing"),City(name: "Shanghai"), City(name: "Shenzhen"), City(name: "Hong Kong")]))
}
replaced by something like
let countriesAndCities = Dicitionary.getCountriesAndCities
// Here I would expect countriesAndCities to have the same content as countries.append(...) above.
Is this doable?
Thanks
As per rmaddy answer you can do your own json file by:
Creating your empty fil, save it as json file and in there you can paste the countries and cities.
Kindly see the code how I implement the pickerView in viewController..
If you can notice I print the "countryList" in my code below so I can check if the array of countriesJson is not empty. (Please check your logs)
Update for 2 components in pickerView.
enter image description here

How can I get value of a Dictionary item from first item of an Array in Swift?

I have defined Dictionary in an Array object below:
var dataList : [[String:Any]]?
And then I have loaded some data in to it. Here is the output when i run this code below:
print("DATA LIST:\n \(myRecordList)")
Output:
Optional([["itemCode": 0, "itemText": please select city], ["itemCode": 1, "itemText": City A], ["itemCode": 2, "itemText": City B], ["itemCode": 3, "itemText": City C], ["itemCode": 4, "itemText": City D], ["itemCode": 5, "itemText": City E], ["itemCode": 6, "itemText": City F]])
I would like to reach value of a dictionary from first item in an Array:
As you know, here is the first item in an array:
["itemCode": 0, "itemText": please select city]
And here is the value in above dictionary:
"itemText": please select city
After that I would like to set above text ("please select city") as a button title if dataList count is not zero:
if dataList?.count != 0
{
**//need help in here!!!**
{
self.btnSelectMarkaOutlet.setTitle(tempBtnTitle, for: .normal)
}
}
I have tried below scope. But it crushed:
if let newTry = dataList![0]["itemText"] as? [String]
{
print("here is the output:: \(newTry[0])")
}
Here is the error:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
let tempBtnTitle = must be "please select city"
I wanted to do this.
Let me share whole view controller file. Because dataList is nil.
import UIKit
class SinyalSeviyesineGoreAramaYapViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var btnSelectMarkaOutlet: UIButton!
#IBOutlet weak var pickerViewOutlet: UIPickerView!
var dataList : [[String:Any]]?
override func viewDidLoad() {
print("viewDidLoad")
super.viewDidLoad()
self.pickerViewOutlet.delegate = self
self.pickerViewOutlet.dataSource = self
self.loadSayacRecordsForTheUIPickerView()
print("Is the data list nil? : \(self.dataList)")
if let newTry = self.dataList![0]["itemText"] as? String
{
print("here is the output:: \(newTry)")
}
//I WANTED TO SET BUTTON TITLE !!
let tempButtonTitle = ""
self.btnSelectMarkaOutlet.setTitle(tempButtonTitle, for: .normal)
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let recordName = self.dataList![row]
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
//return self.cityList![row]
let info = self.dataList![row]
if let name = info["itemCode"] as? String,
let code = info["itemText"] as? String
{
let text = "\(name) - \(code)"
//print("am i here? \(text)")
return text
}
return nil
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.dataList == nil ? 0 : self.dataList!.count
}
func loadSayacRecordsForTheUIPickerView()
{
print("loadSayacRecordsForTheUIPickerView method is called")
ServiceManager.oServiceManager.loadSayacRecords()
{
(result) in
if let sayacRecords = try? JSONSerialization.jsonObject(with: result!, options: .mutableContainers) as? [String:Any]
{
if let resultCodeFromWebApi = sayacRecords?["resultCode"] as? String
{
DispatchQueue.main.async
{
print("loadSayacRecordsForTheUIPickerView resultCode: \(resultCodeFromWebApi)")
if resultCodeFromWebApi == "999"
{
if let myRecordList = sayacRecords?["recordList"] as? [[String:Any]]
{
//DispatchQueue.main.async
//{
self.dataList = myRecordList
print("DATA LIST:\n \(self.dataList)")
self.pickerViewOutlet.reloadAllComponents()
//}
}
} // resultCodeFromWebApi == 999 ENDS
else
{
if let resultMessageFromWebApi = sayacRecords?["resultMessage"] as? String
{
print("resultMessage: \(resultMessageFromWebApi)")
}
}
} // DispatchQueue.main.async ENDS
}
else
{
print("cant parse it")
} // if let resultCodeFromWebApi = loginResult?["resultCode"] as? Int ENDS
}
}
} // loadSayacRecordsForTheUIPickerView ENDS
} // class SinyalSeviyesineGoreAramaYapViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource ENDS
The problem is in the logic you implemented: the dataList is loaded asynchronously, which means that you don't know when it will be available, but you are using it like it's always available.
Example of this error is in the viewDidLoad:
self.loadSayacRecordsForTheUIPickerView()
print("Is the data list nil? : \(self.dataList)")
if let newTry = self.dataList![0]["itemText"] as? String
{
print("here is the output:: \(newTry)")
}
With the first line you start to load the dataList, but in the for loop you are force unwrapping, which means you are saying "Ehi I'm 100% sure it will be available" - which is not true.
A possible solution for your problem is to convert the dataList! to dataList? everywhere, so if it's not loaded then nothing happens and your app won't crash.
A better and quicker solution is to have a default value for dataList, so in case is not loaded you will just react to an empty data model:
var dataList : [[String:Any]] = [[String:Any]]()
A reason would be array is nil and also you are unwrapping with [String] which is String, which may give nil.
Try below to avoid crashes.
if dataList?.isEmpty == false,let firstDic = dataList?.first, let neededString = firstDic["itemText"] as? String {
}

Issue saving PFobjects into an array when using pickerView

i'm relatively new to Swift. I've encountered an issue when using PFQuery in pickerView.
I'm trying to implement a 2 component pickerView as demonstrated below:
Component 0: Component 1
"A": "Altivo", "Altrez"
"B": "Beverly", "Bijou"
Choosing "A" from Component 0 will show "Altivo", "Altrez" in Component 1
The values from Component 1 are queried from Parse by using the value from Component 0
Here's my code:
var condoNameList: [String] = [String]()
var condoPicker = UIPickerView()
var condoAlphabet: [String] = [String]()
private let prefixComponent = 0
private let condoNameComponent = 1
override func viewDidLoad() {
super.viewDidLoad()
condoAlphabet = ["A", "B"]
}
//Start: For condoName picker
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 2
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if component == prefixComponent{
return condoAlphabet.count
}else {
return self.condoNameList.count
}
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if component == prefixComponent {
condoNameList.removeAll()
let query = PFQuery(className: "condodirectory")
let selectedalphabet: String = condoAlphabet[row] as String!
query.whereKey("condoName", hasPrefix: selectedalphabet)
query.findObjectsInBackgroundWithBlock{
(objects:[PFObject]?, error:NSError?)->Void in
if error == nil{
for object in objects!{
//print(object)
let condoName:String = object["condoName"] as! String
self.condoNameList.append(condoName)
}
}
//print("condo Name List is \(self.condoNameList)") //Position 1
}
print("condo Name List is \(self.condoNameList)") //Position 2
condoPicker.reloadComponent(condoNameComponent)
condoPicker.selectRow(0, inComponent: condoNameComponent, animated: true)
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == prefixComponent{
return condoAlphabet[row]
}else {
return self.condoNameList[row]
}
}
The issue is at Position 1, the print function is able to print out the condoNameList but at Position 2 it prints an empty array. As a result, the list for Component 1 is not showing up in the PickerView.
Anyone knows why?
Thanks in advance for the help.
I managed to solve the problem from another thread by using the didset{} method.
var condoNameList: [String] = [] {
didSet {
self.condoNameListTemp = condoNameList
condoName.inputView = condoPicker
condoPicker.reloadAllComponents()
}
}