iosswiftswiftuiorientationscroll-position

Swift / SwitUI App crashes, when changing device Orientation after defining a position in ScrollView


My App keeps on crashing when I change the device orientation if I did set a scrollPosition before. My ContentView() looks smth like this - where I'm reading out the display-size.

GeometryReader { display in
    VStack {
        HeaderView()
        MainScrollView(display: display)
        FooterView()
    }
}

Passing display on to the MainScrollView(), where - inside a ScrollViewReader an if-else-statement checks if it should load the PortraitMainView() or the `LandscapeMainView()

struct MainScrollView: View {
    var display: GeometryProxy
    var body: some View {
    
        ScrollViewReader { scrollPosition in
            ZStack {
                ScrollView {
                    if display.size.width > display.size.height {
                        LandscapeMainView()
                    } else {
                        PortraitMainView()
                    }
                }
                Button(action: {
                    withAnimation {
                        scrollPosition.scrollTo("C", anchor: .top)
                    }
                }, label: {
                    Text("Scroll to Position C")
                } )
            }
        }
    }
}

LandscapeMainView() and PortaitMainView() look like this:

LandscapeMainView: View {
    var body: some View {
        VStack {
            Text("Item A")
                .id("A")
            Text("Item B")
                .id("B")
            Text("Item C")
                .id("C")
            Text("Item D")
                .id("D")
            Text("Item E")
                .id("E")
        }
    }
}

It all works fine, I can flip the orientation and it changes from LandscapeMainView() to PortraitMainView() and vice versa. Also, setting a scrollPosition works like a charm in Landscape and Portrait Mode. Only after I set a scrollPosition and then change the orientation my device freezes, giving me an Thread 1: EXC_BAD_ACCESS (code=1, address=0x30) as output. The Screen still shows the Portrait mode, tilted on the Landscape, leaving half of the screen blank. Is there a better way to handle screen orientation and scrollviews in SwiftUI?


Solution

  • GeometryProxy causes issues when you try to pass it anywhere (for me as well). SwiftUI is closed source, so I can't explain what magical pointers issue is happening here. But this code is confirmed to be working as per comments.

    GeometryReader { display in
        VStack {
            HeaderView()
            MainScrollView(display: display.size.width > display.size.height)
            FooterView()
        }
    }
    
    struct MainScrollView: View {
        var display: Boolean
        var body: some View {
        
            ScrollViewReader { scrollPosition in
                ZStack {
                    ScrollView {
                        if display {
                            LandscapeMainView()
                        } else {
                            PortraitMainView()
                        }
                    }
                    Button(action: {
                        withAnimation {
                            scrollPosition.scrollTo("C", anchor: .top)
                        }
                    }, label: {
                        Text("Scroll to Position C")
                    } )
                }
            }
        }
    }
    

    Bonus: same crashes happen with ScrollViewProxy, be careful with those.

    My uneducated guess, why this happening: GeometryProxy is holding reference to parent widget, which is deallocated while being rebuilt, but child widget isn't rebuilt because argument didn't changed so it hits deallocated pointer.