SwiftUI:是否可以使用ForEach + ContextMenu + if语句?

时间:2019-12-26 08:49:07

标签: ios macos swiftui

(仅在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时,在ForEach的ful行上效果很好,并且仅在wegle行的部分上效果很好。

有人可以解释为什么吗?

我在if的contextMenu中有一条StatusViewRow语句未在此部分显示:

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
        }
    }
}

1 个答案:

答案 0 :(得分:0)

您需要将ForEach包装在Listrelevant中。

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

  

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

ForEach conformsHashable

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

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

@frozen struct VStack<Content> where Content : View

...以及ListconformsView

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")

            }
    }
}

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")
            }

    }
}

本文很好地解释了why的必要性。