Приложение Swift / SwitUI аварийно завершает работу при изменении ориентации устройства после определения позиции в ScrollView

Мое приложение продолжает вылетать, когда я меняю ориентацию устройства, если раньше я установил scrollPosition. Мой ContentView() выглядит примерно так - где я читаю размер дисплея.

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

Передача display в MainScrollView(), где - внутри ScrollViewReader оператор if-else проверяет, должен ли он загружать PortraitMainView() или `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 () и PortaitMainView () выглядят так:

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")
        }
    }
}

Все работает нормально, я могу перевернуть ориентацию, и она меняется с LandscapeMainView() на PortraitMainView() и наоборот. Кроме того, установка scrollPosition прекрасно работает в ландшафтном и портретном режимах. Только после того, как я установил scrollPosition и затем изменил ориентацию, мое устройство зависает, давая мне Thread 1: EXC_BAD_ACCESS (code=1, address=0x30) в качестве вывода. Экран по-прежнему показывает портретный режим с наклоном к Альбомной, оставляя половину экрана пустой. Есть ли лучший способ управлять ориентацией экрана и прокруткой в ​​SwiftUI?


person JacquesNorris    schedule 28.12.2020    source источник
comment
Предоставленного кода недостаточно для воспроизведения сбоя, не могли бы вы привести минимальный пример?   -  person Asperi    schedule 28.12.2020
comment
Обновил код - при попытке сбоя я обнаружил, что он не вылетает на симуляторе. Пробовал мое настоящее приложение на симуляторе: работает, как ожидалось. Однако этот код на моем iPhone XS с iOS 14 дает сбой.   -  person JacquesNorris    schedule 28.12.2020
comment
Попробуйте передать bool вместо GeometryProxy в свой MainScrollView. У меня была аналогичная проблема, так что это всего лишь предположение   -  person Dima Rostopira    schedule 28.12.2020
comment
@DimaRostopira, спасибо, это действительно сработало!   -  person JacquesNorris    schedule 28.12.2020


Ответы (1)


GeometryProxy вызывает проблемы, когда вы пытаетесь передать его куда-нибудь (в том числе и у меня). SwiftUI - это закрытый исходный код, поэтому я не могу объяснить, какая проблема с волшебными указателями здесь происходит. Но подтверждено, что этот код работает согласно комментариям.

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")
                } )
            }
        }
    }
}

Бонус: такие же сбои случаются с ScrollViewProxy, будьте осторожны с ними.

Мое необразованное предположение, почему это происходит: GeometryProxy содержит ссылку на родительский виджет, который освобождается при перестроении, но дочерний виджет не перестраивается, потому что аргумент не изменился, поэтому он попадает в освобожденный указатель.

person Dima Rostopira    schedule 29.12.2020