0

I'm trying to allow the FileManager to check if the matching Image Selected is saved. When saved, it needs to update the views on both the MainScreenView and BadgeScreenView. I am getting a error 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." and "Result of 'BadgeScreenView' initializer is unused" after the image is selected and checked to see if it is saved at the right locations.

var ContentViewBadge = UIImage(systemName: "questionmark")!
var fileURL: URL?
    
func saveImage() {
    
    do {
        let furl = try FileManager.default
            .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("Compliance")
            .appendingPathExtension("png")
        fileURL = furl
        try ContentViewBadge.pngData()?.write(to: furl)
        print("Image \(ContentViewBadge) is saved to \(furl)")
    } catch {
        print("could not create imageFile")
    }
    let finding = fileURL
    let fileExists = FileManager().fileExists(atPath: finding!.path)
    if fileExists {
     
    @State var IsTrue: Bool = true
    BadgeScreenView(TrueBadge: $IsTrue)
        
        
        
       //Change State variable "TrueBadge" here
        print("Found something!")
    }
}
import SwiftUI
import Foundation

var IsDone = false

struct BadgeScreenView: View {
    
    @Binding var TrueBadge: Bool //Need help switching this Binding to true
    @State private var ComplianceBadgeIsPicking =  UIImage(named: "BlankComplianceBadge")!
    @State private var isShwoingPhotoPicker = false
    @State private var ShowInstruction = false
    @State private var AlertToReplaceBade = false
    
    
    var body: some View {
        //The beginning
        if TrueBadge {
            
            Color("MainBadgeScreen")
                .edgesIgnoringSafeArea(.all)
                .overlay(
                    VStack{
                        Text("Clearance Status")
                            .font(.title)
                            .fontWeight(.semibold)
                            .offset(y: -15)
                            .foregroundColor(.white)
                        Text("Vaccine Compliant")
                            .foregroundColor(.white)
                            .bold()
                            .font(.system(size: 30))
                        Image(uiImage: ContentViewBadge)
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .scaledToFit()
                        
                        Button(action: {
                            AlertToReplaceBade.toggle()
                        }) {
                            Image(systemName: "trash" )
                            Text("Remove")
                        }
                        .foregroundColor(.white)
                        .padding()
                        .background(Color.red)
                        .cornerRadius(15)
                        .offset(y: 13)
                        
                        
                        
                    }.alert(isPresented: $AlertToReplaceBade, content: {
                        Alert(title: Text("Are you sure you would like to remove your current badge?"),
                              message: Text("Remeber that this badge is and will be permanently removed"),
                              primaryButton: .default(Text("Yes"), action: {
                            // Somehow need to remove the image and activate the UIImagePickerController
                            
                            isShwoingPhotoPicker.toggle()
                            
                        }), secondaryButton: .cancel(Text("No, I do not")))
                        
                    }).sheet(isPresented: $isShwoingPhotoPicker, content: {
                        PhotoPicker(Badge: $ComplianceBadgeIsPicking)
                    })
                    
                )}
        else {
            Color("ExpiredBadgeScreen")
                .edgesIgnoringSafeArea(.all)
                .overlay(
                    VStack{
                        
                        Image(systemName: "person.crop.circle.badge.questionmark.fill")
                            .font(.system(size:150))
                            .offset(y: -10)
                            .foregroundColor(.black)
                        
                        Text("Compliance Badge")
                            .font(.largeTitle)
                            .fontWeight(.bold)
                            .foregroundColor(.black)
                            .offset(y: -2)
                        
                        
                        Text("You do not have a current vaccine compliant badge. Please upload one that shows you are vaccine compliant or within 'green' status")
                            .font(.system(size: 15))
                            .foregroundColor(.black)
                            .fontWeight(.bold)
                            .multilineTextAlignment(.center)
                            .frame(width: 270, height: 140, alignment: .center)
                            .offset(y: -26)
                        
                        
                        Button(action: {
                            ShowInstruction.toggle()
                        }) {
                            Image(systemName: "questionmark.circle")
                            Text("How to upload")
                                .bold()
                                .font(.system(size:20))
                        }
                        .offset(y: -40)
                        
                        Button(action: {
                            isShwoingPhotoPicker.toggle()
                        }) {
                            Image(systemName: "square.and.arrow.up")
                            Text("Upload Badge")
                                .bold()
                                .font(.system(size:20))
                        }
                        .offset(y: -10)
                        
                    }.sheet(isPresented: $ShowInstruction, content: {
                        Instruction()
                    })
                        .sheet(isPresented: $isShwoingPhotoPicker, content: {
                            PhotoPicker(Badge: $ComplianceBadgeIsPicking)
                        })
                        .accentColor(.black)
                    
                )
        }
        //The End
    }
    
    
}
jnpdx
  • 45,847
  • 6
  • 64
  • 94
