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

SwiftUI教程:如何通过AttributedString在App内打开链接而非Safari

在SwiftUI中实现AttributedString链接的App内跳转与邮件功能

一、App内打开网页链接

要让AttributedString中的http/https链接在App内打开,需先封装WKWebView的SwiftUI视图,再通过onOpenURL捕获链接点击事件,导航到该WebView。

  1. 封装WKWebView视图
import SwiftUI
import WebKit

// SwiftUI包装的WKWebView控制器
struct WebView: UIViewControllerRepresentable {
    let url: URL
    
    func makeUIViewController(context: Context) -> WKWebViewController {
        let vc = WKWebViewController()
        vc.loadURL(url)
        return vc
    }
    
    func updateUIViewController(_ uiViewController: WKWebViewController, context: Context) {}
}

class WKWebViewController: UIViewController {
    private let webView = WKWebView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(webView)
        webView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            webView.topAnchor.constraint(equalTo: view.topAnchor),
            webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
    
    func loadURL(_ url: URL) {
        webView.load(URLRequest(url: url))
    }
}
  1. 在FAQ父视图中处理链接跳转
    将onOpenURL添加到FAQ列表页面(或包含faqCell的父视图),捕获链接后弹出WebView:
struct FAQView: View {
    @State private var showWebView = false
    @State private var currentWebURL: URL?
    
    var body: some View {
        List {
            faqCell()
            // 添加其他FAQ单元格
        }
        .sheet(isPresented: $showWebView) {
            if let url = currentWebURL {
                WebView(url: url)
                    .navigationTitle("网页")
                    .navigationBarTitleDisplayMode(.inline)
            }
        }
        .onOpenURL { url in
            // 判断是网页链接则打开WebView
            if url.scheme == "http" || url.scheme == "https" {
                currentWebURL = url
                showWebView = true
            } else if url.scheme == "mailto" {
                // 处理邮件链接,下文实现
                handleMailLink(url)
            }
        }
    }
    
    // 邮件链接处理方法(先占位,下文实现)
    private func handleMailLink(_ url: URL) {}
}

二、实现邮件链接的MailView功能

对于mailto链接,需封装MFMailComposeViewController为SwiftUI视图,同时处理设备是否支持邮件发送的判断。

  1. 封装MailView
import SwiftUI
import MessageUI

struct MailView: UIViewControllerRepresentable {
    let to: [String]?
    let subject: String?
    let body: String?
    
    @Binding var isPresented: Bool
    
    func makeUIViewController(context: Context) -> MFMailComposeViewController {
        let vc = MFMailComposeViewController()
        vc.mailComposeDelegate = context.coordinator
        // 填充邮件参数
        if let to = to { vc.setToRecipients(to) }
        if let subject = subject { vc.setSubject(subject) }
        if let body = body { vc.setMessageBody(body, isHTML: false) }
        return vc
    }
    
    func updateUIViewController(_ uiViewController: MFMailComposeViewController, context: Context) {}
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, MFMailComposeViewControllerDelegate {
        let parent: MailView
        
        init(_ parent: MailView) {
            self.parent = parent
        }
        
        // 邮件发送完成后关闭视图
        func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
            parent.isPresented = false
        }
    }
}

// 扩展:判断设备是否支持邮件发送
extension UIApplication {
    static func canSendMail() -> Bool {
        MFMailComposeViewController.canSendMail()
    }
}
  1. 在FAQView中完善邮件链接处理
    添加状态变量和处理逻辑:
struct FAQView: View {
    // ... 原有状态变量 ...
    @State private var showMailView = false
    @State private var mailTo: [String]?
    @State private var mailSubject: String?
    @State private var mailBody: String?
    
    var body: some View {
        // ... 原有视图内容 ...
        .sheet(isPresented: $showMailView) {
            MailView(to: mailTo, subject: mailSubject, body: mailBody, isPresented: $showMailView)
        }
    }
    
    private func handleMailLink(_ url: URL) {
        guard UIApplication.canSendMail() else {
            // 可添加Alert提示用户无法发送邮件
            return
        }
        
        // 解析mailto链接中的收件人、主题、正文参数
        let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
        mailTo = components?.toRecipients
        mailSubject = components?.queryItems?.first(where: { $0.name == "subject" })?.value
        mailBody = components?.queryItems?.first(where: { $0.name == "body" })?.value
        
        showMailView = true
    }
}

三、测试邮件链接示例

修改你的LinkFormatter,添加一个mailto链接的示例:

struct faqCell : View {
    var mediumLink = LinkFormatter.toAS(for: "you can always read our Medium blog.",
                                        linkRange: "Medium blog",
                                        to: "https://medium.com")
    // 添加邮件链接示例
    var supportMailLink = LinkFormatter.toAS(for: "Contact our support team via email.",
                                             linkRange: "email",
                                             to: "mailto:support@example.com?subject=FAQ咨询&body=我有如下问题:")
    
    var body : some View {
        VStack(alignment: .leading) {
            Text("title")
            Text(mediumLink)
                .foregroundColor(.gray)
            Text(supportMailLink)
                .foregroundColor(.gray)
                .padding(.top, 8)
        }
    }
}

内容的提问来源于stack exchange,提问作者Anna

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 15:45:04