SwiftUI中正确呈现MFMailComposeViewController的问题
解决SwiftUI中MFMailComposeViewController弹出动画异常的问题
问题根源在于SwiftUI的sheet会将UIKit视图控制器包裹在自身的模态容器中,破坏了MFMailComposeViewController原生的底部弹出动画与样式。改用直接通过UIKit层级present的方式,就能还原UIKit中的原生效果。
解决方案步骤
1. 添加顶层视图控制器获取扩展
实现扩展获取当前应用的顶层UIViewController,用于直接present邮件控制器:
import UIKit extension UIViewController { static func topViewController() -> UIViewController? { // 获取当前活跃的窗口场景 let activeScene = UIApplication.shared.connectedScenes .filter { $0.activationState == .foregroundActive } .compactMap { $0 as? UIWindowScene } .first guard let keyWindow = activeScene?.windows.first(where: { $0.isKeyWindow }) else { return nil } // 遍历找到顶层视图控制器 var topController = keyWindow.rootViewController while let presentedVC = topController?.presentedViewController { topController = presentedVC } return topController } }
2. 实现邮件回调代理类
创建独立代理类处理邮件发送完成后的回调逻辑:
import MessageUI class MailComposeDelegate: NSObject, MFMailComposeViewControllerDelegate { private let completion: (MFMailComposeResult?, Error?) -> Void init(completion: @escaping (MFMailComposeResult?, Error?) -> Void) { self.completion = completion super.init() } func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { controller.dismiss(animated: true) completion(result, error) } }
3. 修改ContentView的邮件触发逻辑
移除原sheet实现,改为直接在按钮点击时present原生邮件控制器:
import SwiftUI import MessageUI struct ContentView: View { @State var result: Result<MFMailComposeResult, Error>? = nil var body: some View { Button(action: { guard let topVC = UIViewController.topViewController() else { return } let mailVC = MFMailComposeViewController() // 设置代理并处理回调 mailVC.mailComposeDelegate = MailComposeDelegate { result, error in if let error = error { self.result = .failure(error) } else { self.result = .success(result!) } // 根据发送状态做后续处理示例 switch result { case .sent: print("邮件已发送") case .cancelled: print("邮件发送已取消") case .saved: print("邮件已保存") case .failed: print("邮件发送失败") default: break } } // 呈现邮件控制器,使用原生动画 topVC.present(mailVC, animated: true) }, label: { Label("Send mail", systemImage: "envelope") }) .disabled(!MFMailComposeViewController.canSendMail()) } }
效果说明
这种方式完全复用了UIKit中MFMailComposeViewController的原生弹出动画与样式,不会出现SwiftUIsheet带来的适配问题,和UIKit项目中的表现完全一致。
内容的提问来源于stack exchange,提问作者Martin Gunnarsson
相关产品推荐
相关产品推荐

