SwiftUI:如何在悬停时显示工具提示/提示?

Posted

技术标签:

【中文标题】SwiftUI:如何在悬停时显示工具提示/提示?【英文标题】:SwiftUI : How do you display a tooltip / hint on hover? 【发布时间】:2019-12-01 19:22:09 【问题描述】:

如何在某些视图上显示工具提示/提示? 例如,在按钮上。

【问题讨论】:

该方法将与我在回答您关于acceptsFirstMouse的问题时所描述的相同 @Asperi 我相信我看到了本机修改器。但我不记得我在哪里看到的,谷歌什么也没告诉我。 @Asperi 这个问题包含 UI 平台的一个单独部分,因此最好将它作为一个单独的问题。如果/当 Apple 添加对两个独立问题中的任何一个的支持时,它可能不会是相同的解决方案,并且它们可能不会同时到达,因此将其分成两个问题有助于整个社区,尤其是作为新的答案突然出现 【参考方案1】:

SwiftUI 2.0

就这么简单

   Button("Action")  
     .help("Just do something")

   Button("Action")  
     .help(Text("Just do something"))

【讨论】:

可能在 SwiftUI 2.0 的发布版本中不起作用:***.com/questions/64148126/… 这会是最好的解决方案,但我只是尝试过,它没有效果。我希望将鼠标悬停在按钮上会显示文本,但事实并非如此。 我让它在运行部署目标 11.0 和 Xcode 12.3 的 macOS 应用程序中正常工作。 可以确认@NickN 提到这似乎在 macOS 上运行良好(目前在 Big Sur 11.5.2 上使用 Xcode 13 Beta 5)【参考方案2】:

2020 | SwiftUI 1 和 2 都

在 swiftUI 2 中:

Toggle("...", isOn: $isOn)
    .help("this is tooltip")

在 swiftUI 1 中确实没有创建工具提示的本地方法。但这里也有一个解决方案:

import Foundation
import SwiftUI

public extension View 
    /// Overlays this view with a view that provides a Help Tag.
    func toolTip(_ toolTip: String) -> some View 
        self.overlay(TooltipView(toolTip).allowsHitTesting(false))
    


private struct TooltipView: NSViewRepresentable 
    let toolTip: String

    init(_ toolTip: String?) 
        if let toolTip = toolTip 
            self.toolTip = toolTip
        
        else
        
            self.toolTip = ""
        
    
    
    func makeNSView(context: NSViewRepresentableContext<TooltipView>) -> NSView 
        NSView()
    

    func updateNSView(_ nsView: NSView, context: NSViewRepresentableContext<TooltipView>) 
        nsView.toolTip = self.toolTip
    

【讨论】:

这不适用于自定义 ButtonStyle,此处描述为 ***.com/questions/58419161/…【参考方案3】:

感谢Andrew 和Sorin 提供解决方案方向。提出的解决方案大部分都有效,但是当我使用它们时,它们完全弄乱了布局。事实证明,工具提示有自己的大小、框架等,不会自动匹配内容。

理论上我可以通过使用固定框架等来解决这些问题,但这对我来说似乎不是正确的方向。

我提出了以下(稍微复杂一点)但易于使用且没有这些缺点的解决方案。

extension View 
    func tooltip(_ tip: String) -> some View 
        background(GeometryReader  childGeometry in
            TooltipView(tip, geometry: childGeometry) 
                self
            
        )
    


private struct TooltipView<Content>: View where Content: View 
    let content: () -> Content
    let tip: String
    let geometry: GeometryProxy

    init(_ tip: String, geometry: GeometryProxy, @ViewBuilder content: @escaping () -> Content) 
        self.content = content
        self.tip = tip
        self.geometry = geometry
    

    var body: some View 
        Tooltip(tip, content: content)
            .frame(width: geometry.size.width, height: geometry.size.height)
    


private struct Tooltip<Content: View>: NSViewRepresentable 
    typealias NSViewType = NSHostingView<Content>

    init(_ text: String?, @ViewBuilder content: () -> Content) 
        self.text = text
        self.content = content()
    

    let text: String?
    let content: Content

    func makeNSView(context _: Context) -> NSHostingView<Content> 
        NSViewType(rootView: content)
    

    func updateNSView(_ nsView: NSHostingView<Content>, context _: Context) 
        nsView.rootView = content
        nsView.toolTip = text
    

我在工具提示的内容中添加了GeometryReader,然后将工具提示的大小限制为与内容的大小相匹配。

使用它:

Toggle("...", isOn: $isOn)
   .tooltip("This is my tip")

【讨论】:

【参考方案4】:

当叠加层不够好时,例如你想要一个接受鼠标事件的控件上的工具提示(并且覆盖不允许点击),例如 Toggle,一个解决方案可能是使用内部包含 NSHostingView 本身的主机视图 - 支持作为 AppKit 视图的工具提示- 最终在里面加载更多的 SwiftUI 内容:

struct Tooltip<Content: View>: NSViewRepresentable 
    typealias NSViewType = NSHostingView<Content>

    init(_ text: String?, @ViewBuilder content: () -> Content) 
        self.text = text
        self.content = content()
    

    let text: String?
    let content: Content

    func makeNSView(context: NSViewRepresentableContext<Tooltip<Content>>) -> NSViewType 
        NSViewType(rootView: content)
    
    func updateNSView(_ nsView: NSViewType, context: NSViewRepresentableContext<Tooltip<Content>>) 
        nsView.rootView = content
        nsView.toolTip = text
    

当与某些 SwiftUI 内容一起使用时,这确实有一些关于调整大小的警告(然后你可能希望使用 fixedSize() 或 frame(width:height:) 来让它按照你的需要工作),但它很容易使用:

Tooltip("A description")  
    Toggle("...", isOn: $isOn) 

【讨论】:

【参考方案5】:
struct TooltipText: View 
    @State private var isActive = false
    
    let text: String
    let helpText: String
    var body: some View 
        Text(isActive ? helpText : text)
            .padding( isActive ? 6 : 0)
            .background(Color.white)
            .overlay(
                RoundedRectangle(cornerRadius: 3)
                    .stroke(Color.blue, lineWidth: isActive  ? 1 : 0)
            )
            .animation(.easeOut(duration: 0.2) )
            .gesture(DragGesture(minimumDistance: 0)
                        .onChanged(  _ in isActive = true  )
                        .onEnded(  _ in isActive = false  )
            )
    


使用:

TooltipText(text: "sum of squares:", helpText: "sum of data (with mean subtracted) squared")

【讨论】:

谢谢你,欢迎@user3585825,你能解释一下吗。

以上是关于SwiftUI:如何在悬停时显示工具提示/提示?的主要内容,如果未能解决你的问题,请参考以下文章

extjs4 在按钮单击而不是鼠标悬停时显示工具提示

禁用按钮时在悬停时显示工具提示

使用innerHTML在悬停时显示图像图例(不是工具提示)?

当行悬停并且列值与另一行匹配时显示工具提示

有没有办法在 js 或 css 中悬停时显示一种不透明度低的工具提示?

悬停时不显示工具提示文本