Swift嵌套视图内按钮误触问题
Swift嵌套视图内按钮误触问题

Swift嵌套视图内按钮误触问题

问题复现

在学习Swift过程中,发现一个难题,那就是当我尝试点击RatingView视图的星级时,存在整个按钮都被点击的状况。

首先是一个AddBookView视图,该视图中嵌套了一个RatingView(rating:)视图。

struct AddBookView: View {
    @State private var rating = 3
    var body: some View {
        ...
        Section("Write a review") {
            TextEditor(text: $review)
            RatingView(rating: $rating)
        }
    }
}

下面是RatingView视图代码:­­­

struct RatingView: View {
    @Binding var rating: Int
    var label = ""
    var maximumRating = 5
    var offImage: Image?
    var onImage = Image(systemName: "star.fill")
    var offColor = Color.gray
    var onColor = Color.yellow
    func image(for number: Int) -> Image {
        if number > rating {
            offImage ?? onImage
        } else {
            onImage
        }
    }
    var body: some View {
        HStack {
            if !label.isEmpty {
                Text(label)
            }
            ForEach(1..<maximumRating + 1, id: \.self) {
                number in
                Button {
                    print("点击了第\(number)个星星")
                    rating = number
                } label: {
                    image(for: number)
                        .foregroundStyle(number > rating ? offColor : onColor)
                }
            }
        }
    }
}

在AddBookView视图中点击RatingView的星级,会发现无论点击哪一个星星都是显示所有的星星。

时,Xcode输出了所有按钮,这也说明问题原因在于,某个代码导致所有的星星按钮都执行了一遍,所以显示的始终是五颗星。

点击了第1个星星
点击了第2个星星
点击了第3个星星
点击了第4个星星
点击了第5个星星

于是,在代码中添加了两个调试的按钮:

var body: some View {
    Button(action: {
        print("点击了整个View的按钮")
    }, label: {
        Text("View Button")
    })
    HStack {
        if !label.isEmpty {
            Text(label)
        }
        Button(action: {
            print("点击了整个HStack的按钮")
        }, label: {
            Text("HStack Button")
        })
        ForEach(1..<maximumRating + 1, id: \.self) {
            ...
        }
    }
}

如果添加的View按钮和HStack按钮都触发,说明点击外部视图时,存在问题。如果都不触发,说明ForEach存在问题。

重新执行命令后,点击按钮,发现触发的是HStack的按钮,外部的View按钮没有触发。这说明点击的按钮区域存在问题。

经过排查定位问题在于嵌套视图可能导致按钮误触。

解决方案

解决方案一

替换 Button 为 onTapGesture:将星星图标的 Button 替换为 onTapGesture,可以避免嵌套按钮的问题。这种方式只会触发单个星星的点击事件,不会影响外部按钮。

Button {
    print("点击了第\(number)个星星")
    rating = number
} label: {
    image(for: number)
        .foregroundStyle(number > rating ? offColor : onColor)
}

修改为:

ForEach(1..<maximumRating + 1, id: \.self) {
    number in
    image(for: number)
        .foregroundStyle(number > rating ? offColor : onColor)
        .onTapGesture {
            print("点击了第\(number)个星星")
            rating = number
        }
}

修改后,点击只会触发单个星星的效果,不会影响外部按钮。

解决方案二

HStack {
    ...
}
.buttonStyle(.plain)

给HStack添加.buttonStyle(.plain),这会禁用点击HStack时触发其内部的按钮。

修改后,SwiftUI就可以单独处理整个按钮。

以上就是全部的问题以及解决方案。

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注