如何在SwiftUI中为单个元素应用.onHover

4

我想在鼠标悬停时对个别项目进行动画处理。问题是当我悬停在一个项目上时,每个项目都会被动画处理,而不仅仅是该特定的项目。 这是我的代码:

struct ContentView : View {
    @State var hovered = false
    var body: some View {
        VStack(spacing: 90) {
            ForEach(0..<2) {_ in
                HStack(spacing: 90) {
                    ForEach(0..<4) {_ in
                        Circle().fill(Color.red).frame(width: 50, height: 50)
                            .scaleEffect(self.hovered ? 2.0 : 1.0)
                        .animation(.default)
                        .onHover { hover in
                                print("Mouse hover: \(hover)")
                            self.hovered.toggle()
                        }
                    }
                }
            }
        }
        .frame(minWidth:300,maxWidth:.infinity,minHeight:300,maxHeight:.infinity)
    }
}




drawing

3个回答

8

需要基于每个视图更改Hover视图,即存储悬停视图的某些标识符。

这里是可能的解决方案。Xcode 11.4测试通过。

演示

struct TestOnHoverInList : View {
    @State var hovered: (Int, Int) = (-1, -1)
    var body: some View {
        VStack(spacing: 90) {
            ForEach(0..<2) {i in
                HStack(spacing: 90) {
                    ForEach(0..<4) {j in
                        Circle().fill(Color.red).frame(width: 50, height: 50)
                        .scaleEffect(self.hovered == (i,j) ? 2.0 : 1.0)
                        .animation(.default)
                        .onHover { hover in
                            print("Mouse hover: \(hover)")
                            if hover {
                                self.hovered = (i, j)    // << here !!
                            } else {
                                self.hovered = (-1, -1)  // reset
                            }
                        }
                    }
                }
            }
        }
        .frame(minWidth:300,maxWidth:.infinity,minHeight:300,maxHeight:.infinity)
    }
}

我怎样可以在 LazyVgrid 上使用这个? - lorenzo gonzalez

5

目前每个项目都会得到动画效果,因为它们都依赖于 hovered 来查看是否悬停在 Circle 上。为了解决这个问题,我们可以让每个圆形都有自己的 hovered 状态。

struct CircleView: View {
    @State var hovered = false

    var body: some View {
        Circle().fill(Color.red).frame(width: 50, height: 50)
            .scaleEffect(self.hovered ? 2.0 : 1.0)
        .animation(.default)
        .onHover { hover in
                print("Mouse hover: \(hover)")
            self.hovered.toggle()
        }
    }
}

ForEach 中,我们可以直接调用新的 CircleView,其中每个 Circle 都有自己的真实数据来源。

struct ContentView : View {
    var body: some View {
        VStack(spacing: 90) {
            ForEach(0..<2) { _ in
                HStack(spacing: 90) {
                    ForEach(0..<4) { _ in
                        CircleView()
                    }
                }
            }
        }
        .frame(minWidth:300,maxWidth:.infinity,minHeight:300,maxHeight:.infinity)
    }
}

使用这种方法,您将不得不再次悬停以使其缩小,它不会在悬停结束时自动缩小... - lorenzo gonzalez

0

或者,您可以创建一个修饰符,使您能够在悬停时更改相关的视图:

extension View {
    func onHover<Content: View>(@ViewBuilder _ modify: @escaping (Self) -> Content) -> some View {
        modifier(HoverModifier { modify(self) })
    }
}

private struct HoverModifier<Result: View>: ViewModifier {
    @ViewBuilder let modifier: () -> Result
    @State private var isHovering = false

    func body(content: Content) -> AnyView {
        (isHovering ? modifier().eraseToAnyView() : content.eraseToAnyView())
            .onHover { self.isHovering = $0 }
            .eraseToAnyView()
    }
}

那么,你的例子中每个圆圈都会变成这样:

Circle().fill(Color.red).frame(width: 50, height: 50)
    .animation(.default)
    .onHover { view in
        _ = print("Mouse hover")
        view.scaleEffect(2.0)
    }


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