SwiftUI:是不是可以使用 ForEach + ContextMenu + if 语句?
Posted
技术标签:
【中文标题】SwiftUI:是不是可以使用 ForEach + ContextMenu + if 语句?【英文标题】:SwiftUI: is it possible to use ForEach + ContextMenu + if statement?SwiftUI:是否可以使用 ForEach + ContextMenu + if 语句? 【发布时间】:2019-12-26 08:49:07 【问题描述】:(仅在 macOS 上尝试过,但我相信在 ios 上会有相同的行为)
macOS 10.15.2 (19C57); Xcode:11.3 (11C29)
(问题已更新,因为我在问题中发现了新的关系)
我有以下代码:
List(model.filteredStatus) status in
StatusViewRow(status: status, model: self.model)
.contextMenu
Text("menu 1")
Text("menu 2")
效果很好。
但如果我想使用 ForEach 代替(不管为什么):
ForEach(model.filteredStatus) status in
StatusViewRow(status: status, model: self.model)
.contextMenu
Text("menu 1")
Text("menu 2")
上下文菜单不出现。
我找到了原因。原因是ForEach
+ contextMenu
+ if
语句组合不正确!
ContextMenu 不能完全在 if
语句内的部分行上工作。在 List
的 ful 行上运行良好,并且在我使用 ForEach
时仅在工具行部分上运行。
有人可以解释为什么会这样吗?
我在StatusViewRow
中有一个if
语句,contextMenu 没有出现在这部分:
struct StatusViewRow : View
@ObservedObject var status : FileStatusViewModel
@ObservedObject var model : StatusViewModel
var body: some View
HStack
TaoToggle(state: status.stageState) self.status.toggleStageState()
// you can replace to just a toogle
// ISSUE START HERE
if(model.fileNamesOnly)
StyledText(verbatim: status.fileName )
.style(.highlight(), ranges: [$0.lowercased().range(of: model.filterStr.lowercased() )])
else
StyledText(verbatim: status.path )
.style(.foregroundColor(.gray), ranges: [$0.range(of: status.fileDir) ] )
.style(.bold(), ranges: [$0.range(of: status.fileName) ] )
.style(.highlight(), ranges: [$0.lowercased().range(of: model.filterStr.lowercased() )])
// ISSUE END HERE
【问题讨论】:
适用于 iOS 13.2。适用于 macOS 10.15.2。使用 Xcode 11.2.1 测试。也许你有 Xcode 问题?ForEach
需要在 List
、VStack
、HStack
、ScrollView
等内部 - 我想这可能是问题所在。
@Asperi macOS 10.15.2 (19C57); Xcode:版本 11.3 (11C29)
@LuLuGaGa 不起作用:imgur.com/cC53b1m + imgur.com/c288NJe 并且问题仍然重现。
发现新信息和更新问题;
【参考方案1】:
您需要将ForEach
包裹在List
或relevant 中。
包含Text("...")
的任何视图都必须位于VStack
、HStack
、ZStack
等或List
内。否则不会渲染。
当您创建 SwiftUI 视图时,协议定义我们需要返回“
some View
”。 “some
”这个词意味着我们正在处理一个不透明的结果类型。这是一个新功能added in Swift 5.1。不透明的结果类型意味着我们必须返回一个,并且只有一个,指定类型,在这种情况下,符合“View
”协议。
ForEach
conforms转Hashable
:
struct ForEach<Data, ID, Content> where Data : RandomAccessCollection, ID : Hashable
...而stacks(在这种情况下为VStack
)符合View
:
@frozen struct VStack<Content> where Content : View
...和List
以及conforms 到View
:
struct List<SelectionValue, Content> where SelectionValue : Hashable, Content : View
所以你需要这样的东西:
ForEach (model.filteredStatus) status in
VStack // stack View
StatusViewRow(status: status, model: self.model)
.contextMenu
Text("Menu 1")
Text("Menu 2")
或者:
ForEach (model.filteredStatus) status in
StatusViewRow(status: status, model: self.model)
.contextMenu
VStack // stack View
Text("Menu 1")
Text("Menu 2")
或者:
List // List works because it conforms to View
ForEach (model.filteredStatus) status in
StatusViewRow(status: status, model: self.model)
.contextMenu
Text("Menu 1")
Text("Menu 2")
这篇文章很好地解释了why这是必要的。
【讨论】:
将.contextMenu
内容包装到 VStack
(您的答案中的示例 2)不能解决问题。我已经用原始代码更新了我的问题,也许你会感兴趣....但是简化代码的行为是一样的。
将 ForEach 的内容包装到 HStack 中也没有解决问题。将 ForEach 包装到 VStack 中也没有解决问题......同时所有 3 个操作也没有解决问题:imgur.com/cC53b1m 和 imgur.com/c288NJe ;使用 List 对我来说不是解决方案,我正在尝试不使用它。
我找到了原因,但无法解决问题。更新了问题。
但是,请不要删除已经存在的答案。如果您想更改答案,只需添加某项内容,但不要删除已写入的那些信息 - 可能对其他人有用。谢谢。以上是关于SwiftUI:是不是可以使用 ForEach + ContextMenu + if 语句?的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI 的 List/ForEach 视图中是不是可以有一个 Button(action: -)
ForEach 通过多个 TextField 来验证 SwiftUI 中是不是为空