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

SwiftUI侧边栏菜单:实现WebView与自定义视图跳转切换

解决SwiftUI侧边栏同时支持WebView和自定义视图跳转的问题

核心思路

通过枚举统一管理网页视图、自定义视图两种跳转类型,利用NavigationLink根据选中的目标动态加载对应视图,替代原有的单一网页跳转逻辑。

修改后的完整代码

import SwiftUI
import WebKit

// 定义所有可能的跳转目标类型
enum Destination {
    case web(URL)
    case about
}

struct ContentView: View {
    @State private var isSideMenuOpen = true
    @State private var selectedDestination: Destination? // 替换原有的webViewURL
    
    var body: some View {
        NavigationView {
            GeometryReader { geometry in
                ZStack {
                    // 主内容区域(默认空白,可替换为你的初始视图)
                    Color.white
                    
                    // 侧边栏遮罩层
                    if isSideMenuOpen {
                        Color.black.opacity(0.5).edgesIgnoringSafeArea(.all)
                            .onTapGesture {
                                withAnimation {
                                    isSideMenuOpen.toggle()
                                }
                            }
                    }
                    
                    // 侧边栏菜单
                    VStack {
                        if isSideMenuOpen || geometry.size.width > 800 {
                            SideMenuView(isOpen: $isSideMenuOpen, didSelectDestination: { destination in
                                selectedDestination = destination
                                // 非宽屏模式下选中后关闭侧边栏
                                if geometry.size.width <= 800 {
                                    withAnimation {
                                        isSideMenuOpen = false
                                    }
                                }
                            })
                            .transition(.move(edge: .leading))
                        }
                        
                        Spacer()
                    }
                }
                .navigationBarTitle(Text("App"))
            }
            .background(
                // 根据选中的目标加载对应视图
                NavigationLink(
                    destination: destinationView,
                    isActive: Binding<Bool>(
                        get: { selectedDestination != nil },
                        set: { _ in selectedDestination = nil }
                    ),
                    label: EmptyView.init
                )
            )
        }
    }
    
    // 根据目标类型返回对应的视图
    private var destinationView: some View {
        switch selectedDestination {
        case .web(let url):
            return AnyView(WebView(url: url))
        case .about:
            return AnyView(AboutView())
        case nil:
            return AnyView(EmptyView())
        }
    }
}

struct SideMenuView: View {
    @Binding var isOpen: Bool
    var didSelectDestination: (Destination) -> Void // 更新闭包参数类型
    
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            Button(action: {
                if let url = URL(string: "https://www.example.com") {
                    didSelectDestination(.web(url))
                }
            }) {
                Text("page 1")
            }
            
            Button(action: {
                if let url = URL(string: "https://www.example.com/page2") {
                    didSelectDestination(.web(url))
                }
            }) {
                Text("page 2")
            }
            
            Button(action: {
                didSelectDestination(.about) // 触发自定义视图跳转
            }) {
                Text("About")
            }
            
            // 可添加更多菜单项
        }
        .padding()
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
        .background(Color.white)
        .transition(.move(edge: .leading))
    }
}

struct WebView: UIViewRepresentable {
    let url: URL

    func makeUIView(context: Context) -> WKWebView {
        return WKWebView()
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        let request = URLRequest(url: url)
        uiView.load(request)
    }
}

// 自定义About视图(需确保你已定义该视图)
struct AboutView: View {
    var body: some View {
        Text("About This App")
            .navigationBarTitle("About")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

关键修改点

  • 新增Destination枚举:统一管理网页和自定义视图两种跳转类型,避免单独维护多个状态变量。
  • 替换状态变量:用selectedDestination替代原有的webViewURL,支持多种目标类型。
  • 更新侧边栏闭包:SideMenuView的回调改为传递Destination类型,让主视图统一处理跳转逻辑。
  • 动态生成目标视图:通过destinationView计算属性,根据选中的目标返回对应的视图(使用AnyView实现类型擦除)。
  • 优化侧边栏交互:非宽屏模式下选中菜单项后自动关闭侧边栏,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 13:33:17