SwiftUI中如何配置onOpenUrl实现Widget跳转指定视图?
问题描述
在大尺寸Widget视图LargeMetarView中添加了Link组件,使每个机场条目可点击跳转,但点击后仅打开主应用首页,无法跳转到主应用内的WidgetStationView展示选中机场的详情。目前仅需实现链接跳转功能,尚未完成视图的完整数据展示。此前主要使用UIKit开发,刚接触SwiftUI,怀疑问题出在URL Scheme、host及path的定义上。
相关代码
LargeMetarView 代码
struct LargeMetarView: View { @ObservedObject var metarservice = MetarService() @State private var skyImage = String() var entry: MetarEntry var counter: Int = 0 var body: some View { ZStack(alignment: .topLeading) { LinearGradient(gradient: Gradient(colors: [.black.opacity(1), .black.opacity(0.7), .black.opacity(1)]), startPoint: .bottomLeading, endPoint: .topTrailing) HStack(alignment: .top) { Text("FRAT Metars") Text("Updated: ") Text(entry.date, style: .time) } .frame(maxWidth: .infinity) .padding(10) .background(.blue) .foregroundColor(.white) .clipped() .shadow(radius: 8) HStack(alignment: .top, spacing: 8) { VStack(alignment: .leading, spacing: 5){ Spacer() ForEach(entry.metars) { entries in Link(destination: URL(string: "frat://metar/\(entries.mairport)")!){ HStack(spacing: 5){ switch entries.mfltcat { case "VFR": Image(systemName: entries.metarImage) .renderingMode(.original) .font(.system(size: 25)) .foregroundColor(.green) .shadow(color: .black, radius: 1, x: 0.5, y: 0.5) case "MVFR": Image(systemName: entries.metarImage) .renderingMode(.original) .font(.system(size: 25)) .foregroundColor(.cyan) .shadow(color: .black, radius: 1, x: 0.5, y: 0.5) case "IFR": Image(systemName: entries.metarImage) .renderingMode(.original) .font(.system(size: 25)) .foregroundColor(.red) .shadow(color: .black, radius: 1, x: 0.5, y: 0.5) case "LIFR": Image(systemName: entries.metarImage) .renderingMode(.original) .font(.system(size: 25)) .foregroundColor(.purple) .shadow(color: .black, radius: 1, x: 0.5, y: 0.5) default: Image(systemName: entries.metarImage) .renderingMode(.original) .font(.system(size: 25)) .foregroundColor(.green) .shadow(color: .black, radius: 1, x: 0.5, y: 0.5) } Text(entries.mairport) switch entries.mfltcat { case "VFR": Text(entries.mfltcat).bold() .foregroundColor(Color.green) case "MVFR": Text(entries.mfltcat).bold() .foregroundColor(Color.cyan) case "IFR": Text(entries.mfltcat).bold() .foregroundColor(Color.red) case "LIFR": Text(entries.mfltcat).bold() .foregroundColor(Color.purple) default: Text(entries.mfltcat) .foregroundColor(Color.white).bold() } Text(entries.mobstime + "Z") Text(entries.mvis) Text(entries.mwind) } // End of HSTACK } .foregroundColor(.white) .font(.subheadline) Divider() .frame(height: 2) .overlay(.white) } } .padding(20) }.onAppear(perform: WidgetCenter.shared.reloadAllTimelines) // HSTACK Spacer() } // ZSTACK //.widgetURL(URL(string: "metarnav")) } // End of someview struct LargeMetarView_Previews: PreviewProvider { static var previews: some View { LargeMetarView(entry: MetarEntry(date: .now, metars: tempData)) .previewContext(WidgetPreviewContext(family: .systemLarge)) } } }
WidgetStationView 代码
import SwiftUI import WidgetKit struct WidgetStationView: View { var mdm: MetarDataModel var selectedStation: MetarDataModel? var metarservice = MetarService() var body: some View { HStack { Text("Metar Data") } GroupBox { VStack(alignment: .leading) { Text("\(mdm.mairport)") .font(.headline) } } label: { Label("Metar #\(mdm.mairport)", systemImage: "person") } .padding() } // end of view } func getStation(with apt: String) async -> [MetarDataModel] { let metarservice = MetarService() var metars = [MetarDataModel]() do { metars = try await metarservice.fetchMetars(metarAPTs: apt) } catch { } return metars }
解决方案
1. 确认URL Scheme配置
在项目的Info.plist中添加URL Scheme配置:
- 添加
CFBundleURLTypes数组,其中包含一个字典 - 字典内添加
CFBundleURLSchemes数组,值为frat - 可选:添加
CFBundleURLName,设置为唯一标识符(比如com.yourcompany.frat)
2. 主应用处理URL跳转
SwiftUI应用需要在根层级处理URL打开事件,将跳转逻辑放在@main标记的App结构体中,配合导航栈实现页面跳转:
@main struct FRATApp: App { @State private var navigationPath = NavigationPath() var body: some Scene { WindowGroup { NavigationStack(path: $navigationPath) { // 替换为你的主应用首页视图 HomeView() .navigationDestination(for: String.self) { airportCode in // 根据机场码初始化WidgetStationView WidgetStationView(mdm: MetarDataModel(mairport: airportCode)) } } .onOpenURL { url in // 解析URL,提取机场码 guard url.scheme == "frat", url.host == "metar", let airportCode = url.pathComponents.last else { return } // 将机场码加入导航栈,触发跳转 navigationPath.append(airportCode) } } } }
3. 修正URL解析逻辑
你的Widget中生成的URL格式为frat://metar/KJFK,其中path是/KJFK,因此不能直接用url.path == mdm.mairport判断,需要通过pathComponents.last获取机场码(即KJFK)。
4. 移除WidgetStationView中的onOpenURL
原代码中WidgetStationView的onOpenURL不会生效,因为从Widget打开应用时该视图尚未加载,跳转逻辑必须放在主应用的根层级处理。
内容的提问来源于stack exchange,提问作者ZDigitalPro
相关产品推荐
相关产品推荐

