Home > Software engineering >  SwiftUI Swift - VStack inside LazyVStack item ignores the children height
SwiftUI Swift - VStack inside LazyVStack item ignores the children height

Time:12-10

I got this inside LazyVstack:

                            ForEach(memes, id: \.memeid){
                                meme in

                                if(!user.hiddenUsersString.contains(String(meme.userid)   ",")){
                                    
                                    GeometryReader{ proxy in
                                        
                                        MemeView(memeid: meme.memeid,
                                            picUrl: Functions.getMediaUrl(media: meme.pic, serverScaling: serverScaling,
                                            whichMedia: "memes"),
                                            profilImgUrl: finalImgUrl(theUrl: Functions.getProfileImg(userid: meme.userid, serverScaling: serverScaling, imgTag: user.userid == meme.userid ? user.profilePicTag : nil), memeOwner: meme.userid),
                                            memeWidth: memeWidth,
                                            memeHeight: Functions.getMemeHeight(memeWidth: memeWidth, fileName: meme.pic, memeSize: meme.size),
                                            position: proxy.frame(in: .global).origin.y,
                                            meme: meme,
                                            memes: self.$memes)
                                
                                    }
                                }
                            }

In the body of MemeView I got:

        VStack{

            HStack{
                AsyncImage(url: URL(string: profilImgUrl)) { phase in
                    switch phase {
                    case .empty:
                        ProgressView()
                            .scaleEffect(x: 1.5, y: 1.5, anchor: .center)
                            .aspectRatio(contentMode: .fit)
                            .frame(maxWidth: 34)
                    case .success(let image):
                        image.resizable()
                             .aspectRatio(contentMode: .fit)
                             .frame(maxWidth: 34)
                             .cornerRadius(50)
                    case .failure:
                        Image(systemName: "person.circle.fill")
                            .resizable()
                            .aspectRatio(contentMode: .fit).frame(height: 34)
                            .cornerRadius(50)
                            .foregroundColor(Color.gray)
                    @unknown default:
                        Image(systemName: "person.circle.fill")
                            .resizable()
                            .aspectRatio(contentMode: .fit).frame(height: 34)
                            .cornerRadius(50)
                            .foregroundColor(Color.gray)
                    }
                }
                
                Text(meme.nickname)
            }
            .frame(height: 30)
            
            ZStack{
                if(meme.pic.contains(".mp4")){
                    VideoPlayer(player: player)
                        .frame(width: memeWidth,
                               height: memeHeight,
                               alignment: .center)
                }else{
                    AsyncImage(url: URL(string: picUrl)) { phase in
                        switch phase {
                        case .empty:
                            ProgressView()
                                .scaleEffect(x: 1.5, y: 1.5, anchor: .center)
                                .aspectRatio(contentMode: .fit)
                                .frame(width: 34)
                        case .success(let image):
                            image.resizable()
                                 .aspectRatio(contentMode: .fit)
                                 .frame(width: memeWidth, height: memeHeight)
                                 .cornerRadius(50)
                        case .failure:
                            Image(systemName: "person.circle.fill")
                                .resizable()
                                .aspectRatio(contentMode: .fit).frame(height: 34)
                                .cornerRadius(50)
                                .foregroundColor(Color.gray)
                        @unknown default:
                            Image(systemName: "person.circle.fill")
                                .resizable()
                                .aspectRatio(contentMode: .fit).frame(height: 34)
                                .cornerRadius(50)
                                .foregroundColor(Color.gray)
                        }
                    }
                }
            }
            .frame(width: memeWidth, height: 500)
        }
        .task {

            if(meme.pic.contains(".mp4")){
                player = AVPlayer(url: URL(string: picUrl)!)
            }
        }

Everything is squeezed together! VStack completely ignores the height of the children views.

Why is that and how to fix this?

I'm new to iOS / SwiftUI and must say that I absolutely loathe it. I'm shocked that apple think it's ok to release such a bugged and limited pile garbage into the world!

CodePudding user response:

First thing you should do is remove the VStack located in your MemeView, when you consider your overall structure you have something like this, which is not what I believe you're expecting.

LazyVStack {
    VStack {
        //Your MemeViewContent
    }
}

As you can see, you have a LazyVStack and then another VStack inside of it, when your outer LazyVStack manages your expected view just fine.

The next step is to refactor out the GeometryReader OR, moving it higher in the view stack. Currently you have it ForEach individual view, so it's going to cause the views contained inside of it, to be resized unexpectedly. Effectively, you've got a bunch of really small Frames inside of your ForEach loop, which then you're attempting to use as if it was taking up the entire screen, effectively chopping it into very small parts, eg squished views. A better solution would be to make use of the .onAppear{} or .onDisappear{} modifiers, or get the view's properties and calculate it that way. Ultimately, I would always recommend avoiding GemometryReader except in special circumstances. If you're hard-stuck needing to have to use the GeometryReader have a look here for a usage of it, where you'd bring it down to a more child view position and make use of the proxy. https://stackoverflow.com/a/73038067/12417531

onAppear/onDisappear Resources: How to check if a view is displayed on the screen? (Swift 5 and SwiftUI)

Geometry Reader Resources: https://swiftwithmajid.com/2020/11/04/how-to-use-geometryreader-without-breaking-swiftui-layout/

  • Related