@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
How to Detect When Avplayer Video Ends Playing
Nsfetchedresultscontroller V.S. Uilocalizedindexedcollation
How to Stop Multiple Times Method Calling of Didupdatelocations() in iOS
Cannot Put a Google Maps Gmsmapview in a Subview of Main Main View
Swift - Which Types to Use? Nsstring or String
Remove Grey Background on Link Clicked in iOS Safari/Chrome/Firefox
Fix CSS Hover on Iphone/Ipad/Ipod
Issue with Code Autocompletion/Syntax Highlighting in Xcode 4.X
Writing Handler for Uialertaction
How to Create a Colored 1X1 Uiimage on the iPhone Dynamically