iOS:如何使用侧边菜单、SwiftUI 切换视图

Posted

技术标签:

【中文标题】iOS:如何使用侧边菜单、SwiftUI 切换视图【英文标题】:iOS: How to switch views with a Side Menu, SwitftUI 【发布时间】:2021-06-10 22:41:40 【问题描述】:

首先,我对 ios 开发和 Swift 非常陌生(从 php 到这里需要 2 周 :))

我正在尝试构建一个带有侧边菜单的 iOS 应用程序。我的意图是,当我单击菜单中的一个项目时,新视图将出现在屏幕上,如“HomeViewController”以及每个后续项目,如 example1、2 等(代替菜单按钮,注意我将添加顶部导航栏即将打开菜单) 我想知道如何才能完成此功能?

谢谢

ContentView.swift

import SwiftUI

struct MenuItem: Identifiable 
    var id = UUID()
    let text: String


func controllView(clickedview:String) 
print(clickedview)


struct MenuContent: View
    
    let items: [MenuItem] = [
    MenuItem(text: "Home"),
    MenuItem(text: "Example1"),
    MenuItem(text: "Example2"),
    MenuItem(text: "Example3")
    ]
    
    
    var body: some View 
        ZStack 
            Color(UIColor(red: 33/255.0, green: 33/255.0, blue: 33/255.0, alpha: 1))
            
            VStack(alignment: .leading, spacing: 0) 
                
                ForEach(items) items in
                    HStack 
                        Text(items.text)
                            .bold()
                            .font(.system(size: 20))
                            .multilineTextAlignment(/*@START_MENU_TOKEN@*/.leading/*@END_MENU_TOKEN@*/)
                            .foregroundColor(Color.white)
                        Spacer()
                    
                    .onTapGesture 
                        controllView(clickedview: items.text)
                    
                    .padding()
                    
                    Divider()
                
                
                Spacer()
                
            
            .padding(.top, 40)
        
    


struct SideMenu: View 
    let width: CGFloat
    let menuOpen: Bool
    let toggleMenu: () -> Void
    
    var body: some View 
        ZStack 
            //Dimmed backgroud
            GeometryReader  _ in
                EmptyView()
            
            .background(Color.gray.opacity(0.15))
            .opacity(self.menuOpen ? 1 : 0)
            .animation(Animation.easeIn.delay(0.25))
            .onTapGesture 
                self.toggleMenu()
            
            
            //Menucontent
            HStack 
                MenuContent()
                    .frame(width: width)
                    .offset(x: menuOpen ? 0 : -width)
                    .animation(.default)
                
                Spacer()
            
            
        
    


