AndrewWicks
AndrewWicks

Reputation: 93

@State var only changes after second button click SwiftUI

Hello I apologize for my ignorance if this is an overdone or overly simple question. I am using SwiftUI and want my page to have multiple buttons that all send emails with different subject and message bodies. Everything currently works although only after the second time I click a button. The first time the subject is my initialized var ("start") regardless of button.

Another small thing, when I click the same button repeatedly it never updates until I click a different one.

Thank you very much, attached is code, I left out my mailcompose swift file as it all works as expected.

import SwiftUI
import MessageUI
import Foundation


struct ContentView: View {
    
    @State var result: Result<MFMailComposeResult, Error>? = nil
    @State private var showSheet = false
    @State private var subject: String = "start"
    @State private var msgBody: String = ""
    let numberString = "201-228-0752"
    
    var body: some View {
        ZStack{
            Color.white.ignoresSafeArea()
            VStack{
                Image("Icon-1024")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .clipShape(Circle())
                    .shadow(radius: 10)
                    .overlay(Circle().stroke(Color.black, lineWidth: 3))
                    .frame(width: 50, height: 50, alignment: .center)
                    .padding(.top, 10)
                Text("Contact Us")
                    .foregroundColor(.black)
                    .fontWeight(.heavy)
                    .font(.title)
                    .padding()
                Text("Be part of something bigger.")
                    .foregroundColor(.black)
                    .fontWeight(.medium)
                    .font(.system(size: 20))
                    .padding()
                    .padding(.bottom, 5)
                    .multilineTextAlignment(.center)
                Spacer()
                VStack(spacing: 0){
                    Button(action: {
                        self.subject = "General"
                        self.msgBody = "Hello, I need help."
                        self.suggestFeature()
                    }){
                        HStack {
                            Spacer()
                            Text("General help")
                             .font(.system(size: 25))
                             .fontWeight(.medium)
                             .foregroundColor(.white)
                                .padding(.top, 15)
                                .padding(.bottom, 15)
                            Spacer()
                        }
                     }
                    .background(Color(red: 1.00, green: 0.49, blue: 0.51))
                    Button(action: {
                        self.subject = "Technical"
                        self.msgBody = "Hello, I am having technical difficulties"
                        self.suggestFeature()
                    }){
                        HStack {
                            Spacer()
                            Text("Technical issues")
                             .font(.system(size: 25))
                             .fontWeight(.medium)
                             .foregroundColor(.white)
                                .padding(.top, 15)
                                .padding(.bottom, 15)
                            Spacer()
                        }
                     }
                    .background(Color(red: 0.81, green: 0.39, blue: 0.40))
                    Button(action: {
                        self.subject = "Gender"
                        self.msgBody = "Hello, I would like to make a gender or sexuality request."
                        self.suggestFeature()
                    }){
                        HStack {
                            Spacer()
                            Text("Gender & Sexuality")
                             .font(.system(size: 25))
                             .fontWeight(.medium)
                             .foregroundColor(.white)
                                .padding(.top, 15)
                                .padding(.bottom, 15)
                            Spacer()
                        }
                     }
                    .background(Color(red: 0.62, green: 0.29, blue: 0.30))
                    Button(action: {
                        self.subject = "Delete"
                        self.msgBody = "Hello, I would like to request a delete of my info."
                        self.suggestFeature()
                    }){
                        HStack {
                            Spacer()
                            Text("Delete info")
                             .font(.system(size: 25))
                             .fontWeight(.medium)
                             .foregroundColor(.white)
                                .padding(.top, 15)
                                .padding(.bottom, 15)
                            Spacer()
                        }
                     }
                    .background(Color(red: 0.45, green: 0.19, blue: 0.20))
                    Button(action: {
                        let telephone = "tel://"
                        let formattedString = telephone + numberString
                        guard let url = URL(string: formattedString) else { return }
                        UIApplication.shared.open(url)
                    }){
                        HStack {
                            Spacer()
                            (Text("Call ") + Text(Image(systemName: "phone.fill")))
                             .font(.system(size: 25))
                             .fontWeight(.medium)
                             .foregroundColor(.white)
                                .padding(.top, 15)
                                .padding(.bottom, 15)
                            Spacer()
                        }
                    }
                    .background(Color(.black))
                    Spacer()
                    Text("© Copyright Sixish, Inc.")
                        .padding(.bottom, 30)
                        .foregroundColor(.gray)

                }.sheet(isPresented: $showSheet) {
                    MailView(result: self.$result, newSubject: self.subject, newMsgBody: self.msgBody)
                }
            }
        }
    }
    
    func suggestFeature() {
        print("You've got mail")
        if MFMailComposeViewController.canSendMail() {
            self.showSheet = true
        } else {
            print("Error sending mail")
            // Alert : Unable to send the mail
        }
    }
    
}

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

Upvotes: 2

Views: 763

Answers (1)

jnpdx
jnpdx

