2021-07-31

Opening a Sheet with buttons generated with a ForEach Loop

I have a view where a list of buttons is generated using a ForEach loop, the data is a list of email addresses for a given contact. The number of email addresses are variable. On pressing an email, I want to open a sheet showing a email view and pass the email address selected to sheet. I followed the answer to this question here that showed how to do sheets with a ForEach button loop, but I ran into a strange issue, when I add the .sheet call to my code, it makes the ForEach loop buttons all become button, and clicking on it opens the sheet, but always passes the last email address in the list to the sheet, am I missing something or is this a bug in swiftUI?

Edit: Based on feedback from Yrb (Thanks for the feedback BTW) I have edited it my code so its modular enough that you can run it without any editing

import SwiftUI
import UIKit
import MessageUI
import CoreLocation

struct TestEmailAddress { // --> We also have a Email Address struct as they can have labels and we want to display them
    var label: String
    var email: String
}

extension Int: Identifiable {
    public var id: Int {self}
}

struct MailView: UIViewControllerRepresentable {

@Binding var isShowing: Bool
@Binding var result: Result<MFMailComposeResult, Error>?
var recipients: [String]
var messageBody = ""

class Coordinator: NSObject, MFMailComposeViewControllerDelegate {

    @Binding var isShowing: Bool
    @Binding var result: Result<MFMailComposeResult, Error>?

    init(isShowing: Binding<Bool>,
         result: Binding<Result<MFMailComposeResult, Error>?>) {
        _isShowing = isShowing
        _result = result
    }

    func mailComposeController(_ controller: MFMailComposeViewController,
                               didFinishWith result: MFMailComposeResult,
                               error: Error?) {
        defer {
            isShowing = false
        }
        guard error == nil else {
            self.result = .failure(error!)
            return
        }
        self.result = .success(result)
    }
}

func makeCoordinator() -> Coordinator {
    return Coordinator(isShowing: $isShowing,
                       result: $result)
}

func makeUIViewController(context: UIViewControllerRepresentableContext<MailView>) -> MFMailComposeViewController {
    let vc = MFMailComposeViewController()
    vc.setToRecipients(recipients)
    vc.setMessageBody(messageBody, isHTML: true)
    vc.mailComposeDelegate = context.coordinator
    return vc
}

func updateUIViewController(_ uiViewController: MFMailComposeViewController,
                            context: UIViewControllerRepresentableContext<MailView>) {

}
}

struct RolodexMailViewTest: View {
    @State var testEmails: [TestEmailAddress]
    //Alert Stuff
    @State private var showAlert = false
    //Mail Stuff
    @State private var result: Result<MFMailComposeResult, Error>? = nil
    @State private var isShowingMailView = false
    @State private var selectedEmail: Int? = nil
    var body: some View {
        Section() {
            ForEach(0 ..<  self.testEmails.count) { email in // --> here, we display all the numbers we got
                if !MFMailComposeViewController.canSendMail() {
                    Button(action: { self.showAlert = true
                        self.selectedEmail = email
                    }) {
                        VStack(alignment: .leading, spacing: 5) {
                            Text(self.testEmails[email].label.isEmpty ? "Email" : self.testEmails[email].label)
                                .font(.system(.subheadline))
                                .foregroundColor(.secondary)
                            Text(self.testEmails[email].email)
                        }
                    }
                }
                else {
                    Button(action: { self.isShowingMailView = true
                            self.selectedEmail = email }) {
                        VStack(alignment: .leading, spacing: 5) {
                            Text(self.testEmails[email].label.isEmpty ? "Email" : self.testEmails[email].label)
                                .font(.system(.subheadline))
                                .foregroundColor(.secondary)
                            Text(self.testEmails[email].email)
                        }
                    }
                }
            }
        }
        .sheet(item: self.$selectedEmail) { selectedEmail in
            MailView(isShowing: self.$isShowingMailView, result: self.$result, recipients: [self.testEmails[selectedEmail].email], messageBody: "Hi \("John Q Test"),\r\n")
        }
        .alert(isPresented: $showAlert) { ()
            return Alert(title: Text("Unable to Send Mail"), message: Text("Please Setup Your Mail App"), dismissButton: .default(Text("Ok")))
        }
    }
}

let testEmailArray: [TestEmailAddress] = [TestEmailAddress(label: "Work", email: "johnqtest@work.com"), TestEmailAddress(label: "Home", email: "johnqtest@home.com")]

struct TestUIView_Previews: PreviewProvider {
    static var previews: some View {
        Form() {
            RolodexMailViewTest(testEmails: testEmailArray)
        }
    }
}

When the .sheet is commented in the buttons looks like this: View When .sheet is commented out

However as soon as you comment it out, it becomes one button like this: View When .sheet is included



from Recent Questions - Stack Overflow https://ift.tt/3j9LtjZ
https://ift.tt/eA8V8J

No comments:

Post a Comment