You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:32:36