Using a Picker with CoreData - Saving and fetching data - ios

I am trying to save a string selection from a picker into CoreData, but it is stating
"Picker: the selection "" is invalid and does not have an associated tag, this will give undefined results." I can see all the options, but it won't let me click any of them and its throwing that error down below in the console.
I have the following:
'feeling' is defined in my CoreData Entity as a String & defined in my SleepModel type as a String
PickerValues.swift
class PickerValues {
enum Feeling: String, CaseIterable, Codable {
case great = "Great"
case ok = "OK"
case bad = "Bad"
}
}
In the AddEntryView.swift:
#State var feelingSelect = String()
Picker ("Feeling", selection: $feelingSelect, content: {
ForEach(PickerValues.Feeling.allCases, id: \.self){ feeling in
Text(feeling.rawValue).tag(feeling)
}
}).foregroundColor(.black)
//inside the function called when clicking the add entry button
var sleepModel = SleepModel(feeling: feelingSelect)

Related

SwiftUI ForEach with array or sub-array

I want to iterate an array using ForEach and, depending on the use-case, its sub-version with fewer elements.
ForEach(isExpanded ? $items : $items[..<4])
The problem is if I am using a subscript, I am getting an error Result values in '? :' expression have mismatching types 'Binding<[RowItem]>' and 'Binding<[RowItem]>.SubSequence' (aka 'Slice<Binding<Array<RowItem>>>'). I can not use it like this, because it is not an array but a Slice. Just casting to Array does not work either.
This is a sample code which responds to the poster's second question.
Use binding to store/pass/receive data from another struct:
struct Test: View {
#State var bindWithChild: String = ""
var body: some View {
Text("\(bindWithChild)")
//when you edit data in the child view, it will updates back to the parent's variable
TestChild(receiveAndUpdate: $bindWithChild)
}
}
struct TestChild: View {
#Binding var receiveAndUpdate: String
var body: some View {
TextField("Enter here", text: $receiveAndUpdate)
}
}
However, If your data only works within one struct, and you don't need to pass the data back and forth within another struct, use #State:
struct Test: View {
#State var binding: String = ""
var body: some View {
Text("\(binding)")
//when you edit data in the TextField below, it will update this Text too.
TextField("Enter data: ", text: $binding)
}
}
You don’t have to use $ sign in front of your arrays.
ForEach(isExpanded ? items[..<items.count] : items[..<4], id: \.self) { sub in
}

swiftUI using picker to show different values in the Text field than in the selector

