gpt4 book ai didi

swift - 在 SwiftUI 中递归构建菜单

转载 作者:行者123 更新时间:2023-12-04 01:14:26 26 4
gpt4 key购买 nike

问题

我最近发现了 SwiftUI 的 OutlineGroup在 iOS 14 中(我使用的是 Xcode 12 beta 6)。这非常有效,无论是单独使用还是在使用 List 时,都可以计算“根据树结构的、已识别数据的基础集合按需查看和披露组。”

即如果您有递归定义的 struct,这对于构建 DisclosureGroup 元素非常有效。但是,我正在寻找可以让我构建“下拉”(或汉堡包)菜单的稍微不同的东西。

还有另一个控件叫做 Menu ,在 iOS 14 中,它完全按照我的意愿呈现“下拉”(或汉堡包)菜单:

enter image description here

但是我似乎无法同时使用两者来构建基于 data represented recursively 的动态 Menu ,例如:

struct Tree<Value: Hashable>: Hashable {
let value: Value
var children: [Tree]? = nil
}

菜单按以下方式构建:

struct SideMenu: View {    
var body: some View {
Menu {
Button(action: {}) {
Image(systemName: "person")
.foregroundColor(.gray)
.imageScale(.large)
Text("Profile")
.foregroundColor(.gray)
.font(.headline)
}
Button(action: {}) {
Image(systemName: "person.3")
.foregroundColor(.gray)
.imageScale(.large)
Text("Family Members")
.foregroundColor(.gray)
.font(.headline)
}
Button(action: {}) {
Image(systemName: "calendar")
.foregroundColor(.gray)
.imageScale(.large)
Text("Events")
.foregroundColor(.gray)
.font(.headline)
}
} label: {
Image(systemName: "line.horizontal.3")
}
}
}

问题

有没有一种方法可以从递归数据构建Menu,类似于OutlineGroup

最佳答案

我喜欢用枚举来表示树,以避免出现不可能或不一致的状态。此外,您需要一个递归的 UI 函数调用,但是使用方法会使编译器对我来说失败(Xcode 12 beta 6),所以我将菜单部分分开在不同的 View 中,这似乎可行。现在您有一个完全动态的菜单,可以从您的 ViewModel 构建。

import SwiftUI

enum ViewEvent {
case profileTapped
case familyMembersTapped
case eventsTapped
case foldersTapped
case deletedItemsTapped
}

struct MenuItem: Identifiable {
var id: String { return text }
let text: String
let systemImage: String?
let action: ViewEvent?
}

enum MenuContent: Identifiable {
var id: String {
switch self {
case let .item(item): return item.id
case let .submenu(text, _): return text
}
}

case item(MenuItem)
indirect case submenu(text: String, content: [MenuContent])
}

struct ViewState {
let menu: [MenuContent]
let content: String

static var `default`: ViewState {
.init(
menu: [
.item(MenuItem(text: "Profile", systemImage: "person", action: .profileTapped)),
.item(MenuItem(text: "Family Members", systemImage: "person.3", action: .familyMembersTapped)),
.item(MenuItem(text: "Events", systemImage: "calendar", action: .familyMembersTapped)),
.submenu(text: "More", content: [
.item(MenuItem(text: "Folders", systemImage: "folder.fill", action: .foldersTapped)),
.item(MenuItem(text: "Deleted", systemImage: "trash.fill", action: .deletedItemsTapped))
])
],
content: "Content")
}
}

struct ContentView: View {
@State var viewState: ViewState

var body: some View {
HStack(alignment: .top, spacing: 16) {
AppMenu(contents: viewState.menu) {
Image(systemName: "line.horizontal.3")
}

Text("Content")
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.padding()
}
}

struct AppMenuItem: View {
let item: MenuItem

func dispatch(_ action: ViewEvent) {
// todo: call viewModel.dispatch
print("Sending action \(action)")
}

init(item: MenuItem) {
self.item = item
}

var body: some View {
Button(action: {
item.action.map { action in dispatch(action) }
}) {
item.systemImage.map { systemImage in
Image(systemName: systemImage)
.foregroundColor(.gray)
.imageScale(.large)
}

Text(item.text)
.foregroundColor(.gray)
.font(.headline)
}
}
}

struct AppSubmenu: View {
let text: String
let contents: [MenuContent]

var body: some View {
AppMenu(contents: contents) {
HStack {
Text(text)
Image(systemName: "chevron.right")
}
}
}
}

struct AppMenu<Label: View>: View {
let label: () -> Label
let contents: [MenuContent]

init(contents: [MenuContent], @ViewBuilder label: @escaping () -> Label) {
self.contents = contents
self.label = label
}

var body: some View {
Menu {
ForEach(contents) { content in
// In case this is an item
if case let .item(item) = content {
AppMenuItem(item: item)
}

// In case this is a submenu
if case let .submenu(text, contents) = content {
AppSubmenu(text: text, contents: contents)
}
}
} label: { label() }
}
}

关于swift - 在 SwiftUI 中递归构建菜单,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63802388/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com