Lalli
Lalli

Reputation: 466

Text not appearing but onAppear triggers

I am trying to use forEach in HStack and VStack. I am using Text in them and Text is not appearing while running but onAppear print values. Please have a look on my code. Why Text is not appearing? How can I make this work?

@State var sd = ["1","2","3","4","5","6","7"]

VStack {
    ForEach(0...sd.count/3) { _ in
        HStack {
            ForEach(0...2) { _ in
                if(self.sd.isEmpty) {
                } else {
                    Text("Test")
                        .onAppear() {
                            if(!self.sd.isEmpty) {
                                print("i appeared")
                                self.sd.removeFirst()
                            }
                    }
                }
            }
            Spacer()
        }
    }
}

What I am trying to achieve here?

I am trying to create a HStacks with maximum 3 Texts in it. I am using array here only to rendered Text 7 times.

Expected result with array with 7 elements--->

Want to create a VStack of 3 HStacks, In first 2 HStacks I want to render Text 3 times and in last HStack I want only one Text. (Like I have 7 array elements that's why 3 texts in first two hstacks and one in last hstack). If array has 10 elements, then 3 Hstacks of 3 Texts and last Stack with 1 Text. I am unable to render Text because my array is @state var and it refresh view.body every time I remove firstElement from it.

Is there any way to achieve this behaviour I am trying to achieve by using SwiftUI only. I don't want to use UICollection view.

Upvotes: 0

Views: 642

Answers (1)

marcprux
marcprux

Reputation: 10355

You generally don't want to change a @State variable as a side-effect of the evaluation of body; in this case, your self.sd.removeFirst() is causing the body to be marked as needing to be re-evaluated, so SwiftUI just keeps calling it until your sd array is empty, which is why you don't see anything being rendered.

To get the effect you want, this is one way you can do it:

import SwiftUI

struct ContentView : View {
    @State private var sd = ["1","2","3","4","5","6","7"]
    private let columns = 3

    var body: some View {
        VStack {
            ForEach(Array(stride(from: 0, to: sd.count, by: columns))) { row in
                HStack {
                    ForEach(0..<self.columns) { col in
                        if row + col < self.sd.count {
                            Text("\(self.sd[row + col])")
                        }
                    }
                }
            }
        }
    }
}

P.S. Most attempts to modify state within the body evaluation seem to result in the runtime warning Modifying state during view update, this will cause undefined behavior. – it seems that your array mutation somehow side-stepped this check, but hopefully that sort of thing will be called out at runtime in the future.

Upvotes: 0

Related Questions