SwiftUI:是否可以使用ForEach + ContextMenu + if语句? [英] SwiftUI: is it possible to use ForEach + ContextMenu + if statement?
问题描述
(仅在macOS上尝试过,但我相信iOS上也有相同的行为)
(tried on macOS only, but I believe there is the same behaviour on iOS)
macOS 10.15.2(19C57); Xcode:11.3(11C29)
macOS 10.15.2 (19C57); Xcode: 11.3 (11C29)
(由于我在问题中发现了新的关系,所以对问题进行了更新)
(question was updated because of I have found new relations in the issue)
我有以下代码:
List(model.filteredStatus) { status in
StatusViewRow(status: status, model: self.model)
.contextMenu{
Text("menu 1")
Text("menu 2")
}
}
效果很好.
但是如果我想改用ForEach(无论为什么):
But in case of I want to use ForEach instead ( no matter why):
ForEach(model.filteredStatus) { status in
StatusViewRow(status: status, model: self.model)
.contextMenu{
Text("menu 1")
Text("menu 2")
}
}
上下文菜单不出现.
我已找到原因.原因是ForEach
+ contextMenu
+ if
语句组合的错误工作!
I have found the reason. Reason was in incorrect work of ForEach
+ contextMenu
+ if
statement combination!
ContextMenu在if
语句内的行的一部分上无法完全正常工作.当我使用ForEach
时,它可以在List
的ful行上很好地工作,并且仅适用于toogle行的部分.
ContextMenu doesn't work exactly on part of row inside of the if
statement. Works well on the ful row with List
and works only on the toogle row part when I'm using ForEach
.
有人可以解释为什么吗?
Can somebody explain why so?
我在StatusViewRow
contextMenu内有一个if
语句,未在此部分显示:
I have an if
statement inside of StatusViewRow
contextMenu doesn't appear on this part:
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
}
}
}
推荐答案
您需要将ForEach
包装在List
或任何包含Text("...")
的视图都必须位于VStack
,HStack
,ZStack
等内部或List
中.否则不会渲染.
Any view that contains Text("...")
needs to be either inside a VStack
, HStack
, ZStack
, etc or a List
. It won't render otherwise.
创建SwiftUI视图时,协议定义我们需要返回"
some View
".单词"some
"表示我们正在处理一个不透明的结果类型.这是在Swift 5.1中添加的新功能.不透明的结果类型意味着我们必须返回一个且只能返回指定类型的一个,在这种情况下,该类型必须符合"View
"协议.
When you create a SwiftUI view, the protocol defines that we need to return "
some View
". The word "some
" means that we are dealing with an opaque result type. This is a new feature added in Swift 5.1. An opaque result type means that we must return one, and only one, of the specified type, in this case something that conforms to the "View
" protocol.
ForEach
符合至Hashable
:
struct ForEach<Data, ID, Content> where Data : RandomAccessCollection, ID : Hashable
...而堆栈(在这种情况下为VStack
)符合View
:
...whereas stacks (VStack
in this case) conforms to View
:
@frozen struct VStack<Content> where Content : View
...和List
也在符合到View
:
struct List<SelectionValue, Content> where SelectionValue : Hashable, Content : View
所以您需要这样的东西:
So you need something like this:
ForEach (model.filteredStatus) { status in
VStack { // stack View
StatusViewRow(status: status, model: self.model)
.contextMenu {
Text("Menu 1")
Text("Menu 2")
}
}
}
OR:
ForEach (model.filteredStatus) { status in
StatusViewRow(status: status, model: self.model)
.contextMenu {
VStack { // stack View
Text("Menu 1")
Text("Menu 2")
}
}
}
OR:
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")
}
}
}
本文很好地解释了为什么
这篇关于SwiftUI:是否可以使用ForEach + ContextMenu + if语句?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!