0

I have a grid of items. Each item can expand height. I want to autoscroll when the item is expanded so it doesn't overflow the screen.
I was successful with the following code but I had to revert to a hack. The idea was to detect when the item is overflowing using a Geometry reader on the item's background. Works wonders.
The issue is that when the view is expanded , the geo reader will update after the condition to check if autoscroll should execute is ran by the dispatcher. Hence my ugly hack.

Wonder what is the proper way ?

import SwiftUI

struct BlocksGridView: View {
    
    private var gridItemLayout = [GridItem(.adaptive(minimum: 300, maximum: .infinity), spacing: 20)]
    
    
    var body: some View {
        ZStack{
            ScrollView {
                ScrollViewReader { value in
                    LazyVGrid(columns: gridItemLayout, spacing: 20) {
                        ForEach((0..<20), id: \.self) {
                            BlockView(cardID: $0,scrollReader: value).id($0)
                        }
                    }
                }
                .padding(20)
            }
        }
    }
}

struct BlockView : View {
    
    var cardID : Int
    var scrollReader : ScrollViewProxy
    
    @State private var isOverflowingScreen = false
    @State private var expand = false
    
    var body: some View {
        ZStack{
            Rectangle()
                .foregroundColor(isOverflowingScreen ? Color.blue : Color.green)
                .frame(height: expand ? 300 : 135)
                .clipShape(Rectangle()).cornerRadius(14)
                .overlay(Text(cardID.description))
                .background(GeometryReader { geo -> Color in
                    DispatchQueue.main.async {
                        if geo.frame(in: .global).maxY > UIScreen.main.bounds.maxY {
                            isOverflowingScreen = true
                        } else {
                            isOverflowingScreen = false
                        }
                    }
                    return Color.clear
                })
                .onTapGesture {
                    expand.toggle()
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // <-- Hack :(
                        if isOverflowingScreen {
                            withAnimation{
                                scrollReader.scrollTo(cardID)
                            }
                        }
                    }
                }
        }
    }
}


struct BlocksGridView_Previews: PreviewProvider {
    static var previews: some View {
        BlocksGridView()
    }
}

Blue items are overflowing ...

enter image description here

GrandSteph
  • 2,053
  • 1
  • 16
  • 23
  • have your tried `onAppear` / `onDisappear`? – user1046037 Nov 13 '22 at 21:44
  • @user1046037 just tried that. Doesn't work, I created a top and bottom view in the BlockView but they appear at the same time because I believe the LazyVStack will render the entire BlockView when the top part appears. I would have to create top and bottom view out of the BlockView, it would be a nightmare – GrandSteph Nov 14 '22 at 07:50

0 Answers0