I have a grid of images in my app which will open a scroll view of the images if one is clicked. But no matter what image I click, the scroll view will always open on image 1 with 2,3,4,5,6,7,8,9 below it. I want to make it so if a user clicks on image 6 it will open the scroll view on image 6 with images 1,2,3,4,5 above it and 7,8,9 below it. Here is my code:
LazyVGrid(columns: threeColumnGrid, alignment: .center) {
ForEach(viewModel.posts) { post in
NavigationLink(destination: ScrollPostView(user: user, postids: viewModel.posts, selectedpostid: post.id)){
KFImage(URL(string: post.imageurl))
.resizable()
.aspectRatio(1, contentMode: .fit)
.cornerRadius(15)
}
.overlay(RoundedRectangle(cornerRadius: 14)
.stroke(Color.black, lineWidth: 2))
}
}
// scroll view
@StateObject var viewModel: PostGridViewModel
let postids: [Post]
let selectedpostid: Post.ID
@State private var scrollPosition: Post.ID?
let user: User
init(user: User, postids: [Post], selectedpostid: Post.ID ) {
self._viewModel = StateObject(wrappedValue: PostGridViewModel(user: user))
self.user = user
self.postids = postids
self.selectedpostid = selectedpostid
}
var body: some View {
ZStack{
Color(red: 0.1608, green: 0.4078, blue: 0.3725)
.ignoresSafeArea()
VStack(spacing: 2){
ScrollView{
VStack{
ForEach(viewModel.posts) {post in
posts_cell(post: post, user: user)
}
.padding(.horizontal)
}
.padding(.top, 20)
.padding(.bottom,18)
.scrollTargetLayout()
}
.scrollPosition(id: $scrollPosition, anchor: .center)
.onAppear {
scrollPosition = selectedpostid
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.padding(.top, 30)
.padding(.bottom, 52)
}
}
}
}
//viewModel
class PostGridViewModel: ObservableObject{
private let user: User
@Published var posts = [Post]()
init(user: User) {
self.user = user
Task { try await fetchUserPosts() }
}
@MainActor
func fetchUserPosts() async throws {
self.posts = try await PostService.FetchUserPosts(uid: user.id)
for i in 0 ..< posts.count {
posts[i].user = self.user
}
}
}
//post service
static func FetchUserPosts(uid: String) async throws -> [Post] {
let snapshot = try await postsCollection.whereField("ownerUid", isEqualTo: uid).order(by: "timestamp", descending: true).getDocuments()
return try snapshot.documents.compactMap({ try $0.data(as: Post.self) })
}
struct Post: Identifiable, Hashable, Codable {
let id: String
let ownerUid: String
let caption: String
var likes: Int
var postlikes: Array<String>
var postsaves: Array<String>
let imageurl: String
let timestamp: Timestamp
var user: User?
var didLike: Bool? = false
var didSave: Bool? = false
}
You can scroll to a particular view in a
ScrollViewby using its id. This can either be done using aScrollViewReader(as suggested in a comment) or by using a.scrollPositionmodifier on theScrollView(requires iOS 17).Here is an example of how it can be done using
.scrollPosition.LazyVStackto predict the scroll distance correctly, so aVStackis used instead. But if your images all have the same height then you could try using aLazyVStack..onAppear..centeras the anchor for scrolling then images in the middle of the list should be centered vertically in the display.EDIT Thanks for updating your question with more code. It looks like everything is correctly in place, but I am wondering if the posts are being loaded after the view has appeared? This might be why the scroll position isn't working.
You could try setting
scrollPositionin an.onAppearhandler on the post itself (and remove the.onAppearthat you had on theScrollView). You could also add aprintto help debugging: