SWIFT core data load to pickerview - swift

It appears to be nowhere found. Ive got Core data and needs them load to picker view. How to do it? I think it needs to work with array[String] and not array[AnyObject].
var myList : Array<String> = []
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
var appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
var context: NSManagedObjectContext = appDelegate.managedObjectContext!
var request = NSFetchRequest(entityName: "Activity")
var results: Array = context.executeFetchRequest(request, error: nil)!
//???
return myList[row]
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){
itemActivityValue = myList[row]
}

Do not make fetch requests in the pickerView delegate/datasources. You only need to do fetch once, and you're storing your data in a view controller, so you should do it in viewDidLoad.
The array type is NSManagedObject, not String.
You can't dump NSManagedObject into the pickerView rows. NSManagedObjects will likely have your own variables, for instance, a name attribute. In titleForRow, you want to return something like return myList[row].name, if you've made a subclassed NSManagedObject.
If not, you'll need to do KVC to grab the value.

Related

How to make user generated core data entities options in a UIPickerview

So I'm working on a calculator app and I've hit a wall when it comes to my UIPickerView and my core data. I currently have it set up so the user can save settings using a core-data entity. This also pushes the name attribute of the entity to an array. The UIPickerView then uses the names in the array to auto-populates fields using the selected core-data entity. However, the array I'm currently using does not persist after closing the app. I'm looking for a way to push the name attributes to an array in such a way that the array stores the data after restarting the app. That way the UIPickerView can access the names after restart.
Here's the way I'm currently doing things.
var pickerOptions = ["none"]
...
#IBAction func saveSettings (_ sender: Any){
...code to save settings...
coreDataManager.shared.createSetting(power: power, name:name)
pickerOptions.append(name)
}
func numberOfcomponents(in pickerView: UIPickerView)-> Int {
return 1}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component:Int) ->Int{
return pickerOptions.count}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, inComponent component: Int){
return pickerOptions[row]}
func pickerView(_ pickerView: UIPickerView, didSelectRow row:Int, inComponent component: Int){
let setting = coreDataManager.shared.fetchSetting(withName: pickerOptions[row])
...code for auto-populating...}
Okay so I figured it out. I wrote a function in my core data handler to dump all my entities into an array. Then it loops through the array pulling out all the .name attributes. It then returns that array. Then in my view controller I call the function and set it equal to a variable. I then use that variable for my UIPickerView array. Code solution below.
//function to generate array from core data. This happens in my coreDataManager file.
func fetchSettingName() ->[String] {
let context = persistentContainer.viewContext
var settingOptions = ["none"]
let fetchRequest = NSFetchRequest<Settings>(entityName: "Settings")
do{
let settings = try context.fetch(fetchRequest)
for setting in settings{
settingOptions.append("\(setting.name!)")
}
return settingOptions
}catch let fetchError {
print("Failed to fetch companies: \(fetchError)")
}
return [""]
//How I implemented it into my UIPickerView.This happens in my view controller
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
//calls an array out of core data. The array contains all the title properties for all setting entities
let settingOptions = coreDataManager.shared.fetchPresetsName()
return settingOptions.count

UIPickerView not returning the correct row that the user has selected

So I just used this SO Post in order to add a second pickerView to my ViewController. I used the answer from the user 'LAOMUSIC Arts', which implemented the idea of using tags from the accepted solution.
Here is my implementation:
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate{
var molyRow = ""
var tungRow = ""
var molyPickerViewOptions = ["mils", "mm", "in."]
var tungPickerViewOptions = ["mils", "mm", "in."]
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if (pickerView.tag == 1){
return molyPickerViewOptions.count
}else{
return tungPickerViewOptions.count
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if (pickerView.tag == 1){
molyRow = "\(molyPickerViewOptions[row])"
print("molyRow = ", "\(molyPickerViewOptions[row])")
return "\(molyPickerViewOptions[row])"
}else{
tungRow = "\(tungPickerViewOptions[row])"
print("tungRow = ", "\(tungPickerViewOptions[row])")
return "\(tungPickerViewOptions[row])"
}
}
As you can see, I have it print the tungRow and molyRow along with their corresponding values. This way, as I'm running the app in the simulator, I can see what value it is getting.
When I tried this, I stumbled on something really weird. It will properly return "mils" and "mm" when those are selected in the simulator, but if I 'flick' from either the "mils" or "mm" row down to the "in." row, it will for some reason return "mils". I'll attach a video to show you.
Video
As you can see, the picker seems to correctly return "mils" and "mm" most of the time, however based on how I 'flick' the picker, "in." will not always be returned when it should.
Please let me know if there is anything I can do to make this post better, more specific, etc.
Looking forward to a response. Thanks in advance!
EDIT: After getting a suggestion to try pickerView(_:didSelectRow:inComponent:), I tried implementing the function to both return, and print the row number. I edited the pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) function so that it simply sets the title:
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if (pickerView.tag == 1){
return "\(molyPickerViewOptions[row])"
}else{
return "\(tungPickerViewOptions[row])"
}
}
private func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) -> Int{
if (pickerView.tag == 1){
molyRow = row
print("molyRow = ", "\(row)")
return row
}else{
tungRow = row
print("tungRow = ", "\(row)")
return row
}
}
Note that when I tried implementing the pickerView(_:didSelectRow:inComponent:) function, Xcode warned me: Instance method 'pickerView(_:didSelectRow:inComponent:)' nearly matches optional requirement 'pickerView(_:didSelectRow:inComponent:)' of protocol 'UIPickerViewDelegate' Make 'pickerView(_:didSelectRow:inComponent:)' private to silence this warning
I have tried running with and without making the function private. Either way, it still doesn't print the row number.
It looks like you're maybe trying to determine the row selection in the pickerView(_:titleForRow:forComponent:) method. That method is being called by UIKit whenever it needs to determine the text to display in a picker row.
To find out which row was selected whenever the selection changes, you should check out the pickerView(_:didSelectRow:inComponent:) method of UIPickerViewDelegate.
EDIT
Swift isn't calling the method you've written because it doesn't match the signature of the delegate protocol's method. pickerView(_:didSelectRow:inComponent:) is not the same as pickerView(_:didSelectRow:inComponent:) -> Int. This is why you got that compiler warning about the method name almost—but not quite—matching.
To get UIKit to see the method as the one it should call, you need to remove the Int return. If the method signature matches, and your class is properly assigned as the picker's delegate (which it seems to be since titleForRow: is getting called), then UIKit will see it and call it.

