How to modify state inside a func in SwiftUI?

I want to modify my state inside a function but xcode gave this error:

Modifying state during view update, this will cause undefined behavior.

 struct ContentView: View {
    
    @State private var offsetEnum: OffsetEnum = .left
    
    var body: some View {
        VStack {
            
            MyView(offsetEnum: offsetEnum)
            
            Button("update") {
                if offsetEnum == .left {
                    offsetEnum = .right
                }
                else {
                    offsetEnum = .left
                }
            }
            
        }
        .padding()
    }
}


struct MyView: View {
    
    let offsetEnum: OffsetEnum
    @State private var angle: Angle = .zero
    
    var body: some View {
        
        Text("Hello, world!")
            .rotationEffect(angle)
            .offset(offsetCalculator())
        
    }
    
    func offsetCalculator() -> CGSize {
        
        switch offsetEnum {
        case .left:
            angle -= Angle(degrees: 45.0)
            return CGSize(width: -200.0, height: .zero)
        case .right:
            angle += Angle(degrees: 45.0)
            return CGSize(width: 200.0, height: .zero)
        }
        
    }
    
}

enum OffsetEnum {
    case left, right
}

As you can see the offset can be left or right but even if we are in same posation I want be able update the angle via pressing the button. How to solve this issue?

The problem is that the function which calculates the offset is also updating the state variable angle. This is not allowed in a modifier such as .offset. But it would be fine if called from an action callback.

So to fix, you could add another state variable to store the offset and update both the angle and offset whenever the enum value changes. This is done using an .onChange callback. Like this:

struct MyView: View {

    let offsetEnum: OffsetEnum
    @State private var angle: Angle = .zero
    @State private var offset = CGSize.zero

    var body: some View {

        Text("Hello, world!")
            .rotationEffect(angle)
            .offset(offset)
            .onChange(of: offsetEnum) { newVal in
                offset = offsetCalculator(direction: newVal)
            }
    }

    func offsetCalculator(direction: OffsetEnum) -> CGSize {
        switch offsetEnum {
        case .left:
            angle -= Angle(degrees: 45.0)
            return CGSize(width: -200.0, height: .zero)
        case .right:
            angle += Angle(degrees: 45.0)
            return CGSize(width: 200.0, height: .zero)
        }
    }
}

Leave a Comment