SwiftUI教程:如何通过AttributedString在App内打开链接而非Safari
在SwiftUI中实现AttributedString链接的App内跳转与邮件功能
一、App内打开网页链接
要让AttributedString中的http/https链接在App内打开,需先封装WKWebView的SwiftUI视图,再通过onOpenURL捕获链接点击事件,导航到该WebView。
- 封装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)) } }
- 在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视图,同时处理设备是否支持邮件发送的判断。
- 封装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() } }
- 在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
相关产品推荐
相关产品推荐

