【问题标题】: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 并且问题仍然重现。
  • 发现新信息和更新问题;

标签: ios macos swiftui


【解决方案1】:

您需要将ForEach 包裹在Listrelevant 中。

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

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

ForEachconformsHashable

struct ForEach<Data, ID, Content> where Data : RandomAccessCollection, ID : Hashable

...而stacks(在这种情况下为VStack)符合View

@frozen struct VStack<Content> where Content : View

...和List 以及conformsView

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/cC53b1mimgur.com/c288NJe ;使用 List 对我来说不是解决方案,我正在尝试不使用它。
  • 我找到了原因,但无法解决问题。更新了问题。
  • 但是,请不要删除已经存在的答案。如果您想更改答案,只需添加某项内容,但不要删除已写入的那些信息 - 可能对其他人有用。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-10
  • 1970-01-01
  • 2013-07-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-25
  • 2013-05-17
相关资源
最近更新 更多