繁体   English   中英

SwiftUI - 单击按钮打开新视图

[英]SwiftUI - Opening a new view on button click

watchOS 和 SwiftUI 中的菜鸟。 我创建了一个带有多个按钮的网格视图。 每当单击按钮时,我希望打开一个带有导航链接的新视图。 由于视图上有多个按钮,我创建了一个可重用的视图并且很难实现导航到下一个视图。 下面是我的代码:

内容视图:

struct ContentView: View {
    @Namespace var namespace
    @State var selected: [MenuItem] = []
    
    var body: some View {
        MainMenuCircularGridView()
    }
}

MainMenuCircularGridView:

struct MainMenuCircularGridView: View {
    let columns = Array(repeating: GridItem(.fixed(72.0), spacing: 10), count: 2)
    
    var body: some View {
        NavigationView {
            ScrollView {
                let menuOptions = MenuOptions()
                let menuOptionsAction = MenuActions()
                LazyVGrid(columns: columns, spacing: 5) {
                    ForEach(menuOptions.menu) { item in
                        MenuItemCircularGridView(imageName: item.imageName, menuItemName: item.name, action: (menuOptionsAction.menuActions.first {$0.id == item.id})?.action ?? {})
                    }
                }.padding()
            }.navigationTitle("Sample App")
        }
    }
}

菜单项圆形网格视图:

struct MenuItemCircularGridView: View {
    var imageName: String = ""
    var menuItemName: String = ""
    var action: () -> Void
    
    var body: some View {
        VStack {
            CircularButtonWithImage(imageName:imageName,
                                    imageBackgroundColor:Color(red: 34 / 255, green: 34 / 255, blue: 34 / 255),
                                    imageForegroundColor: Color(red: 23 / 255, green: 121 / 255, blue: 232 / 255),
                                    imageFrameWidth: 30.0,
                                    imageFrameHeight: 30.0,
                                    imagePadding: 10.0,
                                    action: action)
            Text(menuItemName).font(.system(size: 10))
        }.padding(10)
    }
}

CircularButtonWithImage:

struct CircularButtonWithImage: View {
    var imageName: String = ""
    var imageBackgroundColor: Color?
    var imageForegroundColor: Color?
    var imageFrameWidth: CGFloat = 0.0
    var imageFrameHeight: CGFloat = 0.0
    var imagePadding: CGFloat = 0.0
    var action: () -> Void
    
    var body: some View {
        Button(action: { action() }) {
            VStack{
                Image(imageName)
                    .renderingMode(.template)
                    .resizable()
                    .scaledToFill()
                    .frame(width: imageFrameWidth, height: imageFrameHeight)
                    .padding(imagePadding)
                    .background(imageBackgroundColor)
                    .foregroundColor(imageForegroundColor)
                    .clipShape(Circle())
            }
        }
        .buttonStyle(PlainButtonStyle())
    }
}

这是我的应用程序的外观:

在此处输入图像描述

每当我点击这些按钮中的任何一个时,我都想打开一个带有导航链接的新视图。 如下所示:

NavigationLink(destination: DetailView()) {
     Text("Show Detail View")
}.navigationBarTitle("Navigation")

由于我已将视图分解为多个可重用文件,因此我不确定我应该将这个逻辑放在哪里以在按钮单击时打开一个新视图。

编辑:添加我正在使用的硬编码数据。 我试图将导航链接作为操作传递给按钮。

struct MenuOptions {
    let menu: [MenuItem] = [
        MenuItem(id: 0, name: "Option 1", imageName: "settings-gray"),
        MenuItem(id: 1, name: "Option 2", imageName: "settings-gray"),
        MenuItem(id: 2, name: "Option 3", imageName: "settings-gray"),
        MenuItem(id: 3, name: "Set 1", imageName: "settings-gray"),
        MenuItem(id: 4, name: "Set 2", imageName: "settings-gray"),
        MenuItem(id: 5, name: "Settings", imageName: "settings-gray")
    ]
}

