@Binding and Foreach in Swiftui

@Binding and ForEach in SwiftUI

You can use something like the code below. Note that you will get a deprecated warning, but to address that, check this other answer: https://stackoverflow.com/a/57333200/7786555

import SwiftUI

struct ContentView: View {
@State private var boolArr = [false, false, true, true, false]

var body: some View {
List {
ForEach(boolArr.indices) { idx in
Toggle(isOn: self.$boolArr[idx]) {
Text("boolVar = \(self.boolArr[idx] ? "ON":"OFF")")
}
}
}
}
}

Binding in SwiftUI ForEach from Parent to Child

You missed the $ before subtask.

If you don't write $subtask, then subtask is now a Binding<Subtask>. This would mean you can still do subtask.wrappedValue to use subtask normally and subtask to use the binding, but that's not as neat. With the $, $subtask is a Binding<Subtask> and subtask is a Subtask.

Change:

ForEach($model.subtasks) { subtask in
ChildView(subtask: $subtask)
}

To:

ForEach($model.subtasks) { $subtask in
ChildView(subtask: $subtask)
}

Or alternatively (and more confusingly, I don't recommend):

ForEach($model.subtasks) { subtask in
ChildView(subtask: subtask)
}

Binding in a ForEach in SwiftUI

Assuming your elements is state of items array, it can be

List {
ForEach(elements.indices, id: \.self) { i in
CheckBoxView(checked: $elements[i].checked)
}
}

Using ForEach with a an array of Bindings (SwiftUI)

Trying a different approach. The FormField maintains it's own internal state and publishes (via completion) when its text is committed:

struct FormField : View {
@State private var output: String = ""
let viewModel: FormFieldViewModel
var didUpdateText: (String) -> ()

var body: some View {
VStack {
TextField($output, placeholder: Text(viewModel.placeholder), onCommit: {
self.didUpdateText(self.output)
})

Line(color: Color.lightGray)
}.padding()
}
}
ForEach(viewModel.viewModels) { vm in
FormField(viewModel: vm) { (output) in
vm.output = output
}
}

SwiftUI - using ForEach with a Binding array that does not conform to identifiable / hashable


Option 1:

Give your IncidentResponse an ID and then tell it to not try to decode the value:

struct IncidentResponse: Codable, Identifiable {
var id = UUID()
let incident: IncidentDetails?

enum CodingKeys: String, CodingKey {
case incident
}
}

Option 2:

Make incident and id non-optional and then use this to get the id:

ForEach(existingIncidents, id: \.incident.id) { incident in

I'll also note that IncidentResponse seems to be a somewhat meaningless wrapper at this point. When you do your decoding and store the values to existingIncidents (which you haven't shown), you could probably store them just as [IncidentDetails] instead. In that case, you just have to make the id property non-optional on IncidentDetails and declare it as Identifiable. For example:

struct IncidentDetails: Codable, Identifiable {
let id: String
let reason: IncidentReasonResponse?
let message: String?
let startedAt: String?
let endedAt: String?
}

//....

let existingIncidentsWrappers : [IncidentResponse] = //...
let existingIncidents : [IncidentDetails] = existingIncidentsWrappers.compactMap { $0.incident }

SwiftUI: How to filter Binding value in ForEach?

I think the issue is not the filtering per se, but the .lowercased() that the ForEach is rejecting. Rather than try to force that, there is a simpler way. Use a computed variable that does the filtering, and then roll your own Binding to send to the view like this:

struct ContentView: View {
@State var testArray = [
Test(number: 1, text: "1"),
Test(number: 2, text: "2"),
Test(number: 3, text: "3"),
Test(number: 4, text: "4"),
Test(number: 5, text: "5"),
Test(number: 6, text: "6")
]

@State private var searchText = ""
@State private var placeholder = "Search..."

var filteredTest: [Test] {
// If you want to return no items when there is no matching filter use this:
testArray.filter { ($0.text.lowercased().contains(searchText.lowercased()))}

// If you want the whole array unless the filter matches use this:
let returnTestArray = testArray.filter { ($0.text.lowercased().contains(searchText.lowercased()))}
guard !returnTestArray.isEmpty else { return testArray }
return returnTestArray
}

var body: some View {
NavigationView{
VStack{
SearchBar(text: $searchText, placeholder: $placeholder)
List {
ForEach(filteredTest) { test in
TestView10(test: Binding<Test> (
get: { test },
set: { newValue in
if let index = testArray.firstIndex(where: { $0.id == newValue.id } ) {
testArray[index] = newValue
}
}
))
}
}
}
}
}
}

I also renamed your array of data testArray to better reflect what it was, and in the ForEach, data now becomes test.

SwiftUI Bind to core data object inside foreach

I love CoreData with SwiftUI. It works just so simple. Just create a new edit view with ObservedObject of Book.

struct EditView : View {
@ObservedObject var book: Book

init(book: Book) {
self.book = book
}

var body : some View {
TextField("Name", text: $book.bookName)
}
}

And that's it. Then you can send $book.bookName as Binding to the String.

However, make sure! you have declared bookName as non-optional value in CoreData. TextField requires a Binding<String>, NOT a Binding<String?>

Use that EditView inside your ForEach and you are ready to go:

ForEach(books) { book in
EditView(book: book)

Binding two ForEach loop to update each item cell

I'd suggest storing all of your data in an ObservableObject that is owned by the parent view and then can get passed into subviews (either explicitly or via an EnvironmentObject):

class DataSource : ObservableObject {
@Published var data : [Data] = DataList.dot
@Published var favoritedItems: Set<UUID> = []

func favoriteBinding(forID id: UUID) -> Binding<Bool> {
.init {
self.favoritedItems.contains(id)
} set: { newValue in
if newValue {
self.favoritedItems.insert(id)
} else {
self.favoritedItems.remove(id)
}
}
}
}

For example:

struct ParentView : View {
@StateObject var dataSource = DataSource()

var body: some View {
VStack {
Search(dataSource: dataSource)
}
}
}

Note that the data source stores a list of IDs that have been favorited. It uses a custom binding that can pass the boolean value down to a detail view:


struct Search: View {

@ObservedObject var dataSource : DataSource

var body: some View {
NavigationView {
ScrollView (.vertical, showsIndicators: false) {
LazyVStack(alignment: .leading, spacing: 10) {

ForEach (dataSource.data, id: \.id) { data in

NavigationLink(
destination: DetailView(data: data,
selectedFavoriteSong: dataSource.favoriteBinding(forID: data.id)),
label: {
Text(data.name1)
})

}

}.padding(.horizontal)
}
}
}
}

struct DetailView: View {
var data : Data
@Binding var selectedFavoriteSong : Bool

var body: some View {
HStack {
Button(action: {
self.selectedFavoriteSong.toggle()
}, label: {
if self.selectedFavoriteSong {
Image(systemName: "suit.heart.fill")
.foregroundColor(.red)
.padding(.horizontal)

} else {
Image(systemName: "suit.heart")
.padding(.horizontal)
}
})

Spacer()
Text("\(data.name1)")
Spacer()

}
.padding(.top)

VStack {

Text(data.name2 ?? "")
.font(.title2.smallCaps())
.fontWeight(.bold)
.foregroundColor(.primary)
}
.padding(.horizontal)
.multilineTextAlignment(.center)

Spacer()

}
}

How to use array of ForEach in SwiftUI

As @jnpdx said, you can use the map(...) method of Array to accomplish your goal:

let places: [Place] = viewModel.posts.map { post in
Place(latitude: post.location.latitude, longitude: post.location.longitude)
}


Related Topics



Leave a reply



Submit