在Swift的普通UIViewController中,我使用此代码发送邮件.

let mailComposeViewController = configuredMailComposeViewController()

mailComposeViewController.navigationItem.leftBarButtonItem?.style = .plain
mailComposeViewController.navigationItem.rightBarButtonItem?.style = .plain
mailComposeViewController.navigationBar.tintColor = UIColor.white

if MFMailComposeViewController.canSendMail() {
    self.present(mailComposeViewController, animated: true, completion: nil)
} else {
    self.showSendMailErrorAlert()
}

如何在SwiftUI中实现同样的效果?

我需要用UIViewControllerRepresentable吗?

推荐答案

正如您所提到的,您需要通过UIViewControllerRepresentable将组件连接到SwiftUI.

下面是一个简单的实现:

struct MailView: UIViewControllerRepresentable {

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

    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.mailComposeDelegate = context.coordinator
        return vc
    }

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

    }
}

Usage:

struct ContentView: View {

    @State var result: Result<MFMailComposeResult, Error>? = nil
    @State var isShowingMailView = false

    var body: some View {

        VStack {
            if MFMailComposeViewController.canSendMail() {
                Button("Show mail view") {
                    self.isShowingMailView.toggle()
                }
            } else {
                Text("Can't send emails from this device")
            }
            if result != nil {
                Text("Result: \(String(describing: result))")
                    .lineLimit(nil)
            }
        }
        .sheet(isPresented: $isShowingMailView) {
            MailView(isShowing: self.$isShowingMailView, result: self.$result)
        }

    }

}

(在运行iOS 13的iPhone 7 Plus上测试——效果非常好)

Updated for Xcode 11.4

Swift相关问答推荐

启用完整并发判断以及如何解决警告

如何取消正在视图修改器中运行的任务

如何将图像插入到矩形中

如何让ScrollView缩小到合适的大小,并在没有黑客攻击的情况下占用最小空间

SwiftUI:为什么@State在子视图中持续存在?

带DispatchSourceTimer的定时器

查找数组中 ** 元素 ** 的属性的最小值和最大值

如何在枚举有关联数据时使用combined if with case

如何隐藏集合视图中特定的单元格,以避免出现空白空间?

DispatchQueue.main.asyncAfter 等同于 Swift 中的 struct 化并发?

可以使 Swift init 仅可用于 Objective C 吗?

自定义碰撞形状出现在错误的位置

Swift 数组拆分成重叠的块

如何在 SwiftUI 中创建条件内容?

String(validatingUTF8:) 和 String(utf8String:) 之间有区别吗?

SwiftUI:异步网络请求后@State 值不更新

在 Swift 5.5 中编写同步和异步函数

Swift:在子类中覆盖 == 导致仅在超类中调用 ==

即使设置为从不也可以访问 iOS11 照片库

使用 ARAnchor 插入 node 和直接插入 node 有什么区别?