Reputation: 1506
Trying to create a lightweight CRUD UI for CoreData, so need a solution that can lazy load both the rows and columns. Currently using a LazyVGrid like:
GeometryReader { geometry in
ScrollView([.horizontal, .vertical]) { scrollProxy in
LazyVGrid(columns: viewModel.gridColumns(), alignment: .leading, spacing: 0, pinnedViews: [.sectionHeaders]) {
Section(header: headerView()) {
ForEach(0..<viewModel.rowCount, id:\.self) { rowIndex in
viewModel.rowViewForRowIndex(rowIndex:rowIndex).fixedSize(horizontal: true, vertical: true)
}
}
}
.frame(maxWidth: .infinity,
minHeight: geometry.size.height,
alignment: .top)
}
}
Nested inside the LazyVGrid that rowViewForRowIndex() returns a view with LazyHStack:
LazyHStack{
ForEach(content.indices, id:\.self) { colIndex in
let field = content[colIndex]
let newScrollID = RowColScrollID(row:rowIndex, col:colIndex)
Text(field.value)
.lineLimit(1)
.frame(width: CGFloat(field.width), height: CGFloat(field.height), alignment: .center)
.scrollId(newScrollID)
.onAppear(perform:{
if let unwrappedVisAndInteractionDel = visiblityAndInteractionDelegate {
unwrappedVisAndInteractionDel.cellAppeared(newScrollID)
}
})
.onDisappear(perform:{
if let unwrappedVisAndInteractionDel = visiblityAndInteractionDelegate {
unwrappedVisAndInteractionDel.cellDisappeared(newScrollID)
}
})
.allowsHitTesting(false)
}
}
If one scrolls horizontally or vertically only, everything looks fine. If I change the LazyHStack to be a regular HStack, everything is fine. However when scrolling diagonally using a nested LazyHStack it appears the lazy loaded rows loose their horizontal alignment vs their brethren. Perhaps this is best explained with a video:
If a solution here isn't apparent to anyone, using an HStack where the LazyHStack is would be appropriate (I know this works nicely for all scrolling directions). However, I really need to know which members of the HStack are actually on screen and as we all know .onAppear is closer to ".onLoad" than it is actually related to appearance to the user. If I could determine which members of that HStack are ACTUALLY on screen a solution to the above wouldn't be necessary. In UIKit land I'd just get the views origin and do some simple math on the displays CGRect and Bob's your uncle, but I cannot think of a solution in SwiftUI.
THANKS!
Upvotes: 2
Views: 1070
Reputation: 1506
I just wanted to post a workaround I'm going with. The optimal (smallest memory footprint, cleanest logic/code) solution is to use a LazyHStack nested inside the LazyVStack/Grid. As I cannot determine how to fix the diagonal scroll issue in the gif attached to the question, I'm using a regular HStack inside LazyVStack/Grid. My application is a CRUD editor for CoreData objects, so having the entire row (HStack) loaded at once isn't terrible as the rows are lazy loaded (however approaching 100 columns it does get choppy, but few "tables" have 100 columns).
I still need to know which "cells" are presently on screen (not just loaded via the scrollview (.onAppear called) but offscreen). To accomplish this I'm using two GeometryReaders to test each loaded cell to determine if it's on screen. This requires iterating each cell, which is inefficient (another reason a LazyHStack would be best if the original issue can be solved) - thankfully for my application this iteration doesn't happen terribly often. I would paste my code, but the solution I used is very accurately described here (where I learned of it): Get first visible index from LazyHStack in SwiftUI
I would be so pleased if someone provides an additional answer that solves the issue in the gif and allows LazyHStack to be used nested inside LazyVStack (I'll promptly delete this workaround), but figured I'd post my current workaround for the sake of others.
Upvotes: 0