ARealChar
  • 11
  • 3
  • As it says, you can't create a `@State`/`@Binding` outside of a `View` struct. They are part of a view's lifecycle – George Jan 05 '22 at 23:27
  • @George then how would I go about changing the views if I cannot create them? – ARealChar Jan 05 '22 at 23:30
  • 1
    You have `BadgeScreenView(TrueBadge: $IsTrue)` in the middle of a new-View body function -- it wouldn't be displayed in the view. Your views should always be in a view hierarchy. – jnpdx Jan 05 '22 at 23:34
  • @jnpdx I'm still new to this. What is the best way to fix this? – ARealChar Jan 05 '22 at 23:36
  • I can't answer that very specifically because it's not clear to me what you're trying to do and I don't have enough code for a [mre]. You need to include `BadgeScreenView` in a view hierarchy -- that means the `body` property of a `View`. Then, if you want to change `isTrue`, you can modify that state or binding from other places/functions. – jnpdx Jan 05 '22 at 23:40
  • Might be a good time to check out the Apple SwiftUI tutorials and/or the Hacking With Swift 100 Days of SwiftUI – jnpdx Jan 05 '22 at 23:40
  • You may also find this useful: https://stackoverflow.com/questions/56517610/conditionally-use-view-in-swiftui – jnpdx Jan 05 '22 at 23:44
  • @jnpdx I just included the BadgeScreenView. All I need is for the saveImge() function to set the Binding variable to true. But since it is not possible, I am unsure how to do that – ARealChar Jan 05 '22 at 23:46
  • If you include your `saveImage()` function *within* your `BadgeScreenView`, then you would have direct access to change `TrueBadge`. Is there a reason it's separate? Or, you could pass the binding as a parameter -- right now, you're not passing any parameters to `saveImage` – jnpdx Jan 05 '22 at 23:52
  • @jnpdx I'm not sure, but now I'm getting a segmentation fault 11 – ARealChar Jan 06 '22 at 00:01
  • That doesn't give me information about what went wrong, unfortunately – jnpdx Jan 06 '22 at 00:05
  • @jnpdx I don't know. I changed saveImage() to BadgeScreenView and the error just appeared – ARealChar Jan 06 '22 at 00:06
  • It doesn't give you a line number or anything? – jnpdx Jan 06 '22 at 00:07
  • @jnpdx I'm not seeing any sort of line number. "error: Segmentation fault: 11 (in target 'QAProject1' from project 'QAProject1')" – ARealChar Jan 06 '22 at 00:12

1 Answers1

0

There's not enough for a minimal reproducible example in your code, so I had to make some guesses here, but this is the gist of what I'd expect things to look like. Note that saveImage is inside the View and thus has access to change the state.

It's not clear to me, though, where you call saveImage (you don't do it anywhere in your included code), which could effect things further.


let contentViewBadge = UIImage(systemName: "questionmark")!

struct BadgeScreenView: View {
    @Binding var trueBadge: Bool
    @State private var complianceBadgeIsPicking =  UIImage(named: "BlankComplianceBadge")!
    @State private var isShowingPhotoPicker = false
    @State private var showInstruction = false
    @State private var alertToReplaceBade = false
    
    func saveImage() {
        do {
            let furl = try FileManager.default
                .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
                .appendingPathComponent("Compliance")
                .appendingPathExtension("png")
            try contentViewBadge.pngData()?.write(to: furl)
            print("Image \(contentViewBadge) is saved to \(furl)")
            
            let fileExists = FileManager().fileExists(atPath: furl.path)
            if fileExists {
                trueBadge = true
                print("Found something!")
            }
        } catch {
            print("could not create imageFile")
        }
    }
    
