SwiftUI-带有图像的按钮可在外部单击

时间:2020-04-04 00:06:58

标签: ios swift swiftui

我有一个带有多个Button的ScrollView。一个按钮在下面包含一个图像和一个文本。

由于图像很大,因此我使用.scaledToFill和.clipped。而且,即使未显示,图片的“剪切”部分仍然可以点击。

在视频中,您看到我单击了按钮1,但是按钮2被触发。

Gif showing the error

这是我的编码。图片位于视图卡片中。

struct ContentView: View {

    @State var useWebImage = false
    @State var isSheetShowing = false
    @State var selectedIndex = 0

    private let images = [
        "https://images.unsplash.com/photo-1478368499690-1316c519df07?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2706&q=80",
        "https://images.unsplash.com/photo-1507154258-c81e5cca5931?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2600&q=80",
        "https://images.unsplash.com/photo-1513310719763-d43889d6fc95?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2734&q=80",
        "https://images.unsplash.com/photo-1585766765962-28aa4c7d719c?ixlib=rb-1.2.1&auto=format&fit=crop&w=2734&q=80",
        "https://images.unsplash.com/photo-1485970671356-ff9156bd4a98?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2734&q=80",
        "https://images.unsplash.com/photo-1585607666104-4d5b201d6d8c?ixlib=rb-1.2.1&auto=format&fit=crop&w=2700&q=80",
        "https://images.unsplash.com/photo-1577702066866-6c8897d06443?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2177&q=80",
        "https://images.unsplash.com/photo-1513809491260-0e192158ae44?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2736&q=80",
        "https://images.unsplash.com/photo-1582092723055-ad941d1db0d4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2700&q=80",
        "https://images.unsplash.com/photo-1478264635837-66efba4b74ba?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjF9&auto=format&fit=crop&w=2682&q=80"
    ]

    var body: some View {
        NavigationView {
            ScrollView {
                VStack(spacing: 40) {

                    Text(useWebImage ? "WebImage is used." : "SwiftUI Image is used")
                        .font(.system(size: 18))
                        .bold()
                        .kerning(0.5)
                        .padding(.top, 20)

                    Toggle(isOn: $useWebImage) {
                        Text("Use WebImage")
                            .font(.system(size: 18))
                            .bold()
                            .kerning(0.5)
                            .padding(.top, 20)
                    }

                    ForEach(0..<images.count) { index in
                        Button(action: {
                            self.selectedIndex = index
                            self.isSheetShowing.toggle()
                        }) {
                            Card(imageUrl: self.images[index], index: index, useWebImage: self.$useWebImage)
                        }
                        .buttonStyle(PlainButtonStyle())
                    }
                }
                .padding(.horizontal, 20)
                .sheet(isPresented: self.$isSheetShowing) {
                    DestinationView(imageUrl: self.images[self.selectedIndex], index: self.selectedIndex, useWebImage: self.$useWebImage)
                }
            }
            .navigationBarTitle("Images")
        }
    }
}
struct Card: View {

    let imageUrl: String
    let index: Int
    @Binding var useWebImage: Bool

    var body: some View {
        VStack {
            if useWebImage {
                WebImage(url: URL(string: imageUrl))
                    .resizable()
                    .indicator(.activity)
                    .animation(.easeInOut(duration: 0.5))
                    .transition(.fade)
                    .scaledToFill()
                    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
                    .cornerRadius(12)
                    .clipped()
            } else {
                Image("image\(index)")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
                    .cornerRadius(12)
                    .clipped()
            }

            HStack {
                Text("Image #\(index + 1) (\(useWebImage ? "WebImage" : "SwiftUI Image"))")
                    .font(.system(size: 18))
                    .bold()
                    .kerning(0.5)

                Spacer()
            }
        }
        .padding(2)
        .border(Color(.systemRed), width: 2)
    }
}

您是否有解决此问题的想法? 我已经尝试过使用.resizable(resizingMode:.tile),但是在仅使用图块之前,需要缩小图像。

有关详细信息,您还可以在GitHub GitHub Project

上找到该项目。

非常感谢您的帮助。

1 个答案:

答案 0 :(得分:7)

.clipped仅影响图形,默认情况下,Button的所有内容都可单击,而不取决于其内容。

因此,如果要使按钮仅在图像区域内可单击,则必须将命中测试仅限制在其矩形区域内,并禁用其他所有功能。

这里是可能方法的演示。在Xcode 11.4 / iOS 13.4上进行了测试。

enter image description here

演示代码(快照的简化版本):

struct ButtonCard: View {

    var body: some View {
        VStack {
            Image("sea")
                .resizable()
                .aspectRatio(contentMode: .fill)
                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 250, maxHeight: 250, alignment: .center)
                .cornerRadius(12)
                .contentShape(Rectangle())    // << define clickable rect !!
                .clipped()

            HStack {
                Text("Image #1")
                    .font(.system(size: 18))
                    .bold()
                    .kerning(0.5)

                Spacer()
            }.allowsHitTesting(false)         // << disable label area !!
        }
        .padding(2)
        .border(Color(.systemRed), width: 2)
    }
}

struct TestClippedButton: View {
    var body: some View {
        Button(action: { print(">> tapped") }) {
            ButtonCard()
        }.buttonStyle(PlainButtonStyle())
    }
}