Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI ScrollView not getting updated?

Tags:

swiftui

Goal

Get data to display in a scrollView

Expected Result

data shown in scrollview

Actual Result

a blank view

Alternative

use List, but it is not flexible (can't remove separators, can't have multiple columns)

Code

struct Object: Identifiable {
    var id: String
}

struct Test: View {
    @State var array = [Object]()

    var body: some View {
//        return VStack { // uncomment this to see that it works perfectly fine
        return ScrollView(.vertical) {
            ForEach(array) { o in
                Text(o.id)
            }
        }
        .onAppear(perform: {
            self.array = [Object(id: "1"),Object(id: "2"),Object(id: "3"),Object(id: "4"),Object(id: "5")]
        })
    }
}
like image 330
youjin Avatar asked Nov 13 '19 05:11

youjin


3 Answers

A not so hacky way to get around this problem is to enclose the ScrollView in an IF statement that checks if the array is empty

if !self.array.isEmpty{
     ScrollView(.vertical) {
          ForEach(array) { o in
               Text(o.id)
          }
     }
}
like image 127
Paula Ysabelle Medina Avatar answered Nov 13 '22 08:11

Paula Ysabelle Medina


I've found that it works (Xcode 11.2) as expected if state initialised with some value, not empty array. In this case updating works correctly and initial state have no effect.

struct TestScrollViewOnAppear: View {
    @State var array = [Object(id: "1")]

    var body: some View {
        ScrollView(.vertical) {
            ForEach(array) { o in
                Text(o.id)
            }
        }
        .onAppear(perform: {
            self.array = [Object(id: "1"),Object(id: "2"),Object(id: "3"),Object(id: "4"),Object(id: "5")]
        })
    }
}
like image 2
Asperi Avatar answered Nov 13 '22 09:11

Asperi


One hacky workaround I've found is to add an "invisible" Rectangle inside the scrollView, with the width set to a value greater than the width of the data in the scrollView

struct Object: Identifiable {
    var id: String
}

struct ContentView: View {
    @State var array = [Object]()

    var body: some View {
        GeometryReader { geometry in
            ScrollView(.vertical) {
                Rectangle()
                    .frame(width: geometry.size.width, height: 0.01)
                ForEach(array) { o in
                    Text(o.id)
                }
            }
        }
        .onAppear(perform: {
            self.array = [Object(id: "1"),Object(id: "2"),Object(id: "3"),Object(id: "4"),Object(id: "5")]
        })
    }
}
like image 2
youjin Avatar answered Nov 13 '22 08:11

youjin