xcode cannot assign value of type.. to NSSet

I am working on CoreData with Swift 3 and I am almost done. I did the CoreData entities and now I am trying to save data from a form on the app and I got this error:
import UIKit
import CoreData
class ItemDetailsVC: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var storePicker: UIPickerView!
#IBOutlet weak var titleField: CustomTextField!
#IBOutlet weak var priceField: CustomTextField!
#IBOutlet weak var detailsField: CustomTextField!
var stores = [Store]()
override func viewDidLoad() {
super.viewDidLoad()
storePicker.delegate = self
storePicker.dataSource = self
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
let store = stores[row]
return store.name
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return stores.count
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// update
}
func getStores() {
let fetchRequest: NSFetchRequest<Store> = Store.fetchRequest()
do {
self.stores = try context.fetch(fetchRequest)
self.storePicker.reloadAllComponents()
} catch {
// some code
}
}
#IBAction func savePressed(_ sender: UIButton) {
let item = Item(context: context)
if let title = titleField.text {
item.title = title
}
if let price = priceField.text {
item.price = (price as NSString).doubleValue
}
if let details = detailsField.text {
item.details = details
}
item.toStore = stores[storePicker.selectedRow(inComponent: 0)] error: cannot assign value of type 'Store' to value of type NSSet?
}
I am not asking anyone to solve the error, I just wanna know what the error is telling me to do. Error is on the item.toStore = stores[storePicker... line
When you set a relationship on CoreData model to To Many it starts behaving like an Array. And like so you need to append the data to that array instead of assigning an class to it. You can do so with:
.addingObjects(from: [Any])
I am not asking anyone to solve the error, I just wanna know what the
error is telling me to do.
From what I have understood is that toStore is a to many relationship from Item to Store entity in CoreData.
Now since you need to know about the error only ,if I am correct and the relationship is exactly above then it means that Item entity has a set of stores associated with it not a single Store , hence you are getting this error
error: cannot assign value of type 'Store' to value of type NSSet?
The problem is your entity has one-to-many relationship, if you want to fix this issue try to change it "to one". I know you don't want the answer but here is the hint on how you can fix this. If you go to your *.xcdatamodeld file and locate your Item entity you can see the attributes relationships...find appropriate relationship and modify it from "to-many" to "to-one"

Xcode Swift 3 Picker: Putting the DataSource in ViewDidLoad

I am trying to set up a picker that would display different results based on certain conditions. The only issue is that I need to put the dataSource (picker data that will display) outside the viewDidLoad. I need the viewDidLoad to load the appropriate data if the condition was met. However, with code dealing with pickers that I've seen, it seems the dataSource array is placed outside the viewDidLoad (I presume so the delegate methods can use that value). I am just wondering if I can place this dataSource into viewDidLoad so that it would recognize it, or if there was a way to run a Switch or If statement outside the viewDidLoad and in the class.
import UIKit
import Parse
class VC: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
var pickerData = [String]()
#IBOutlet weak var namePicker: UIPickerView!
override func viewDidLoad() {
super.viewDidLoad()
namePicker.dataSource = self
namePicker.delegate = self
//here I want the picker to show something different depending on the value of nameOfPerson. I changed the order of the array because the picker selects the first name of the array as the default.
switch nameOfPerson
{
case "Carly":
let pickerData = ["Carly","Jeff","Bob","Anita"]
case "Jeff":
let pickerData = ["Jeff","Carly","Bob","Anita"]
case "Bob":
let pickerData = ["Bob","Carly","Jeff","Anita"]
case "Anita":
let pickerData = ["Anita","Carly","Jeff","Bob"]
}
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerData.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return pickerData[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int)
{
}
}
I am just wondering if I can place this dataSource into viewDidLoad so that it would recognize it, or if there was a way to run a Switch or If statement outside the viewDidLoad and in the class
No. The data source methods must be methods, i.e. functions at the top level of the class, in order for Objective-C to see them. Therefore the data itself must be visible to the data source methods, and the only coherent way to share data between viewDidLoad and the data source methods is as a property, like your var pickerData.
What's wrong with your code is the let:
case "Carly":
let pickerData = ["Carly","Jeff","Bob","Anita"]
case "Jeff":
let pickerData = ["Jeff","Carly","Bob","Anita"]
case "Bob":
let pickerData = ["Bob","Carly","Jeff","Anita"]
case "Anita":
let pickerData = ["Anita","Carly","Jeff","Bob"]
}
Delete the term let from each of those cases! Otherwise you are just setting a temporary local variable pickerData, which is not what you want to do. You want to set the property that you declared like this:
var pickerData = [String]()
....and your code is not doing that! What you want is this:
case "Carly":
self.pickerData = ["Carly","Jeff","Bob","Anita"]
case "Jeff":
self.pickerData = ["Jeff","Carly","Bob","Anita"]
case "Bob":
self.pickerData = ["Bob","Carly","Jeff","Anita"]
case "Anita":
self.pickerData = ["Anita","Carly","Jeff","Bob"]
}
(Plus you need a default case, of course.)
Then, at the end, just to be on the safe side, reload the data:
self.namePicker.reloadAllComponents()
Datasource needs to be set while the view loads, though you can load it anywhere, where you need it.
Inside viewDidLoad
picker.datasource = self
Anywhere, you can set the actual data;
picker.dataArray = "example, anything"
I'm not sure I understand you completely. Please let me know if you were not looking for this.

How to recognize which UIPickerView received event when they share a Data Source

I have 2 UIPickerViews with different datasource arrays. My problem is that I can't figure out how to display both datasources to my 2 picker views.
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return firstArray[row]
}
How can I return both of my datasources to my picker views? The logical solution is to recognize if the first picker view is tapped, use firstArray as datasource, and if the second picker view is tapped, use another array.
Guide me on which properties and methods to use please.
The simplest way is to declare both UIPickerViews as #IBOutlet stored properties at the top of the class (be sure to link these up properly in StoryBoard):
class MyVC: UIViewController, UIPickerViewDataSource {
#IBOutlet weak var picker1: UIPickerView?
#IBOutlet weak var picker2: UIPickerView?
/* set up the delegates ... */
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
switch pickerView {
case picker1: return firstArray[row]
case picker2: return secondArray[row]
default: /* print an error or assertion failure */ return nil
}
}
}