Search code examples
swiftuiscrollviewgeometryreader

GeomtryReader + ScrollView SwiftUI


I know that GeometryReader inside ScrollView works poorly, so the possible solution is to place ScrollView in to GeometryReader. That's what I've done. I'm trying to integrate a not-so-complicated view but am facing some problems. I have a ScrollView and a geometry reader:

        GeometryReader { proxy in
        let width = proxy.size.width / 3
        let offset = proxy.size.width / 2
        let half = proxy.size.width / 2
        let size = proxy.size
        ScrollView {
            userInfoView
            CustomTabBar()
            Spacer()
            userPhotos(width: width, half: half, size: size)
                .scrollTargetLayout()
        }
        .scrollTargetBehavior(.viewAligned)
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
    }

Inside userPhotos I got another ScrollView:

            ScrollView(.horizontal) {
            LazyHStack(spacing: 0) {
                prepareUserMainPhotos(width: width)
                    .id(Tab.public)
                    .containerRelativeFrame(.horizontal)
                
                prepareCosplayUserPhotos(half: half)
                    .id(Tab.cosplay)
                    .containerRelativeFrame(.horizontal)
                    .task {
                        await viewModel.loadCosplayCharacters()
                    }
            }
            .scrollTargetLayout()
            .offsetX { value in
                let progress = -value / (size.width * CGFloat(Tab.allCases.count - 1))
                tabProgress = max(min(progress, 1), 0)
            }
            
        }
        .scrollPosition(id: $selectedTab)
        .scrollIndicators(.hidden)
        .scrollTargetBehavior(.paging)
        .scrollClipDisabled()

prepareUserMainPhotos and prepareCosplayPhotos got the same code:

                LazyVGrid(columns: columnsCosplay, spacing: 2) {
                ForEach(posts, id: \.id) { post in
                    CharacterAvatarView(
                        imageURL: "\(NetworkManager.heightResolutionDownloadUrl)\(post.avatarID)",
                        fandom: post.fandom,
                        frame: half
                    )
                    .onTapGesture {
                        viewModel.prepareCharacterCosplayFeed(withCharacter: post)
                    }
                }
            }
            .padding(.horizontal)

The problem I have is that the ScrollView gets the maximum height size of the first tab inside userPhotos (prepareUserMainPhotos(width: width)). When I swipe to the second tab, if the second tab is bigger than the first, I encounter some visual issues: From first tab in progress to second tab

Second tab


Solution

  • I'm afraid that I couldn't fully understand your idea, however, trying to set the frame size through GeometryReader is a bit strange. GeometryReader is mainly needed for reading dimensions. Instead, if you want to maintain some proportions and define frames, then consider creating view frames using UIScreen. For example:

    UIScreen.main.bounds.size.width
    UIScreen.main.bounds.size.height
    

    For each device, these will be their own values, which can be easily multiplied, divided, etc., based on your taste or the sizes that you calculated through GeometryReader, and so on. Of course, pictures inside such views should preferably have resizing modifiers.

    PS However, you can think about the design. Consider the size of all views. Picture sizes. Overlay the image representations with the necessary modifiers so that pictures of different sizes take on the same appearance. Then, you will not need to invent anything with GeometryReader and UIScreen.main.bounds, your main view will itself take the form you need, with minimal settings.