I am working with a ScrollView and a LazyVStack that can have many rows, sometimes 500 or more. My rows are expandable on tap. Performance of expand/collapse is unacceptable when the number of rows exceeds 100. The more rows, the more performance degrades.
This is the gist of my code:
struct PlaceView: View {
		/* ... */
		var place: Place
		@Binding expandedPlaceId: Int
		var content: some View {
				VStack(alignment: .leading) {
						Text(place.name)
						
						if self.expandedPlaceId.wrappedValue == place.id {
								VStack(alignment: .leading) {
										Text(place.city)
								}
						}
				}
		}
}
struct PlacesListView: View {
		/*...*/
		@State expandedPlaceId = -1
		var body: some View {
			ScrollView {
				LazyVStack {
					ForEach(places, id: \.id) { place in
						PlaceView(place: place, expandedPlaceId: self.$expandedPlaceId)
								.onTapGesture { self.selectDeselect(place) }
								.animation(.linear(duration: 0.3))
					}
			 }
		 }
	}
}
I created an init function in PlaceView with the following:
print("calling int for place \(place.id)")
What I have noticed is when expanding, every single PlaceView calls init. For 20 or 30 PlaceView rows this is not a problem, but with several hundred views this creates a choppiness in animation and delays the expansion of the detail section.
Is there any workaround for this problem? It does not matter if I use LazyVStack or VStack (which itself is unexpected).
Here is an example project that can easily be modified to highlight the problem: https://github.com/V8tr/ExpandableListSwiftUI
In the above project, simply add the following to PlaceView.swift
init(place: Place, isExpanded: Bool) {
self.place = place
self.isExpanded = isExpanded
print("PlaceView init \(self.place.id)")
}
Post
Replies
Boosts
Views
Activity
I am programmatically directing traffic in my app using NavigationLink and isActive.
My primary hierarchy goes like this ContentView -> View 2 -> View 3. And then ContentView -> View 4 (but this part doesn't work).
What I would like to do is pop back to the root ContentView from anywhere and then navigate to a fourth view. Popping back to root is easy, navigating to that fourth view programmatically seems impossible due to a bug in NavigationLink where the fourth view will become active and visible only to immediately go back to ContentView.
Here is my code:
import SwiftUI
@main
struct BugApp: App {
@StateObject var session = Session()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(session)
}
}
}
class Session: ObservableObject {
@Published var showingContentViewTwo: Bool = false
@Published var showContentViewFour: Bool = false
}
struct ContentView: View {
@EnvironmentObject var session: Session
@State private var showingContentViewFour = false
var body: some View {
NavigationView {
VStack {
Spacer()
NavigationLink(destination: ContentViewTwo(), isActive: $session.showingContentViewTwo) {
EmptyView()
}.isDetailLink(false)
NavigationLink(destination: ContentViewFour(), isActive: self.$showingContentViewFour) {
EmptyView()
}.isDetailLink(false)
Text("I am ContentView")
.padding()
HStack {
HStack(alignment: .center) {
Spacer()
Text("Go To View 2")
.foregroundColor(Color.primary)
.bold()
Spacer()
}.padding()
.background(Color.blue)
}
.padding()
.onTapGesture(count: 1) {
session.showingContentViewTwo = true
}
.onReceive(session.$showContentViewFour) { value in
if value {
session.showContentViewFour = false
self.showingContentViewFour = true
}
}
Spacer()
}
}
}
}
struct ContentViewTwo: View {
@EnvironmentObject var session: Session
var body: some View {
VStack {
Spacer()
Text("I am ContentViewTwo")
.padding()
NavigationLink(destination: ContentViewThree()) {
HStack {
HStack(alignment: .center) {
Spacer()
Text("Go To View 3")
.foregroundColor(Color.primary)
.bold()
Spacer()
}.padding()
.background(Color.blue)
}
}
.padding()
Spacer()
}
}
}
struct ContentViewThree: View {
@EnvironmentObject var session: Session
@State private var showingViewFour: Bool = false
var body: some View {
VStack {
NavigationLink(destination: ContentViewFour(), isActive: self.$showingViewFour) {
EmptyView()
}.isDetailLink(false).hidden().allowsHitTesting(false)
Spacer()
Text("I am ContentViewThree")
.padding()
Button(action: {
session.showingContentViewTwo = false
session.showContentViewFour = true
}) {
HStack {
HStack(alignment: .center) {
Spacer()
Text("Go All The Way Back")
.foregroundColor(Color.primary)
.bold()
Spacer()
}.padding()
.background(Color.blue)
}
}
.padding()
Spacer()
}
}
}
struct ContentViewFour: View {
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var session: Session
var body: some View {
VStack {
Spacer()
Text("I am ContentViewFour")
.padding()
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack {
HStack(alignment: .center) {
Spacer()
Text("Go Back")
.foregroundColor(Color.primary)
.bold()
Spacer()
}.padding()
.background(Color.green)
}
}
.padding()
Spacer()
}
}
}
Help?
When using ScrollViewProxy scrollTo, if a "row" the VStack/LazyVStack is expandable and is expanding, it will randomly result in a crash:
[error] precondition failure: setting value during update: 4927752 AttributeGraph precondition failure: setting value during update: 4927752. Is there a safe way to use scrollTo in such that this does not occur?
Use of contextMenu randomly causes the following crash/error:
CoreSimulator 732.13 - Device: iPhone 11 Pro Max (7932F9B5-D9B7-4365-B442-78A1D7718034) - Runtime: iOS 14.0 (18A5357e) - DeviceType: iPhone 11 Pro Max
AttributeGraph precondition failure: setting value during update: 4216072.
Using recursive views, even with each subview housing a single container VStack and .id(), scrollTo does not function. There are no duplicate ids, scrollTo simply does nothing.
As of Xcode 12 beta 3, WKWebView content is no longer visible in the simulator.