0

I ran into an issue when using the @State property.

My ContentView.swift looks like this:

import SwiftUI

struct ContentView: View {
    @State var showText: Bool = true
    
    var Mod: Modifier
    init() {
        Mod = Modifier(showText: $showText) // Throws error -> 'self' used before all stored properties are initialized ('self.Mod' not initialized)
    }
    
    var body: some View {
        VStack {
            if showText == true {
                Text("Hello, World!")
            }
            Mod
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

And my Modifier.swift from which the Modifier view is called has following code:

import SwiftUI

struct Modifier: View {
    @Binding var showText: Bool
    
    var body: some View {
        VStack {
            Button("Hide Text") {
                self.showText.toggle()
            }
        }
    }
}

I created this simplified code from my actual project that my problem is easier to understand.

Problem

The problem is that the code in the init function results into an error and I don't know how to resolve it.

What I tried and what I would need

Because this is just a simplified version of my actual code there are some requirements I need to my code:

  • Mod can't be a computed variable
  • I somehow need the Modifier view as a variable called Mod in my ContentView
  • When I remove the @State property and the @Binding property and the $ the code works and results with 0 errors. But I need to use the @State property (which unfortunately results into errors with my code)
  • Also the button to hide and show the text should work

I would be very thankful if anyone could give me a hint. I really appreciate your help!

pawello2222
  • 46,897
  • 22
  • 145
  • 209
sp4c38
  • 455
  • 3
  • 14

3 Answers3

1
Mod = Modifier(showText: _showText.projectedValue)

You can make it let instead of var if you'd like.

  • Thanks for your answer. When I try this it compiles and runs. But when I click on the button the text doesn't disappear anymore. Do you maybe have some way that I can use the @State and that the text disappears? – sp4c38 Aug 03 '20 at 18:38
  • 1
    No; your example won't actually work. I figured it was just a simplification. Use Xcode 12 for better feedback about it. https://stackoverflow.com/questions/62633728/accessing-viewmodel-field-in-swiftui-using-xcode-12-accessing-states-value-ou –  Aug 03 '20 at 18:47
  • Thanks for looking into my question! I will try it with Xcode 12 for better feedback. Will post the answer here if I can find one. Thanks again for helping. – sp4c38 Aug 03 '20 at 18:59
1

I did actually find a way to do this. I'm not sure whether it'll be suitable but here are the details.

The problem was that SwiftUI didn't seem to allow setting the Binding outside of body. So this solution returns a new instance of Modifier

struct Modifier: View {
    @Binding var showText: Bool
    
    var body: some View {
        VStack {
            Button("Hide Text") {
                self.showText.toggle()
            }
        }
    }
    
    // this function returns a new instance with the binding
    func bind(to binding: Binding<Bool>) -> Self {
        return Modifier(showText: binding)
    }
}

And the code for ContentView, where we can call this function from within body:

struct ContentView: View {
    @State var showText: Bool = true
    var Mod: Modifier
    
    init() {
        Mod = Modifier(showText: .constant(true)) // .constant() gives a placeholder Binding
    }
    
    var body: some View {
        return VStack {
            if showText == true {
                Text("Hello, World!")
            }
            Mod.bind(to: $showText)
        }
    }
}

Tested and the text can be hidden/shown. Hope this can help.

themathsrobot
  • 581
  • 6
  • 20
-1

Use views inside body context

struct ContentView: View {
    @State var showText: Bool = true
    
    var body: some View {
        VStack {
            if showText == true {
                Text("Hello, World!")
            }
            Modifier(showText: $showText)
        }
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Yes that would work. But I need the Mod variable. In my simplified version it would work but in my real project I need the same View multiple times and want to store it in a variable for that use. – sp4c38 Aug 03 '20 at 18:36
  • 2
    @sp4c38 storing views as variables is almost always the wrong approach (under most normal cases), so you might be on the wrong track with your design – New Dev Aug 03 '20 at 18:39
  • Hmm ok. Thanks. In my actual project I would need to parse the View which (I wanted to store in a variable) into another View to call a function when a button is tapped from the first View. So I probably should use a different approach for that? – sp4c38 Aug 03 '20 at 18:44
  • 1
    @sp4c38 it's not entirely clear to me what you mean by "need to parse the View...". Views should be thought of as declarative UI structures, and you should be reasoning in terms of data (state variables, view models) and manipulate them. Read up on `@Binding`, `@State`, and `@ObservedObject`/`ObservableObject`, and read up on various ways that "views" message up and down the view tree hierarchy. Here's a good [explanation](https://www.vadimbulavin.com/passing-data-between-swiftui-views/) – New Dev Aug 03 '20 at 18:54
  • Thanks for looking into my question! I will read the post and will see if I can resolve the issue (without storing the views in variables). Will post the answer here if I can find one. Thanks again for helping. – sp4c38 Aug 03 '20 at 18:58
  • 1
    I've tried several different approaches and many of them tell me that "Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update." I believe it's just not possible to assign a Binding in that way without doing it from ```body``` – themathsrobot Aug 04 '20 at 09:29