How to assign the #State value to another viewModel published property - swiftui

How to assign the #State value secondMarked from one view to a #Published sampleViewModel property? like
sampleViewModel.secondMarked
Here is the example:
struct ContentView: View {
#EnvironmentObject var sampleViewModel: sampleViewModel
#State var firstMarked = false
#State var secondMarked = true
#State var thirdMarked = false
var body: some View {
VStack {
CheckboxField(id: "Completed", label: "Completed", isMarked: $firstMarked)
CheckboxField(id: "Completed", label: "Completed", isMarked: $secondMarked)
CheckboxField(id: "Completed", label: "Completed", isMarked: $thirdMarked)
}
.padding()
}
}

Instead of having #State and a #Published property for it, just use one.
For example, yours:
CheckboxField(id: "Completed", label: "Completed", isMarked: $firstMarked)
Try this:
CheckboxField(id: "Completed", label: "Completed", isMarked: $sampleViewModel.firstMarked)

We can do that with use of .onChange modifier, like
CheckboxField(id: "Completed", label: "Completed", isMarked: $secondMarked)
.onChange(of: secondMarked) {
sampleViewModel.secondMarked = $0 // << here !!
}

Related

SwiftUI TextField is always deleting itself

After updating to macOS Ventura 13.0, when i use the get: set: method inside a TextField, it now prevents me from typing in the TextField. This wasn't an issue before.
struct ContentView: View {
#State var testing1 = ""
#State var testing2 = ""
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
TextField("Enter Text", text: Binding<String>(
get: { testing1 },
set: { testing2 = ($0)}))
}
.padding()
}
}
I've tested with my other apps and the same thing is happening. Is anyone else having this issue?
You are setting your testing2 to the value that you're entering into your TextField, but in your binding, you're using testing1 as the value you're getting, with these combined, your testing1 is always empty because you're not assigning it to anything in anywhere. consider replacing your Binding with either this:
TextField("Enter Text", text: Binding<String>(
get: { testing1 },
set: { testing1 = ($0)})
)
or this
TextField("Enter Text", text: Binding<String>(
get: { testing2 },
set: { testing2 = ($0)})
)

Optimize application performance when searching in the list