struct MenuActions {
    let menuActions: [MenuItemAction] = [
        MenuItemAction(id: 1, action: { NavigationLink("New View 1", destination: View1()) }),
        MenuItemAction(id: 5, action: { NavigationLink("Settings", destination: SettingsView()) })
    ]
}

NavigationLink必须在视图层次结构中,因此我们需要在其中放置一些模型,而不是将其付诸实施。

可能的方法的草图

  1. 目的地模型
enum MenuDestination: String, CaseIterable, Hashable {
    case set1(MenuItem), set2

    @ViewBuilder var view: some View {
        switch self {
        case .set1(let item): View1(item: item)
        case .set2: SettingsView()
        }
    }
}
  1. 视图中的导航链接
    @State private var selection: MenuDestination?
    var isActive: Binding<Bool> {
      Binding(get: { selection != nil }, set: { selection = nil } )
    }

    var body: some View {
        NavigationView {
            ScrollView {
               // ...
            }
            .background(
                if let selection = selection {
                   NavigationLink(isActive: isActive, destination: { selection.view }) {
                   EmptyView()
             }})
        }
    }
  1. 按钮操作分配相应的值,例如MenuItemAction将作为参数绑定到选择并在内部将目标分配给该绑定
MenuItemCircularGridView(imageName: item.imageName, menuItemName: item.name, 
   action: (menuOptionsAction.menuActions.first {$0.id == item.id})?.action($selection) ?? { _ in })

MenuItemAction以相应MenuDestination的情况启动

另见这篇文章

我意识到我将视图分解为不同的文件可能会使事情变得更加复杂。 由于我只有 6 个按钮,因此我决定将视图保存在一个文件中,并使用您创建 MenuDestination 的想法来传递正确的视图。 在下面分享我的更新代码。

然而,我想使用 NavigationLink 的主要原因是在目标视图上获取后退按钮按钮和导航标题名称以返回主菜单。 我没有在目标视图的左上方看到带有导航标题的后退按钮。 如果您知道我可能会丢失什么,请告诉我。 我的理解是使用 NavigationLink 自动将其添加到目标视图。

更新代码 - MainMenuCircularGridViewNew:

import SwiftUI

struct MainMenuCircularGridViewNew: View {
    let columns = Array(repeating: GridItem(.fixed(72.0), spacing: 10), count: 2)
    @State private var destination: MenuDestination?
    var isActive: Binding<Bool> { Binding(get: { destination != nil }, set: { _ in destination = nil } ) }
    
    var body: some View {
        NavigationView {
            ScrollView {
                let menuOptions = MenuOptions()
                LazyVGrid(columns: columns, spacing: 5) {
                    ForEach(menuOptions.menu) { item in
                        VStack {
                            Button(action: { self.destination = MenuDestination(rawValue: item.id) }) {
                                VStack{
                                    Image(item.imageName)
                                        .circularImageStyle(width: 30, height: 30, padding: 10,
                                                            backgroundColor: Color(red: 34 / 255, green: 34 / 255, blue: 34 / 255),
                                                            foregroundColor: Color(red: 23 / 255, green: 121 / 255, blue: 232 / 255))
                                }
                            }
                            .buttonStyle(PlainButtonStyle())
                            Text(item.name).font(.system(size: 10))
                        }.padding(10)
                    }
                }
            }.background(
                NavigationLink("", isActive: isActive, destination: { destination?.view }).buttonStyle(PlainButtonStyle()).navigationViewStyle(.stack)
        )
        }.navigationTitle("Sample Application")
    }
}

菜单目的地:

enum MenuDestination: Int, CaseIterable, Hashable {
    case option1 = 0, option2 = 1, option3 = 2, option4 = 3, option5 = 4, settings = 5

    @ViewBuilder var view: some View {
        switch self {
            case .option1: EmptyView()
            case .option2: MyCustomView()
            case .option3: EmptyView()
            case .option4: EmptyView()
            case .option5: EmptyView()
            case .settings: SettingsView() 
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM