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 需要在 ListVStackHStackScrollView 等内部 - 我想这可能是问题所在。 @Asperi macOS 10.15.2 (19C57); Xcode:版本 11.3 (11C29) @LuLuGaGa 不起作用:imgur.com/cC53b1m + imgur.com/c288NJe 并且问题仍然重现。 发现新信息和更新问题; 【参考方案1】:

您需要将ForEach 包裹在List 或relevant 中。

包含Text("...") 的任何视图都必须位于VStackHStackZStack 等或List 内。否则不会渲染。

当您创建 SwiftUI 视图时,协议定义我们需要返回“some View”。 “some”这个词意味着我们正在处理一个不透明的结果类型。这是一个新功能added in Swift 5.1。不透明的结果类型意味着我们必须返回一个,并且只有一个,指定类型,在这种情况下,符合“View”协议。

ForEachconforms转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 (SwiftUI) 的偶数行和奇数行

ForEach 通过多个 TextField 来验证 SwiftUI 中是不是为空

SwiftUI:如何使用 ForEach 循环使用各种代码的各种结构?

SwiftUI 截断 ForEach 的内容

在 SwiftUI 中,如何在 LinkedList 中“Foreach”节点?