SwiftUI:是否可以使用ForEach + ContextMenu + if语句? [英] SwiftUI: is it possible to use ForEach + ContextMenu + if statement?

查看:250
本文介绍了SwiftUI:是否可以使用ForEach + ContextMenu + if语句?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(仅在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("...")的视图都必须位于VStackHStackZStack等内部或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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