TipKit is a fantastic new framework that simplifies the process of teaching users about new features in your app, or guiding them to hidden gems.
In this post, I’ll show you the basics and follow with more advanced options in future posts.
You need to follow these steps:
Create a new structure that conforms to Tip. This defines the content of your tip.
struct OnboardingTip: Tip {
var title: Text {
Text("New Note")
}
var message: Text? {
Text("Tap here to create a new note")
}
var image: Image? {
Image(systemName: "info.circle.fill")
}
}
Load all tips using Tips.configure(). This must happen before you’re able to show any tips.
.task {
do {
try Tips.configure()
} catch {
print("TipKit error: \(error.localizedDescription)")
}
}
Initialize an instance of your tip.
let addNoteTip = OnboardingTip()
Display your tip. In our example we use popoverTip, which attaches our tip to a specific view.
Button {
addNoteTip.invalidate(reason: .actionPerformed)
} label: {
Image(systemName: "note.text.badge.plus")
.font(.system(size: 80))
}
.popoverTip(addNoteTip, arrowEdge: .bottom)
Not that we call invalidate on our tip, which informs the system to dismiss the tip, and in this case for the reason .actionPerformed (i.e. the user tapped the target button)
🚨 Find out more about TipKit in Apple’s Highlighting App features with TipKit documentation.
🚨 Full code on Github
🤝 Find more about me on saidmarouf.com. I’m also on X, Threads, and LinkedIn
import SwiftUI
struct ContentView: View {
@State var completed: Double = 0.0
private var progress: Double {
return completed / 1.0
}
var body: some View {
VStack {
CircleProgressView(progress: progress, lineWidth: 16)
.frame(width: 200)
.padding()
CircleProgressView(progress: progress, lineWidth: 12)
.frame(width: 100)
.padding()
CircleProgressView(progress: progress, lineWidth: 8)
.frame(width: 40)
.padding()
CircleProgressView(progress: progress, lineWidth: 3)
.frame(width: 40)
.padding()
Spacer()
Button {
completed += 0.1
} label: {
Text("Update Progress")
}
.buttonStyle(.borderedProminent)
}
.padding(.vertical, 40)
}
}
struct CircleProgressView: View {
var progress: Double
var lineWidth: CGFloat
var body: some View {
ZStack {
Circle().stroke(
.secondary.opacity(0.4),
lineWidth: lineWidth
)
Circle()
.trim(from: 0, to: min(progress, 1.0))
.stroke(
.green.gradient,
style: StrokeStyle(
lineWidth: lineWidth,
lineCap: .round
)
)
.rotationEffect(.degrees(-90))
.animation(.easeOut(duration: 0.25), value: progress)
}
}
}
#Preview {
ContentView()
}
Share this post