Swiftui-打开新的视图按钮单击

发布于 2025-02-09 13:22:27 字数 4159 浏览 2 评论 0原文

noob在watchos和swiftui中。我创建了一个带有多个按钮的网格视图。每当单击一个按钮时,我都希望打开带有导航链接的新视图。由于视图上有多个按钮,因此我创建了一个可重复使用的视图,并且很难实现导航到下一个视图。以下是我的代码:

内容视图:

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

mainmenucularGridView:

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")
        }
    }
}

menuitemcirculargridview:

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)
    }
}

循环buttonwithimage:

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())
    }
}

这是我的应用的外观:

/hicyo.png“ rel =” nofollow noreferrer “ 这些按钮,我想打开带有导航链接的新视图。如下所示:

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()) })
    ]
}

Noob in watchOS and SwiftUI. I have created a grid view with multiple buttons on it. Whenever a button is clicked, I wish to open a new view with navigation link. Since there are multiple buttons on the view, I have created a reusable view and having a hard time to implement navigation to next view. Below is my code:

Content View:

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")
        }
    }
}

MenuItemCircularGridView:

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())
    }
}

This is kind of how my app looks:

enter image description here

Whenever I click on any of those buttons, I want to open a new view with navigation link. Something like below:

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

Since I have broken the view down into multiple reusable files, I am not sure where exactly should I put this logic to open a new view on button click.

Edit: Adding the hardcoded data that I am using. I was trying to pass navigation link as action to the button.

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()) })
    ]
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

成熟的代价 2025-02-16 13:22:27

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 in相应menudestination

另请参见此帖子

A NavigationLink must be in view hierarchy, so instead of putting it in action we need to put some model there.

A sketch of possible approach

  1. destination model
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. navigation link in view
    @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. button action assigns corresponding value, say MenuItemAction take as argument binding to selection and internally assign destination to that binding
MenuItemCircularGridView(imageName: item.imageName, menuItemName: item.name, 
   action: (menuOptionsAction.menuActions.first {$0.id == item.id})?.action($selection) ?? { _ in })

and MenuItemAction inited with case of corresponding MenuDestination

See also this post

就此别过 2025-02-16 13:22:27

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

但是,我要使用NavigationLink的主要原因是在目标视图上获取“后退”按钮和导航标题名称以返回主菜单。我看不到目标视图左上方的带有导航标题的后退按钮。如果您对我可能缺少什么有了解,请告诉我。我的理解是使用NavigationLink自动将其添加到目标视图中。

更新的代码-MainmenucularGridViewNew:

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() 
        }
    }
}

I realized me breaking down the views into different files was probably making things more complicated. Since I just have 6 buttons, I decided to keep the view into a single file and used your idea of creating MenuDestination for passing down the right view. Sharing my updated code below.

However the main reason I wanted to use NavigationLink is to get the back button button and navigation title name on the destination view to get back to the main menu. I am not seeing that back button with navigation title on top left of the destination view. Please do let me know if you have an idea of what I could be missing. My understanding is using NavigationLink automatically adds that to the destination view.

Updated code - 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")
    }
}

Menu Destination:

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() 
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文