SwiftUI Tooltip on hover

Viewed 34

SwiftUI provides the .help() modifier but it is too small, cannot be customised and takes too long to appear to actually serve its intended purpose. I would like to create a tooltip that appears immediately on hover and is larger, similar to the one that appears on hovering on an icon in the Dock.

Something like this: https://user-images.githubusercontent.com/64193267/189029830-847b329b-e053-49d6-9280-65100e4788e5.mov

Is this possible to create from SwiftUI itself? I've tried using a popover but it prevents hover events from propagating once its open, so I can't make it close when the mouse moves away.

1 Answers

Solution #1: Check for .onHover(...)

Use the .onHover(perform:) view modifier to toggle a @State property to keep track of whether your tooltip should be displayed:

@State var itemHovered: Bool = false

var body: some View {
  content
    .onHover { hover in
      itemHovered = hover
    }
    .overlay(
      Group {
        if itemHovered {
          Text("This is a tooltip")
            .background(Color.white)
            .foregroundColor(.black)
            .offset(y: -50.0)
        }
      }
    )
}

Solution #2: Make a Tooltip Wrapper View

Create a view wrapper that creates a tooltip view automatically:

struct TooltipWrapper<Content>: View where Content: View {
  @ViewBuilder var content: Content
  var hover: Binding<Bool>
  var text: String
  var body: some View {
    content
      .onHover { hover.wrappedValue = $0 }
      .overlay(
        Group {
          if hover.wrappedValue {
            Text("This is a tooltip")
              .background(Color.white)
              .foregroundColor(.black)
              .offset(y: -50.0)
          }
        }
      )
  }
}

Then you can call with

@State var hover: Bool = false

var body: some View {
  TooltipWrapper(hover: $hover, text: "This is a tooltip") {
    Image(systemName: "arrow.right")
    Text("Hover over me!")
  }
}

From this point, you can customize the hover tooltip wrapper to your liking.

Solution #3: Use my Swift Package

I wrote a Swift Package that makes SwiftUI a little easier for personal use, and it includes a tooltip view modifier that boils the solution down to:

import ShinySwiftUI

@State var showTooltip: Bool = false

var body: some View {
  MyView()
  .withTooltip(present: $showTooltip) {
    Text("This is a tooltip!")
  }
}

Notice you can provide your own custom views in the tooltip modifier above, like Image or VStack. Alternatively, you could use HoverView to get a stateful hover variable to use solely within your view:

HoverView { hover in
  Rectangle()
    .foregroundColor(hover ? .red : .blue)
    .overlay(
      Group {
        if hover { ... }
      }
    )
}
Related