Search code examples
tooltipswiftui

How do you display a tooltip / hint on hover?


How to display tooltip / hint on some view? As example, on the button.

enter image description here


Solution

  • Thanks to both Andrew and Sorin for the solution direction. The presented solutions mostly worked but when I used them they totally messed up the layout. It turns out that the Tooltip has its own size, frame etc. which isn't automatically matching the content.

    In theory I could address those problems by using fixed frames etc. but that did not seem the right direction to me.

    I have come up with the following (slightly more complex) but easy to use solution which doesn't have these drawbacks.

    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
        }
    }
    

    I have added a GeometryReader to the content of the tooltip and then constrain the size of the Tooltip to the match the size of the content.

    To use it:

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

    Addition January 2023

    The latest release of SwiftUI requires a small change in the tooltip method. I noticed rapid redrawing problems. By adding a ZStack this problem is addressed:

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

    Feb 2023: I removed the second self. With the extra self there is a glow on the parent.