工具提示视图 SwiftUI

问题描述 投票:0回答:1

我在 swiftui 中创建了工具提示,但是当我将它与覆盖层一起使用时 - 框架被破坏。

import SwiftUI

struct HintBox: View {
    @State private var showTooltip = true
    
    var body: some View {
        VStack {
            Text("Here text")
                    TooltipView(text: "it's hint! it's hint! it's hint! it's hint! it's hint! it's hint!", isVisible: $showTooltip)
                        .frame(maxWidth: .infinity)
                        .onTapGesture {
                            showTooltip.toggle()
                        }
        }
    }
}

struct TooltipView: View {
    var text: String
    @Binding var isVisible: Bool
    var body: some View {
        ZStack(alignment: .top) {
            if isVisible {
                Text(text)
                    .padding()
                    .background(Color.gray)
                    .foregroundColor(.white)
                    .cornerRadius(8)
                
                
                Triangle()
                    .fill(Color.gray)
                    .frame(width: 20, height: 10)
                    .offset(y: 0)
            }
        }
    }
}

struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: CGPoint(x: rect.midX - 10, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.midX, y: rect.minY - 10))
        path.addLine(to: CGPoint(x: rect.midX + 10, y: rect.minY))
        path.closeSubpath()
        return path
    }
}

我需要这样做,以便我可以准确确定三角形的位置(底部或顶部)、中间、左侧或右侧。而且,它必须高于其他观点。即用一个三角形指向我将附加它的视图。当您点击它时,它应该停止显示

swiftui tooltip hint
1个回答
0
投票

实现此目的的一种方法是将提示显示为源视图上的覆盖层。覆盖层会自动采用底层视图的大小,并使用

GeometryReader
您可以通过这种方式找到源视图的大小。

您希望提示能够突破源足迹的界限。这是通过使用

.fixedSize()
来完成的。但是,这意味着叠加层不再居中,因此您必须应用偏移量才能使其回到中心。

然后,为了将提示对齐到源视图的上方、下方、之前或之后,您需要知道提示本身的大小。在这里,您可以再次使用相同的技术,在提示的隐藏版本上设置覆盖层,并使用另一个

GeometryReader
来查找其大小。

这里尝试展示它的工作原理:

struct HintBox: View {
    @State private var showTooltip = true

    var body: some View {
        VStack {
            Text("Text here")
                .overlay {
                    TooltipView(
                        text: "it's hint! it's hint! it's hint! it's hint! it's hint!",
                        alignment: .top,
                        isVisible: $showTooltip
                    )
                }
                .onTapGesture {
                    showTooltip.toggle()
                }
        }
    }
}

struct TooltipView: View {
    var text: String
    let alignment: Edge
    @Binding var isVisible: Bool
    let arrowOffset = CGFloat(8)

    private var oppositeAlignment: Alignment {
        let result: Alignment
        switch alignment {
        case .top: result = .bottom
        case .bottom: result = .top
        case .leading: result = .trailing
        case .trailing: result = .leading
        }
        return result
    }

    private var theHint: some View {
        Text(text)
            .padding()
            .background(Color.gray)
            .foregroundColor(.white)
            .cornerRadius(8)
            .overlay(alignment: oppositeAlignment) {

                // The arrow is a square that is rotated by 45 degrees
                Rectangle()
                    .fill(Color.gray)
                    .frame(width: 22, height: 22)
                    .rotationEffect(.degrees(45))
                    .offset(x: alignment == .leading ? arrowOffset : 0)
                    .offset(x: alignment == .trailing ? -arrowOffset : 0)
                    .offset(y: alignment == .top ? arrowOffset : 0)
                    .offset(y: alignment == .bottom ? -arrowOffset : 0)
            }
            .padding()
            .fixedSize()
    }

    var body: some View {
        if isVisible {
            GeometryReader { proxy1 in

                // Use a hidden version of the hint to form the footprint
                theHint
                    .hidden()
                    .overlay {
                        GeometryReader { proxy2 in

                            // The visible version of the hint
                            theHint
                                .drawingGroup()
                                .shadow(radius: 4)

                                // Center the hint over the source view
                                .offset(
                                    x: -(proxy2.size.width / 2) + (proxy1.size.width / 2),
                                    y: -(proxy2.size.height / 2) + (proxy1.size.height / 2)
                                )
                                // Move the hint to the required edge
                                .offset(x: alignment == .leading ? (-proxy2.size.width / 2) - (proxy1.size.width / 2) : 0)
                                .offset(x: alignment == .trailing ? (proxy2.size.width / 2) + (proxy1.size.width / 2) : 0)
                                .offset(y: alignment == .top ? (-proxy2.size.height / 2) - (proxy1.size.height / 2) : 0)
                                .offset(y: alignment == .bottom ? (proxy2.size.height / 2) + (proxy1.size.height / 2) : 0)
                        }
                    }
            }
            .onTapGesture {
                isVisible.toggle()
            }
        }
    }
}

Hint

© www.soinside.com 2019 - 2024. All rights reserved.