How to bring an Image to foreground in LazyVStack/ScrollView SwiftUI?

373 views Asked by At

I have a LazyVStack of Post Views with images in those views (similar to Instagram). When I'm zooming in on one of the images, I would like for that image to overlap over all other content. However, currently what's happening is it overlaps over posts above it, but goes under posts below it. I'm pretty sure the reasoning behind this is the compiler assigns Zindexes to each element in a Stack and by default the later ones are given a higher priority Zindex value.

I've tried playing around with the Zindexes but to no avail. I've tried having a @Binding variable in the PostView that updates the @State variable in the Timeline view, but that didn't work either. Firstly, here's a simplified version of my Timeline View

struct TimelineView: View {
    @State var isZooming: Bool = False
    @State var posts : [Post]
    var body: some View {
        LazyVStack {
            ForEach(posts) { post in
                PostView(post, $isZooming)
                    .zIndex(isZooming ? 1 : 0)
            }
        }
    }
}

And here's a simplified version of my Post View

struct PostView: View {
    var post : Post
    @Binding var isZooming: Bool
    var body: some View {
        VStack {
            post.caption
            post.image
                .gesture(MagnificationGesture().onChanged { amount in 
                    self.isZooming = true
                }   
        }
    }
}
1

There are 1 answers

0
ChrisR On BEST ANSWER

Your zIndex approach is right, but if you apply it inside the ForEach it will be set for all posts, so they end up all being zIndex = 1.

If you move zIndex into the subview it works:

struct TimelineView: View {
    
    @State var posts : [Post] = Post.dummyData
    
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(posts) { post in
                    PostView(post: post)
                }
                .padding()
            }
        }
    }
}

struct PostView: View {
    var post : Post
    
    @State var isZooming: Bool = false
    @State var zoom: CGFloat = 1
    
    var body: some View {
        VStack {
            Text(post.caption)
            Image(post.image)
                .resizable().scaledToFill()
                .frame(height: 250
                )
                .clipped()
                .scaleEffect(zoom)
            
                .gesture(MagnificationGesture()
                    .onChanged { value in
                        zoom = value
                        isZooming = true
                    }
                    .onEnded { _ in
                        zoom = 1
                        isZooming = false
                        
                    }
                )
        }
        .zIndex(isZooming ? 1 : 0) // here
    }
}