struct ContentView: View 
    @State var menuOpen = false
    
    var body: some View 
        
        let drag = DragGesture()
                    .onEnded 
                        if $0.translation.width < -100 
                            if menuOpen 
                            withAnimation 
                                print("Left")
                                menuOpen.toggle()
                            
                        
                    
                        if $0.translation.width > -100 
                            if !menuOpen 
                            withAnimation 
                                print("Right")
                                menuOpen.toggle()
                            
                        
                    
                

        ZStack 
            if !menuOpen 
            Button(action: 
                self.menuOpen.toggle()
            , label: 
                Text("Open Menu")
                    .bold()
                    .foregroundColor(Color.white)
                    .frame(width: 200, height: 50, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
                    .background(Color(.systemBlue))
            )
            
            SideMenu(width: UIScreen.main.bounds.width/1.6, menuOpen: menuOpen, toggleMenu: toggleMenu)
        
        .edgesIgnoringSafeArea(.all)
        .gesture(drag)
    
    
    func toggleMenu()
        menuOpen.toggle()
    






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

上述代码中的点击命令:

.onTapGesture 
   controllView(clickedview: items.text)

HomeViewController.swift

import UIKit
import WebKit
class HomeViewController: UIViewController, WKNavigationDelegate 

    var webView: WKWebView!
    
    override func loadView() 
        webView = WKWebView()
        webView.navigationDelegate = self
        view = webView
    
    
    override func viewDidLoad() 
        super.viewDidLoad()
        let url = URL(string: "https://developer.apple.com")!
        webView.load(URLRequest(url: url))
    


【问题讨论】:

【参考方案1】:

你需要一些材料:

一种存储当前活动视图状态的方法 一种在菜单和主要内容视图之间传达状态的方法

对于第一个,我创建了一个枚举,列出了不同类型的视图 (ViewType),并将其添加到您的 MenuItem 模型中。

对于第二个,您可以通过 @Binding 将状态从父视图传递到子视图并备份链。

struct MenuItem: Identifiable 
    var id = UUID()
    let text: String
    let viewType : ViewType


enum ViewType 
    case home, example1, example2, example3


struct MenuContent: View
    @Binding var activeView : ViewType
    
    let items: [MenuItem] = [
        MenuItem(text: "Home", viewType: .home),
        MenuItem(text: "Example1", viewType: .example1),
        MenuItem(text: "Example2", viewType: .example2),
        MenuItem(text: "Example3", viewType: .example3)
    ]
    
    
    var body: some View 
        ZStack 
            Color(UIColor(red: 33/255.0, green: 33/255.0, blue: 33/255.0, alpha: 1))
            
            VStack(alignment: .leading, spacing: 0) 
                
                ForEach(items)  item in
                    HStack 
                        Text(item.text)
                            .bold()
                            .font(.system(size: 20))
                            .multilineTextAlignment(.leading)
                            .foregroundColor(Color.white)
                        Spacer()
                    
                    .onTapGesture 
                        activeView = item.viewType
                    
                    .padding()
                    
                    Divider()
                
                
                Spacer()
                
            
            .padding(.top, 40)
        
    


struct SideMenu: View 
    let width: CGFloat
    let menuOpen: Bool
    let toggleMenu: () -> Void
    @Binding var activeView : ViewType
    
    var body: some View 
        ZStack 
            //Dimmed backgroud
            GeometryReader  _ in
                EmptyView()
            
            .background(Color.gray.opacity(0.15))
            .opacity(self.menuOpen ? 1 : 0)
            .animation(Animation.easeIn.delay(0.25))
            .onTapGesture 
                self.toggleMenu()
            
            
            //Menucontent
            HStack 
                MenuContent(activeView: $activeView)
                    .frame(width: width)
                    .offset(x: menuOpen ? 0 : -width)
                    .animation(.default)
                
                Spacer()
            
            
        
    


struct ContentView: View 
    @State private var menuOpen = false
    @State private var activeView : ViewType = .home
    
    var body: some View 
        
        let drag = DragGesture()
            .onEnded 
                if $0.translation.width < -100 
                    if menuOpen 
                        withAnimation 
                            print("Left")
                            menuOpen.toggle()
                        
                    
                
                if $0.translation.width > -100 
                    if !menuOpen 
                        withAnimation 
                            print("Right")
                            menuOpen.toggle()
                        
                    
                
            
        
        ZStack 
            
            VStack 
                if !menuOpen 
                    Button(action: 
                        self.menuOpen.toggle()
                    , label: 
                        Text("Open Menu")
                            .bold()
                            .foregroundColor(Color.white)
                            .frame(width: 200, height: 50, alignment: .center)
                            .background(Color(.systemBlue))
                    )
                
                switch activeView 
                case .home:
                    HomeViewControllerRepresented()
                case .example1:
                    Text("Example1")
                case .example2:
                    Text("Example2")
                case .example3:
                    Text("Example3")
                
            
            
            SideMenu(width: UIScreen.main.bounds.width/1.6,
                     menuOpen: menuOpen,
                     toggleMenu: toggleMenu,
                     activeView: $activeView)
             .edgesIgnoringSafeArea(.all)
        
        .gesture(drag)
    
    
    func toggleMenu()
        menuOpen.toggle()
    


struct HomeViewControllerRepresented : UIViewControllerRepresentable 
    func makeUIViewController(context: Context) -> HomeViewController 
        HomeViewController()
    
    
    func updateUIViewController(_ uiViewController: HomeViewController, context: Context) 
        
    

【讨论】:

您好,感谢您的快速回复。我正在尝试执行您的解释,但我收到错误“引用静态方法 'buildEither(first:)' on '_ConditionalContent' 要求 'HomeViewController' 符合 'View' 我在上面附上了一个屏幕截图谢谢 啊——我没注意到这是UIViewController。你知道UIViewControllerRepresentable吗? 不幸的是,我没有 :(,我边走边学 好的,我现在已经为它加了一个垫片。您可能想对它进行一些研究,了解更多关于如何使用它以及与 SwiftUI 的互操作性。 太好了,工作非常感谢,非常感谢您的帮助和建议 :)

以上是关于iOS:如何使用侧边菜单、SwiftUI 切换视图的主要内容,如果未能解决你的问题,请参考以下文章

在导航到另一个视图控制器之前切换标签栏

使用SwiftUI。单击后,“我的滑块/侧面菜单”会启动新视图,但单击按钮,现在所有选项均为'dead'

SwiftUI 绑定到父视图重新渲染子视图

如何在ios中同时实现标签栏和侧边菜单?

带有平移手势冲突的滚动视图和侧边菜单

故事板上的侧边栏导航菜单