I've got an array of strings that I want to present using swiftUI Picker widget. Each string is composed of multiple words delimited by spaces.
I'd like to get the Picker showing the full string of each item in the list, while the selection variable should only get the first word (the selected item is stored in arg)
This was my attempt to do so. notice that the object that hold the items called myHandler, and it's shared to the swiftUI view, and can be modified by external swift closure:
class myHandler: ObservableObject {
#Published var items = [String]()
}
struct ContentView: View {
#State var arg: String = ""
#ObservedObject var handler : myHandler
...
VStack {
Picker("items", selection: $arg) {
Text("AAA").tag("xxx")
Text("BBB").tag("yyy")
Text("CCC").tag("zzz")
ForEach(handler.items , id: \.self, content: {
Text($0).tag($0.components(separatedBy: " ")[0])
})
}
}
.frame()
TextField("firstword", text: $arg).frame()
For the options outside the ForEach statement, I can see that arg get the value written in the tag. However, for all options that derived from the ForEach, I see that arg equals to the iterable item ($0) which is the multi work string, and not to the first word as expected.
Any idea how to fix those items that are generated from the ForEach, so that selection of such item, will set the arg to the string value of the first word in the iterator ?
Picker("items", selection: $arg) {
ForEach(handler.items, id: \.self) {
Text($0)
.tag($0.components(separatedBy: " ").first ?? "")
}
}

List is not showing data from API in SwiftUI?

I am trying to get data from API and want to show that in List.
List {
ForEach(0 ..< (declarationViewModel.items?.count)!) { index in
HStack {
......
but as its taking some time to fetch data from API, initially data is 0 hence can not able to show data in list as it's not reloading after getting data.
public class DeclarationViewModel: ObservableObject {
#Published var items: [Item]?
#Published var title: String?
init() {
self.items = [Item]()
self.title = ""
}
init(items: [Item]?, title: String) {
self.items = items
self.title = title
}
}
Error log
forEach<Range<Int>, Int, HStack<TupleView<(HStack<TupleView<(ModifiedContent<ModifiedContent<Image, _EnvironmentKeyWritingModifier<Optional<Color>>>, _FrameLayout>, Text, Spacer)>>, Spacer, _ConditionalContent<ModifiedContent<ModifiedContent<Image, _EnvironmentKeyWritingModifier<Optional<Color>>>, _FrameLayout>, ModifiedContent<ModifiedContent<Image, _EnvironmentKeyWritingModifier<Optional<Color>>>, _FrameLayout>>, _ConditionalContent<ModifiedContent<ModifiedContent<Image, _EnvironmentKeyWritingModifier<Optional<Color>>>, _FrameLayout>, ModifiedContent<ModifiedContent<Image, _EnvironmentKeyWritingModifier<Optional<Color>>>, _FrameLayout>>)>>> count (14) != its initial count (0). `ForEach(_:content:)` should only be used for *constant* data. Instead conform data to `Identifiable` or use `ForEach(_:id:content:)` and provide an explicit `id`!
you could try something simple like this:
EDIT: and if your items are Identifiable, then remove the "id:.\self",
nothing to it.
List {
if let items = declarationViewModel.items {
ForEach(items, id: \.self) { item in
....
}
}
}
Check out your console, you'll see following warning:
ForEach<Range, Int, ModifiedContent<Text, AddGestureModifier<_EndedGesture>>> count (2) != its initial count (1). ForEach(_:content:) should only be used for constant data. Instead conform data to Identifiable or use ForEach(_:id:content:) and provide an explicit id!
Newer use 0..<shoppingList.count inside ForEach or List. At least use declarationViewModel.items.indices instead.
Make items non optional by specifying a default value of an empty array instead of initializing it in the init. You can do same with title:
#Published var items: [Item] = []
#Published var title = ""
But perfectly instead of using indices you need to confirm your Item to Identifiable and make sure each item has a unique id, so your animations will work good and SwiftUI reuse cells to increase performance. You can use UUID:
struct Item: Identifiable {
let id = UUID()
}
Then in ForEach you get item instead of index:
ForEach(declarationViewModel.items) { item in
If you also need index in ForEach, check out my ForEachIndexed in this answer

Struct Value of type 'ViewController' has no member 'Continents'?

Struct can find in ViewController.
The error show Value of type 'ViewController' has no member 'Continents'?
this massage.
struct Continents {
var continents_name_list : String
}
This is a drop-down menu code:
func getDataToDropDown(cell: UITableViewCell, indexPos: Int, makeDropDownIdentifier: String) {
if makeDropDownIdentifier == "DROP_DOWN_NEW"{
let customCell = cell as! DropDownTableViewCell
customCell.countryNameLabel.text = self.Continents[indexPos]
print("test")
}
}
func numberOfRows(makeDropDownIdentifier: String) -> Int {
return self.Continents.count
}
func selectItemInDropDown(indexPos: Int, makeDropDownIdentifier: String) {
self.continente_text_label.text = "\(self.Continents[indexPos])"
self.dropDown.hideDropDown()
}
Frist:
Last:
Continents Is a type (like Int, String ecc...).
This means you can create arrays, variables etc. of type Continents.
Calling Continents.count is like calling Int.count which means nothing.
This is why it won't work. If you want to get the count of your array, you'll have to write the name of your array.count. In your case continents_list.count.
Anyway there are a few semantical errors in your code so I'll give you a few suggestions.
I assume you want the struct Continents to contain an array of strings. At the moment it will only receive a string. Try this instead:
struct Continents {
var continents_name_list : [String]
}
Furthermore, the continents_list array you created is of type String, you should declare it like this if you want it to be of type Continents:
var continents_list = Continents(continents_name_list: ["c1", "c2", "cn"])
My last advice would be to use an enum instead of a struct to store your continents.
enum Continents {
case Europe
case America
case Africa
case Antartica
case Asia
case Oceania
}

SwiftUI List not recognizing object type

So I'm trying to make a List in SwiftUI like this:
struct DetailsView: View {
var piis = [IDPiece]()
var body: some View {
List(piis, id: \.identifier) { pii in
Text( pii.label )
}
}
}
where IDPiece looks like:
struct IDPiece: Equatable {
init() {}
init(claim: Claim) {
self.document = claim.document
self.identifier = claim.identifier
self.claimUID = claim.claimUID
self.label = claim.label
}
var document: DocumentType = .na
var identifier: String = ""
var claimUID: String = ""
var label: String?
}
But I keep receiving the following error on the line where I initialize the list:
Type '_' has no member 'identifier'
It doesn't seem to be parsing the type of object contains in my piis list. Anyone know why that may be?
SwiftUI compiler errors are generally useless (this will improve over time, but today they're useless). Your problem has nothing to do with \.identifier. The problem is you have an optional .label, but you don't handle the case where it's nil. Almost certainly you should just make label non-optional. But if it needs to be optional (if you treat nil differently than empty in some place), then you need to do something about that, such as:
Text(pii.label ?? "N/A")

Resources