    var body: some View {
        //The beginning
        if trueBadge {
            
            Color("MainBadgeScreen")
                .edgesIgnoringSafeArea(.all)
                .overlay(
                    VStack{
                        Text("Clearance Status")
                            .font(.title)
                            .fontWeight(.semibold)
                            .offset(y: -15)
                            .foregroundColor(.white)
                        Text("Vaccine Compliant")
                            .foregroundColor(.white)
                            .bold()
                            .font(.system(size: 30))
                        Image(uiImage: contentViewBadge)
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .scaledToFit()
                        
                        Button(action: {
                            alertToReplaceBade.toggle()
                        }) {
                            Image(systemName: "trash" )
                            Text("Remove")
                        }
                        .foregroundColor(.white)
                        .padding()
                        .background(Color.red)
                        .cornerRadius(15)
                        .offset(y: 13)
                        
                        
                        
                    }.alert(isPresented: $alertToReplaceBade, content: {
                        Alert(title: Text("Are you sure you would like to remove your current badge?"),
                              message: Text("Remeber that this badge is and will be permanently removed"),
                              primaryButton: .default(Text("Yes"), action: {
                            // Somehow need to remove the image and activate the UIImagePickerController
                            
                            isShowingPhotoPicker.toggle()
                            
                        }), secondaryButton: .cancel(Text("No, I do not")))
                        
                    }).sheet(isPresented: $isShowingPhotoPicker, content: {
                        PhotoPicker(Badge: $complianceBadgeIsPicking)
                    })
                    
                )}
        else {
            Color("ExpiredBadgeScreen")
                .edgesIgnoringSafeArea(.all)
                .overlay(
                    VStack{
                        
                        Image(systemName: "person.crop.circle.badge.questionmark.fill")
                            .font(.system(size:150))
                            .offset(y: -10)
                            .foregroundColor(.black)
                        
                        Text("Compliance Badge")
                            .font(.largeTitle)
                            .fontWeight(.bold)
                            .foregroundColor(.black)
                            .offset(y: -2)
                        
                        
                        Text("You do not have a current vaccine compliant badge. Please upload one that shows you are vaccine compliant or within 'green' status")
                            .font(.system(size: 15))
                            .foregroundColor(.black)
                            .fontWeight(.bold)
                            .multilineTextAlignment(.center)
                            .frame(width: 270, height: 140, alignment: .center)
                            .offset(y: -26)
                        
                        
                        Button(action: {
                            showInstruction.toggle()
                        }) {
                            Image(systemName: "questionmark.circle")
                            Text("How to upload")
                                .bold()
                                .font(.system(size:20))
                        }
                        .offset(y: -40)
                        
                        Button(action: {
                            isShwoingPhotoPicker.toggle()
                        }) {
                            Image(systemName: "square.and.arrow.up")
                            Text("Upload Badge")
                                .bold()
                                .font(.system(size:20))
                        }
                        .offset(y: -10)
                        
                    }.sheet(isPresented: $showInstruction, content: {
                        Instruction()
                    })
                        .sheet(isPresented: $isShowingPhotoPicker, content: {
                            PhotoPicker(Badge: $complianceBadgeIsPicking)
                        })
                        .accentColor(.black)
                )
        }
        //The End
    }
}

An alternative (or addition) to putting savingImage inside the view would be to explicitly pass a binding to it. The function would look like this:

func saveImage(binding: Binding<Bool>) {
    do {
        let furl = try FileManager.default
            .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("Compliance")
            .appendingPathExtension("png")
        try contentViewBadge.pngData()?.write(to: furl)
        print("Image \(contentViewBadge) is saved to \(furl)")
        
        let fileExists = FileManager().fileExists(atPath: furl.path)
        if fileExists {
            binding.wrappedValue = true
            print("Found something!")
        }
    } catch {
        print("could not create imageFile")
    }
}

And you would call it like this:

saveImage(binding: $trueBadge)

Note: I changed your variable names to fit the Swift conventions of using lowercase letters to start variable/property names

jnpdx
  • 45,847
  • 6
  • 64
  • 94