在SwiftUI中,在列表项内添加上下文菜单。

8

我已经创建了一个照片库,其中有一个包含多个单元格的列表,每个单元格中都有一些UIImages。我在图像上添加了上下文菜单,但是当我长按每个图像时,整个单元格都会被调用,而不是每个图像。请问有人能帮我解决如何将上下文菜单添加到列表中每个项目吗?

struct PhotoList : View {

var photoLibrary = PhotoLibrary

var body : some View {
    GeometryReader { geometry in
        List(self.photoLibrary, id: \.self) { imageSet in
            HStack (alignment: .center) {
                ForEach(imageSet, id: \.self) { image in
                    Image(uiImage: image)
                        .scaledToFill()
                        .cornerRadius(7)
                        .padding(3)
                        .frame(width: 150, height: 150, alignment: .center)
                        .contextMenu {
                            VStack {
                                Button(action: {}) {
                                    HStack {
                                        Text("Add to Today")
                                        Image("plus.circle")
                                    }
                                }
                            }
                    }

                }
            }
        }
    }
}

}


你解决了这个问题吗? - ngb
现在使用List不再可行,请改用ScrollView+LazyVStack。 - Asperi
是的,我不得不这样做。但它失去了所有自由列表功能 - 滑动删除、选择/移动行、搜索等。 - ngb
4个回答

0
这个问题在于列表视图将强制触摸事件注册到第一个子视图。例如,无法创建每行具有多个按钮的列表,如以下代码所示。
// Individual buttons can't be pressed
   List(someList, id: \.self) { _ in
        HStack {
             ForEach(1..<4) { k in
                Button("Button") {
                    //what ever
                }
            }
        }
    }

HStack将获取所有的触摸事件...

虽然可以通过使用scrollview和vstack的组合来解决问题。如果不需要滚动,只需删除那段代码即可。

var body : some View {
    GeometryReader() { geometry in
        ScrollView {
            VStack {
                List(self.photoLibrary, id: \.self) { imageSet in
                    HStack (alignment: .center) {
                        ForEach(imageSet, id: \.self) { image in             
                            Image(uiImage: image)
                                .resizable()
                                .scaledToFill()
                                .cornerRadius(7)
                                .padding(3)
                                .frame(width: 150, height: 150, alignment: .center)
                                .contextMenu {
                                    VStack {
                                        Button(action: {}) {
                                            HStack {
                                                Text("Add to Today")
                                                Image("plus.circle")
                                            }
                                        }
                                    }
                                }
                        }
                    }
                }
            }
        }.position(x: geometry.size.width/2, y: geometry.size.height/2)
    }
}

代码需要根据您的设计进行更正。同时将 .resizable 添加到图像中。希望这可以成为您代码的起点。


无法在图像上获得上下文菜单的解释是有道理的,但解决方案似乎不起作用 - 它只会提供一个空白视图。此外,在每一行中放置多个按钮也没有问题。 - ngb

0
我不确定你的 photoLibrary 结构是什么样子的,所以我创建了自己的结构。根据你的结构,这段代码需要稍作修改。
可以调用此 ContentView,并传递要在屏幕上显示的 photoLibrary 列表。
struct photoLibrary {
    var imageSet: [ImageItem]
    init(){
        self.imageSet = [ImageItem()]
    }
}
struct ImageItem{
    var image1: Image
    var image2: Image
    var image3: Image
    init(){
        self.image1 = Image(systemName: "xmark")
        self.image2 = Image(systemName: "xmark")
        self.image3 = Image(systemName: "xmark")
    }
}

struct ContentView: View {
    @State var photoLib: [photoLibrary]
    var body: some View {
        VStack {
            ScrollView{
                ForEach(self.photoLib.indices, id: \.self) { item in
                    VStack{
                        ForEach(photoLib[item].imageSet.indices, id: \.self) { image in
                            HStack{
                                ImageView(image: photoLib[item].imageSet[image].image1)
                                ImageView(image: photoLib[item].imageSet[image].image2)
                                ImageView(image: photoLib[item].imageSet[image].image3)
                            }
                        }
                    }
                }
            }
        }
    }
    
struct ImageView: View{
    @State var image: Image
    var body: some View{
        image
            .resizable()
            .scaledToFill()
            .cornerRadius(7)
            .padding(3)
            .frame(width: 150, height: 150, alignment: .center)
        Text("Add to Today").onTapGesture {
            //do something
        }
        Image(systemName: "plus.circle").onTapGesture {
            //do something
            }
        }
    }
}

以xmarks为图像的示例显示:

enter image description here


1
我真的在寻找一种解决方案,可以让我保留List及其提供的所有免费功能。 - ngb

0
使用 ForEach 替换你的 List,并将它包裹在一个 VStack 或者 LazyVStack 中,就可以了。如果需要滚动,请将它包裹在 ScrollView 中。 List 让它像表格行一样表现。
所以,不要使用:
List(self.photoLibrary, id: \.self) { imageSet in
    // ...
}

做这个:
ScrollView { // If scrolling is needed
    VStack {
        ForEach(self.photoLibrary, id: \.self) { imageSet in
            // ...
        }
    }
}

基本上就像NoosaMaan更详细地解释的那样:

"[...] 列表视图将强制触摸事件被注册到第一个子视图 [...]"


2
这是一个好的、清晰而简洁的答案。我希望能够使用List并保留它所带来的所有免费功能,但似乎这是不可能的。 - ngb
你缺少哪些功能? - calimarkus
2
滑动删除、多选删除、移动、搜索等列表功能。 - ngb
我明白了。滑动删除、多项删除和移动确实非常适用于“行”这个特定对象。但对于多项行,这样做就没有太多意义了,是吧?顺便说一下,有些不错的 SwiftUI 集合视图包装器可以使用,或许它们可以更好地满足你的需求。 - calimarkus
@calimarkus 或许这是唯一一个不需要内置功能的行。不幸的是,SwiftUI 对于诸如“下拉刷新”之类的很多功能都限制在了List中,因此围绕它构建整个 UI 是有意义的。 - atultw
1
在一个ScrollView中拖动时,开始拖动时会出现很多卡顿,非常不可用。很遗憾。 - Fabian

-4

据我所知,上下文菜单必须至少有两个按钮,但我认为以下方式已经足够:

Image(uiImage: image)
.scaledToFill()
.cornerRadius(7)
.padding(3)
.frame(width: 150, height: 150, alignment: .center)
.contextMenu {
    Button(action: {}) {
        Text("Add to Today")
        Image("plus.circle")
    }
    Button(action: {}) {
        Text("Some Other Button")
        Image("globe")
    }
}

这不起作用。它仍然将整行视为一个整体。 - ngb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接