How to insert dynamic list items into SwiftUI list? - swiftui

This is my body:
var body: some View {
NavigationView {
List {
MonthElementView(month: month)
ServiceElementView(service: month.services.first!)
ServiceElementView(service: month.services.first!)
}
}
}
And it works.
But I need to display here ServiceElementView for every Service of the Month, and I don't know how to do it with SwiftUI. The following solution doesn't work:

You're using the wrong forEach.
It's…
ForEach(month.sortedServices) { service in
}

Related

SwiftUI header like datepicker above List

I want to create a List view with SwiftUI with a sticky component on the top that is outside of the List.
An example of what i want to achieve is on the bottom.
I tried placing the component inside the List as first item but it will be put inside a list item. and just hides a big part of the component
var body: some View {
List {
WeekHeader()
.environmentObject(weekStore)
DayView(
date: weekStore.currentDate,
showAddingWorkout: $showAddingWorkout,
isEditing: isEditing
)
}
.listStyle(.insetGrouped)
}
I tried placing a List inside a VStack with the component before the List which just splits the view in 2 with a List really small on the bottom.
var body: some View {
VStack {
WeekHeader()
.environmentObject(weekStore)
List {
DayView(
date: weekStore.currentDate,
showAddingWorkout: $showAddingWorkout,
isEditing: isEditing
)
}
.listStyle(.insetGrouped)
}
}
And my last attempt with 'looks' like what i want to achieve but its placed inside the navigation bar and the height of the bar doesn't grow with it so the bottom row of the component couldn't be interacted with.
var body: some View {
NavigationView {
List {
DayView(
date: weekStore.currentDate,
showAddingWorkout: $showAddingWorkout,
isEditing: isEditing
)
}
.listStyle(.insetGrouped)
.toolbar {
ToolbarItemGroup(placement: .principal) {
WeekHeader()
.environmentObject(weekStore)
}
}
}
}
This is the component i want to add above the list
And i want my list to look like a default list.
Please let me know if i need to elaborate my question or provide more code.

Text view is not show in LazyVGrid