How can I optimize the list filtering? There are 2000 records in the list and when entering text into the application's search engine it clips a bit. Do you have any suggestions that could optimize the search?
Code
import CoreData
import SwiftUI
struct SongbookView: View {
#Environment(\.managedObjectContext) var managedObjectContext
#FetchRequest(
entity: Song.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \Song.number, ascending: true)]
) var songs: FetchedResults<Song>
#State private var searchText = ""
var body: some View {
NavigationView{
VStack{
SearchBar(text: $searchText)
Spacer()
List(songs.filter({searchText.isEmpty ? true : removeNumber(str: $0.content!.lowercased()).contains(searchText.lowercased()) || String($0.number).contains(searchText)}), id:\.objectID) { song in
NavigationLink(destination: DetailView(song: song, isSelected: song.favorite)) {
HStack{
Text("\(String(song.number)). ") .font(.headline) + Text(song.title ?? "Brak tytułu")
if song.favorite {
Spacer()
Image(systemName: "heart.fill")
.accessibility(label: Text("To jest ulubiona pieśń"))
.foregroundColor(.red)
}
}.lineLimit(1)
}
}.id(UUID())
}
.listStyle(InsetListStyle())
.navigationTitle("Śpiewnik")
}
}
func removeNumber(str: String) -> String {
var result = str
let vowels: Set<Character> = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
result.removeAll(where: { vowels.contains($0) })
return result
}
}
extension UINavigationController {
// Remove back button text
open override func viewWillLayoutSubviews() {
navigationBar.topItem?.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
}

SwiftUI: NavigationLinks dependent on Toggle states

I am trying to create a NavigationLink destination that changes if any one of several toggle switches are set to TRUE. I have worked out the if/then logic for the links using tags and a state variable. That is, if I manually set toggleCount = the links work correctly.
What I would like to do however, is set toggleCount = Number of True Switches. You can see my failed attempt to loop through the array and increment toggleCount. It fails because it does not conform to View.
Any advice on a clean way to implement this? I don't necessarily need to count the number of true switches, I just need to know if any one of them were set to true.
import SwiftUI
struct ToggleStruct : Identifiable {
var id : Int
var name : String
var position : Bool
}
struct ContentView: View {
#State var toggleArray = [ToggleStruct(id: 1, name: "Toggle 1", position: false),
ToggleStruct(id: 2, name: "Toggle 2", position: false),
ToggleStruct(id: 3, name: "Toggle 3", position: false)
]
#State private var selection: Int? = nil
var toggleCount = 0
var body: some View {
NavigationView {
VStack {
Text("Toggle Count: \(toggleCount)")
ForEach(0..<toggleArray.count) { i in
Toggle(isOn: $toggleArray[i].position, label: {
Text(toggleArray[i].name)
})
}
NavigationLink(
destination: Text("Destination A"),
tag: 1,
selection: $selection,
label: {EmptyView()})
NavigationLink(
destination: Text("Destination B"),
tag: 2,
selection: $selection,
label: {EmptyView()})
Button(action: {
// ForEach(0..<toggleArray.count) { x in
// if toggleArray[x].position {
// toggleCount += 1
// }
// }
if toggleCount > 0 {
selection = 1
} else {
selection = 2
}
}, label: {
Text("Continue")
})
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Would something like this work for you? You could change the logic in destinationView to be whatever you needed.
import SwiftUI
struct ContentView: View {
#State var toggleOneIsOn = false
#State var toggleTwoIsOn = false
#State var toggleThreeIsOn = false
var body: some View {
NavigationView {
Form {
Toggle("Toggle One", isOn: $toggleOneIsOn)
Toggle("Toggle Two", isOn: $toggleTwoIsOn)
Toggle("Toggle Three", isOn: $toggleThreeIsOn)
NavigationLink("Foo", destination: destinationView)
}
}
}
var destinationView: some View {
switch (toggleOneIsOn, toggleTwoIsOn, toggleThreeIsOn) {
case _ where toggleOneIsOn || toggleTwoIsOn || toggleThreeIsOn:
return Text("At least one toggle is on")
default:
return Text("All toggles are off")
}
}
}

Display a new view when i select a tab in the picker

I have a picker with 3 values. How to display some information based on the selection of the picker tab?
#State var tabSelectedValue = 0
var body: some View{
VStack {
Picker(selection: $tabSelectedValue, label: Text("")) {
Text("A").tag(0)
Text("B").tag(1)
Text("C").tag(2)
}.pickerStyle(SegmentedPickerStyle())
}
}
#State var tabSelectedValue = 0
var yourData = ["one", "Two", "Three"]
var body: some View{
VStack {
Text("\(yourData[tabSelectedValue])")
Picker(selection: $tabSelectedValue, label: Text("")) {
Text("A").tag(0)
Text("B").tag(1)
Text("C").tag(2)
}.pickerStyle(SegmentedPickerStyle())
}
}
Or you may use with TabView together,
struct ContentView: View {
#State var tabSelectedValue = 0
var body: some View{
VStack {
Picker(selection: $tabSelectedValue, label: Text("")) {
Text("A").tag(0)
Text("B").tag(1)
Text("C").tag(2)
}.pickerStyle(SegmentedPickerStyle())
TabView(selection: $tabSelectedValue,
content: {
Text("A Tab Content").tag(0)
Text("B Tab Content").tag(1)
Text("C Tab Content").tag(2)
})
.tabViewStyle(PageTabViewStyle())
}
}
}
struct PickerDataView: View {
var data: String
var body: some View {
Text("Picker Date \(data)")
}
}
struct ContentView: View {
#State private var tabSelectedValue = 0
private var pickerData: [String] = ["A", "B", "C"]
var body: some View {
VStack {
PickerDataView(data: "\(pickerData[tabSelectedValue])")
Picker(selection: $tabSelectedValue, label: Text("")) {
ForEach(0 ..< pickerData.count) {
Text(pickerData[$0]).tag($0)
}
}.pickerStyle(SegmentedPickerStyle())
}
}
}

presentationMode.wrappedValue.dismiss() not working properly

I'm using the sheet method to display a simple form and I pass into it a couple of varsiables. The problem is that if I click the button which performs the .dismiss() method after changing the variables passed in it doesn't work. Instead if I directly click the button it works normally.
Here's the code:
struct EditProductForm: View {
var listIndex : Int
var product : Product
#State var quantity: Int
#State var productName : String
#EnvironmentObject var data : Data
#Environment(\.presentationMode) var presentationModeEdit
func editProduct(){
self.data.editProduct(listIndex: self.listIndex, product: self.product, productName: self.productName, quantity: self.quantity)
}
var body: some View {
VStack{
Spacer()
VStack(spacing: 64){
Text("Edit Product")
TextField("Edit the name", text: $productName)
Picker(selection: $quantity, label: Text("Quantity")){
Text("OK").tag(Constants.Status.OK)
Text("Almost finished").tag(Constants.Status.ALMOST_NONE)
Text("Finished").tag(Constants.Status.NONE)
}.pickerStyle(SegmentedPickerStyle())
Button(action: {
self.editProduct()
self.presentationModeEdit.wrappedValue.dismiss()
}){
Text("Save")
}
}
Spacer()
}.padding(.horizontal)
}
}
I also checked if the isPresented variable changes value and it's actually toggled when i click the button but the sheet stays there.
Here's the code where I use the form:
ForEach(self.list.content, id: \.self) { item in
Button(action: {
self.show_modal_edit[self.list.content.firstIndex(of: item)!] = true
}){
ProductCell(item: item)
}.sheet(isPresented: self.$show_modal_edit[self.list.content.firstIndex(of: item)!]){
EditProductForm(
listIndex: self.listIndex,
product: item,
quantity: item.quantity,
productName: item.productName
).environmentObject(self.data)
}
}
show_modal_edit is a list of Bool, I checked the values and apparently the correct one is passed to the isPresented field of .sheet().
I've setup the following test version of your code and all is working well for me on ios 13.4 and macos catalyst after renaming Data to Datax.
This points to the function in editProduct()
self.data.editProduct(listIndex: self.listIndex, product: self.product, productName: self.productName, quantity: self.quantity)
as the possible source of your problem. Specifically, using Data as the name for your type. It seems to clash with the system struct Data type. Try renaming your ObservableObject class to something else (Datax in my test).
import SwiftUI
class Datax: ObservableObject {
#Published var xxx = "xxx"
func editProduct(listIndex: Int, product: String, productName: String, quantity: Int) {
print("---> editProduct")
}
}
struct ContentView: View {
var data = Datax()
#State var showEditProductForm = false
var body: some View {
VStack {
Text("main view")
Button("EditProductForm") {
self.showEditProductForm.toggle()
}
}.sheet(isPresented: $showEditProductForm) {
EditProductForm(listIndex: 2, product: "product", quantity: 1, productName: "productName")
.environmentObject(self.data)
}
}
}
struct EditProductForm: View {
#EnvironmentObject var data: Datax
#Environment(\.presentationMode) var presentationModeEdit: Binding<PresentationMode>
var listIndex: Int
var product: String
#State var quantity: Int
#State var productName: String
func editProduct() {
self.data.editProduct(listIndex: self.listIndex, product: self.product, productName: self.productName, quantity: self.quantity)
}
var body: some View {
VStack{
Spacer()
VStack(spacing: 64){
Text("Edit Product")
TextField("Edit the name", text: $productName)
Picker(selection: $quantity, label: Text("Quantity")){
Text("OK").tag(0)
Text("Almost finished").tag(1)
Text("Finished").tag(2)
}.pickerStyle(SegmentedPickerStyle())
Button(action: {
self.editProduct()
self.presentationModeEdit.wrappedValue.dismiss()
}){
Text("Save")
}
}
Spacer()
}.padding(.horizontal)
}
}
Hope this helps track down your issue.