使用SwiftUI以编程方式发送短信

9
在 Swift 中,我会这样做来显示一个预先组合的消息。
let composeVC = MFMessageComposeViewController()
composeVC.messageComposeDelegate = self


composeVC.recipients = ["9999999999"]
composeVC.body = "Text Message"


if MFMessageComposeViewController.canSendText() {
    self.present(composeVC, animated: true, completion: nil)
}

在SwiftUI中,这会抛出错误。
Cannot assign value of type 'ContentView' to type 'MFMessageComposeViewControllerDelegate?'

并且
Value of type 'ContentView' has no member 'present'
2个回答

11
你需要在 Windows 根视图上进行演示。使用这些扩展。
import SwiftUI
import MessageUI
/// Main View
struct ContentView: View {

    private let mailComposeDelegate = MailComposerDelegate()

    private let messageComposeDelegate = MessageComposerDelegate()

    var body: some View {
        VStack {
            Spacer()
            Button(action: {
                self.presentMailCompose()
            }) {
                Text("email")
            }

            Spacer()

           Button(action: {
               self.presentMessageCompose()
           }) {
               Text("Message")
           }
            Spacer()
        }
    }
}

// MARK: The email extension

extension ContentView {

    private class MailComposerDelegate: NSObject, MFMailComposeViewControllerDelegate {
        func mailComposeController(_ controller: MFMailComposeViewController,
                                   didFinishWith result: MFMailComposeResult,
                                   error: Error?) {

            controller.dismiss(animated: true)
        }
    }
    /// Present an mail compose view controller modally in UIKit environment
    private func presentMailCompose() {
        guard MFMailComposeViewController.canSendMail() else {
            return
        }
        let vc = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController
        let composeVC = MFMailComposeViewController()
        composeVC.mailComposeDelegate = mailComposeDelegate

        vc?.present(composeVC, animated: true)
    }
}

// MARK: The message extension

extension ContentView {

    private class MessageComposerDelegate: NSObject, MFMessageComposeViewControllerDelegate {
        func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
            // Customize here
            controller.dismiss(animated: true)
        }
    }
    /// Present an message compose view controller modally in UIKit environment
    private func presentMessageCompose() {
        guard MFMessageComposeViewController.canSendText() else {
            return
        }
        let vc = UIApplication.shared.windows.filter {$0.isKeyWindow}.first?.rootViewController
        let composeVC = MFMessageComposeViewController()
        composeVC.messageComposeDelegate = messageComposeDelegate

        vc?.present(composeVC, animated: true)
    }
}

输入图像描述

灵感和来源


10

在SwiftUI中呈现这个视图控制器(或大多数任何UIViewController)的最佳方法是使用UIViewControllerRepresentable协议创建一个结构体,然后在您的视图构建器中使用sheet()来展示它。

// MessageComposeView.swift

import MessageUI
import SwiftUI

struct MessageComposeView: UIViewControllerRepresentable {
    typealias Completion = (_ messageSent: Bool) -> Void

    static var canSendText: Bool { MFMessageComposeViewController.canSendText() }
        
    let recipients: [String]?
    let body: String?
    let completion: Completion?
    
    func makeUIViewController(context: Context) -> UIViewController {
        guard Self.canSendText else {
            let errorView = MessagesUnavailableView()
            return UIHostingController(rootView: errorView)
        }
        
        let controller = MFMessageComposeViewController()
        controller.messageComposeDelegate = context.coordinator
        controller.recipients = recipients
        controller.body = body
        
        return controller
    }
    
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
    
    func makeCoordinator() -> Coordinator {
        Coordinator(completion: self.completion)
    }
    
    class Coordinator: NSObject, MFMessageComposeViewControllerDelegate {
        private let completion: Completion?

        public init(completion: Completion?) {
            self.completion = completion
        }
        
        public func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
            controller.dismiss(animated: true, completion: nil)
            completion?(result == .sent)
        }
    }
}

struct MessagesUnavailableView: View {
    var body: some View {
        VStack {
            Image(systemName: "xmark.octagon")
                .font(.system(size: 64))
                .foregroundColor(.red)
            Text("Messages is unavailable")
                .font(.system(size: 24))
        }
    }
}

一旦你定义好了,你可以像这样使用它

struct MyView: View {
    @State private var isShowingMessages = false
    var body: some View {
        Button("Show Messages") {
            self.isShowingMessages = true
        }
        .sheet(isPresented: self.$isShowingMessages) {
            MessageComposeView(recipients: ["recipients go here"], body: "Message goes here") { messageSent in
                print("MessageComposeView with message sent? \(messageSent)")
            }
        }
    }
}

MessageComposeView 对我来说真的很有 bug,视图的某些部分会消失,而且它会随机出现故障。你有遇到过这种情况吗? - bze12
我正在生产中使用这段代码,而且我没有遇到过那个问题 - 你确认你是在主线程上调用它吗? - Aaron T
1
我发现在呈现时必须忽略键盘安全区域。 - bze12
3
对于我而言,忽略键盘安全区域是有效的。为了澄清,忽略安全区域需要使用以下视图修饰符:MessageComposeView(...){...}.ignoresSafeArea(.keyboard)。 - dwaz
我已经尝试了这个解决方案(包括ignoresSafeArea的添加)以及Jawad Ali的解决方案,但对于我来说两者都有一些小问题。就像在点击缩小的文本区域使它变大时,该区域首先变大,但然后在最终再次完全打开之前会瞬间变小。删除文本可能有点卡顿,并且完成后需要大约两秒钟才能关闭视图。有人知道如何解决这个问题吗? - wristbands
我会移除controller.dismiss(animated: true, completion: nil)这一行代码,并使用绑定来控制展示。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接