I experience kind of weird behavior of placing Text view in nested ForEach in one LazyVGrid view. My code look like this
LazyVGrid(columns: dataColumns) {
ForEach(months, id: \.self) { month in
Text("\(dateFormatter.string(from: month))")
}
ForEach(categories) { category in
ForEach(months, id: \.self) { month in
Text("aaa")
}
}
}
and "aaa" string is not shown but if I add just another Text view like this
ForEach(months, id: \.self) { month in
Text("aaa")
Text("bbb")
}
then both Text views with strings are repeatedly shown as expected. Any idea? Thanks.
The issue here is to do with view identity. LazyVGrid uses each views identity to know when they need to be loaded in.
Structural identity is also used by SwiftUI to identify views based on the structure of their view hierarchy. This is why when you added another view to the ForEach instance the views show, as they now are uniquely structurally identifiable from the other ForEach content.
Here, you have two separate ForEach instances inside the LazyVGrid which use the same identifiers. Two of the forEach instances are using months with the id \.self to identify them. These are conflicting and should be unique to avoid these sorts of issues.
To fix this you should preferably use a unique array of elements for each ForEach instance, though could create a copy of the months array to use in the second ForEach instead of referring to the same instances. Here's an example:
import SwiftUI
import MapKit
struct MyType: Identifiable, Hashable {
var id = UUID()
var name = "Tester"
}
struct ContentView: View {
let columns = [GridItem(.flexible()), GridItem(.flexible())]
var months: [MyType] = []
var monthsCopy: [MyType] = []
private var monthValues: [MyType] {
[MyType()]
}
init() {
months = monthValues
monthsCopy = monthValues
}
var body: some View {
LazyVGrid(columns: columns) {
ForEach(months) { _ in
Text("Test1")
Text("Test2")
}
ForEach(monthsCopy) { _ in
Text("Test3")
Text("Test4")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
If you replace ForEach(monthsCopy) with ForEach(months) you will see the issue you were faced with.

SwiftUI: Using List and ForEach to display model data of different types with an array of Strings

I want to display a dynamic list of items that come from model data. The problem I have is how to properly use ForEach to display a var that is an array of Strings. There are no compiling issues with the code, but I cannot get the List to display.
Here is the struct for the model data:
struct OrderInfo: Codable, Identifiable, Hashable {
var id: Int
var orderNumber: String
var activeOrderStatus: Bool
var pickupSection: Int
var pickupStand: String
var pickupItems: [String]
}
Here is the code for my View:
struct CompletedOrderPickUp: View {
var completedOrder: OrderInfo
var body: some View {
ScrollView {
VStack {
HStack {
Text("Section")
Spacer()
Text("\(completedOrder.pickupSection)")
}
.pickUpDetailsTitleModifier()
HStack {
Text("Stand Name")
Spacer()
Text(completedOrder.pickupStand)
}
.pickUpDetailsTitleModifier()
HStack {
Text("Order Items")
Spacer()
}
.pickUpDetailsTitleModifier()
List {
ForEach(completedOrder.pickupItems, id: \.self) { items in
Text("\(items)")
Text(items)
}
}
}
}
}
}
And here is the screenshot of what it produces:
Screenshot with no items under "Order Items"
There's no issue with accessing the two other variables (pickupSection and pickupStand), which leads me to believe the problem lies with how to properly access the array of Strings within the data for pickupItems.
My friend Google has lots to say about ForEach and accessing data but not a lot about the combination of the two. Please let me know if I may clarify my question. Any help is much appreciated!

Why the List does not appear?

Why the presetsList does not appear? No errors were thrown though.
import SwiftUI
struct AddMessagePreset: View {
let presetsList = [
Preset(name: "preset text 1"),
Preset(name: "preset text 2"),
Preset(name: "preset text 3")
]
var body: some View {
List(presetsList) { singlePresetModel in
SinglePresetChild (presetModel: singlePresetModel)
}
}
}
import SwiftUI
struct Preset: Identifiable {
let id = UUID()
let name: String
}
struct SinglePresetChild: View {
var presetModel: Preset
var body: some View {
Text("Preset Name \(presetModel.name)")
}
}
UPDATE: To show a List inside another ScrollView (or List), you have to set a height on the inner list view:
struct Preview: View {
var body: some View {
ScrollView {
AddMessagePreset().frame(height: 200)
// more views ...
}
}
}
But let me advise against doing so. Having nested scroll areas can be very confusing for the user.
As discussed in the comments, your component code is fine. However, the way you integrate it into your app causes a problem. Apparently, nesting a List inside a ScrollView does not work properly (also see this thread).
List is already scrollable vertically, so you won't need the additional ScrollView:
struct Preview: View {
var body: some View {
VStack {
AddMessagePreset()
}
}
}
P.S.: If you only want to show AddMessagePreset and won't add another sibling view, you can remove the wrapping VStack; or even show AddMessagePreset as the main view, without any wrapper.

SwiftUI NavigationLink

I'm working on a SwiftUI practice app and I ran into an issue with the NavigationView/NavigationLink. I am currently using the Metropolitan Museum of Art API and I wanted to make a list of departments that segues to another list of objects in that department, then that list segues to the object's information. Currently the NavigationView/NavigationLink setup I have is creating multiple NavigationViews and is resulting in multiple back buttons/navigation bars. Is there a way to have the new NavigationView replace the old one or have them work in line with one another? The only way I know how to create a segue in SwiftUI is through a NavigationView/NavigationLink but creating it twice seems to be the wrong way to go about things. I have a screen shot of the current state of my app.
App Image
This is my code at the moment.
struct ContentView: View {
#ObservedObject var model = DepartmentListViewModel()
var body: some View {
NavigationView {
List {
ForEach(model.departments, id: \.self) { department in
NavigationLink(destination: DetailView(viewModel: DetailListViewModel(selectedDepartment: department))) {
Text(department.displayName)
}
}
}
.navigationBarTitle("Departments")
}
}
}
struct DetailView: View {
#ObservedObject var viewModel: DetailListViewModel
init(viewModel: DetailListViewModel) {
self.viewModel = viewModel
}
var body: some View {
NavigationView {
List {
ForEach(viewModel.objects, id: \.self) { object in
NavigationLink(destination: ObjectView(viewModel: ObjectListViewModel(selectedObject: object))) {
Text(String(object))
}
}
}
.navigationBarTitle("ObjectIDs")
}
}
}
You don't need NavigationView in your DetailView anymore, the first one handle it