Creating a Custom Tooltip Component in SwiftUI

M.Abbas
4 min readDec 25, 2024

--

Tooltips are powerful UI components that provide users with additional information or context about a feature or element. In iOS development, tooltips can enhance user experience by delivering concise, contextual data in an intuitive way. While SwiftUI doesn’t include a built-in Tooltip component, you can create a custom Tooltip with tailored styling and behavior.

In this blog, we’ll walk through the process of building a flexible Tooltip component in SwiftUI. The component will support different positions (top, bottom, left, right) and customizable content, making it suitable for various use cases.

Creating the Triangle Shape

A Tooltip often includes a small triangular pointer to indicate its target element. Let’s start by creating a reusable Triangle shape in SwiftUI:

struct Triangle: Shape {
public func path(in rect: CGRect) -> Path {
var path = Path()

let topMiddle = CGPoint(x: rect.midX, y: rect.minY)
let bottomLeft = CGPoint(x: rect.minX, y: rect.maxY)
let bottomRight = CGPoint(x: rect.maxX, y: rect.maxY)

path.move(to: bottomLeft)
path.addLine(to: bottomRight)

path.addArc(
center: CGPoint(x: topMiddle.x, y: topMiddle.y),
radius: 0,
startAngle: .degrees(0),
endAngle: .degrees(180),
clockwise: true
)

path.addLine(to: bottomLeft)

return path
}
}

This Triangle shape will serve as the arrow for the Tooltip. The path function defines the three vertices of the triangle and closes the shape.

Building the Tooltip Component

Next, let’s create the Tooltip component. This component will display a background container with text or icons, along with the triangle shape pointing to the specified direction:


struct Tooltip: View {

var items: [TooltipModel]
var type: TooltipDirection

public var body: some View {
ZStack(alignment: alignment()) {
HStack(spacing: 8) {
ForEach(items, id: \.id) { item in
ActivityItem(item: item)
}
}
.padding(8)
.background(Color.red.opacity(0.5))
.cornerRadius(8)

switch type {
case .top:
triangle()
.offset(y: -10)
case .left:
triangle()
.rotationEffect(.degrees(-90))
.offset(x: -15)
case .right:
triangle()
.rotationEffect(.degrees(90))
.offset(x: 15)
case .bottom:
triangle()
.rotationEffect(.degrees(180))
.offset(y: 10)
}
}
}

private func ActivityItem(item: TooltipModel) -> some View {
HStack(spacing: 2) {
if let icon = item.icon {
Image(icon)
.resizable()
.frame(width: 16, height: 16)
}

Text(item.title)
.font(.system(size: 14, weight: .semibold))
.lineLimit(1)
.foregroundStyle(.white)
}
}

private func triangle() -> some View {
Triangle()
.fill(Color.red.opacity(0.5))
.frame(width: 20, height: 10)
}

private func alignment() -> Alignment{
switch type {
case .top:
return .top
case .left:
return .leading
case .right:
return .trailing
case .bottom:
return .bottom
}
}
}

Key features of the Tooltip component:

  • Dynamic Alignment: The tooltip aligns dynamically based on the TooltipDirection (top, bottom, left, or right).
  • Customizable Content: It supports displaying a combination of icons and text.
  • Reusable Triangle: The triangle’s direction is adjusted using rotation transformations.

Defining the Tooltip Model

The TooltipModel struct represents the items displayed inside the Tooltip:

struct TooltipModel {
let id = UUID().uuidString
var icon: String? = nil
let title: String
}

Creating the Content View

Finally, let’s assemble everything in a ContentView. This example demonstrates multiple tooltips with various configurations:

struct ContentView: View {
var list: [TooltipModel] = [
TooltipModel(title: "Hello")
]

public var body: some View {
VStack(spacing: 50) {
HStack(spacing: 24) {
Tooltip(items: list, type: .bottom)
Tooltip(items: list, type: .top)
Tooltip(items: list, type: .left)
Tooltip(items: list, type: .right)
}
}
}
}

This ContentView illustrates:

  • Different Tooltip positions (top, bottom, left, right).
  • Variations in content, including icons and plain text.

After writing that code you will get the tooltip with the text which you have added into the TooltipModel list.

If you want to add text along side the icon then you will add the icon name and then that icon will show in the tooltip.


struct ContentView: View {

var list1: [TooltipModel] = [
TooltipModel(icon: "ic_Heart", title: "6"),
TooltipModel(icon: "ic_Chat", title: "31")
]

var list2: [TooltipModel] = [
TooltipModel(icon: "ic_Heart", title: "31")
]

var list3: [TooltipModel] = [
TooltipModel(icon: "ic_Chat", title: "31")
]

public var body: some View {
VStack(spacing: 50) {

HStack(spacing: 50) {
Tooltip(items: list1, type: .bottom)
Tooltip(items: list2, type: .bottom)
Tooltip(items: list3, type: .bottom)
}

HStack(spacing: 50) {
Tooltip(items: list1, type: .top)
Tooltip(items: list2, type: .top)
Tooltip(items: list3, type: .top)
}

HStack(spacing: 50) {
Tooltip( items: list1, type: .left)
Tooltip(items: list2, type: .left)
Tooltip(items: list3, type: .left)
}

HStack(spacing: 50) {
Tooltip(items: list1, type: .right)
Tooltip(items: list2, type: .right)
Tooltip(items: list3, type: .right)
}
}
}
}

After writing that code you will see the following result.

Conclusion

With a custom Tooltip component, you can enrich your SwiftUI applications by providing context-sensitive information elegantly. This implementation is flexible, reusable, and adheres to SwiftUI’s declarative style, making it easy to integrate into your projects. Try extending the Tooltip further by adding animations, additional styling options, or interactive features to create an even more engaging user experience.

You can also get the complete code from following link.
https://gist.github.com/muhammadabbas001/4dd58271b8407a24f146072e6e9cf090

--

--

M.Abbas
M.Abbas

No responses yet