Reputation: 52645

This is a classic issue that gets run into a lot with sheet() on SwiftUI. The gist is that your sheet's content view can get rendered before it actually appears and before the @State variables' changes propagate to it.

The fix is to use sheet(item:):


struct Message : Identifiable, Hashable {
    var id = UUID()
    var subject : String
    var body : String
}

struct ContentView: View {
    
    //@State var result: Result<MFMailComposeResult, Error>? = nil
    @State private var showSheet = false
    @State private var message : Message?
    let numberString = "201-228-0752"
    
    var body: some View {
        ZStack{
            Color.white.ignoresSafeArea()
            VStack{
                Image("Icon-1024")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .clipShape(Circle())
                    .shadow(radius: 10)
                    .overlay(Circle().stroke(Color.black, lineWidth: 3))
                    .frame(width: 50, height: 50, alignment: .center)
                    .padding(.top, 10)
                Text("Contact Us")
                    .foregroundColor(.black)
                    .fontWeight(.heavy)
                    .font(.title)
                    .padding()
                Text("Be part of something bigger.")
                    .foregroundColor(.black)
                    .fontWeight(.medium)
                    .font(.system(size: 20))
                    .padding()
                    .padding(.bottom, 5)
                    .multilineTextAlignment(.center)
                Spacer()
                VStack(spacing: 0){
                    Button(action: {
                        self.message = Message(subject: "General", body: "Hello, I need help.")
                        self.suggestFeature()
                    }){
                        HStack {
                            Spacer()
                            Text("General help")
                             .font(.system(size: 25))
                             .fontWeight(.medium)
                             .foregroundColor(.white)
                                .padding(.top, 15)
                                .padding(.bottom, 15)
                            Spacer()
                        }
                     }
                    .background(Color(red: 1.00, green: 0.49, blue: 0.51))
                    Button(action: {
                        self.message = Message(subject: "Technical", body: "Hello, I am having technical difficulties")
                        self.suggestFeature()
                    }){
                        HStack {
                            Spacer()
                            Text("Technical issues")
                             .font(.system(size: 25))
                             .fontWeight(.medium)
                             .foregroundColor(.white)
                                .padding(.top, 15)
                                .padding(.bottom, 15)
                            Spacer()
                        }
                     }
                    .background(Color(red: 0.81, green: 0.39, blue: 0.40))
                    Button(action: {
                        self.message = Message(subject: "Gender", body: "Hello, I would like to make a gender or sexuality request.")
                        self.suggestFeature()
                    }){
                        HStack {
                            Spacer()
                            Text("Gender & Sexuality")
                             .font(.system(size: 25))
                             .fontWeight(.medium)
                             .foregroundColor(.white)
                                .padding(.top, 15)
                                .padding(.bottom, 15)
                            Spacer()
                        }
                     }
                    .background(Color(red: 0.62, green: 0.29, blue: 0.30))
                    Button(action: {
                        self.message = Message(subject: "Delete", body: "Hello, I would like to request a delete of my info.")
                        self.suggestFeature()
                    }){
                        HStack {
                            Spacer()
                            Text("Delete info")
                             .font(.system(size: 25))
                             .fontWeight(.medium)
                             .foregroundColor(.white)
                                .padding(.top, 15)
                                .padding(.bottom, 15)
                            Spacer()
                        }
                     }
                    .background(Color(red: 0.45, green: 0.19, blue: 0.20))
                    Button(action: {
                        let telephone = "tel://"
                        let formattedString = telephone + numberString
                        guard let url = URL(string: formattedString) else { return }
                        UIApplication.shared.open(url)
                    }){
                        HStack {
                            Spacer()
                            (Text("Call ") + Text(Image(systemName: "phone.fill")))
                             .font(.system(size: 25))
                             .fontWeight(.medium)
                             .foregroundColor(.white)
                                .padding(.top, 15)
                                .padding(.bottom, 15)
                            Spacer()
                        }
                    }
                    .background(Color(.black))
                    Spacer()
                    Text("© Copyright Sixish, Inc.")
                        .padding(.bottom, 30)
                        .foregroundColor(.gray)

                }.sheet(item: $message) { item in
                    MailView(message: item)
                    //your code would probably need to be more like MailView(result: self.$result, newSubject: item.subject, newMsgBody: item.body)
                }
            }
        }
    }
    
    func suggestFeature() {
        print("You've got mail")
        if MFMailComposeViewController.canSendMail() {
            self.showSheet = true
        } else {
            print("Error sending mail")
            // Alert : Unable to send the mail
        }
    }
    
}

struct MailView : View {
    var message : Message
    
    var body: some View {
        Text(message.subject)
        Text(message.body)
    }
}

Note that now, your sheet is displayed if message has an item it it. Message is now a struct.

I simplified things a little bit since I didn't have your MailView container (and I temporarily commented out your result property), but the concept presented here should get you started doing what you need to do.

Upvotes: 3

Related Questions