This component usually shows when you connect to Airpods. That shows a message in a toast/balloon/pill shape at the top of the screen, with a small image (icon) and a title, it would dismiss in a few seconds, similar to the toast on the Android side.
Is this a built-in view control? Check my screenshot for more details.
Not built-in but easy to make
import SwiftUI
struct ToastParentView: View {
@State var showToast: Bool = false
var body: some View {
VStack{
Button(action: {
showToast.toggle()
}, label: {
Text("show toast")
})
List(){
ForEach(0...20, id: \.self, content: { idx in
Text("\(idx)")
})
}
}.toast(showToast: $showToast, position: .top, toastContent: {
VStack{
Text("Hello World!!")
Text("Headline").font(.headline)
}
})
}
}
extension View {
func toast<T:View>(showToast: Binding<Bool>, duration: TimeInterval = 5, position: ToastView<T>.ToastPosition = .top, @ViewBuilder toastContent: @escaping () -> T) -> some View {
modifier(ToastView(showToast: showToast, toastContent: toastContent(), duration: duration, position: position))
}
}
struct ToastView<T: View>: ViewModifier {
@Binding var showToast: Bool
let toastContent: T
@State var timer: Timer?
let duration: TimeInterval
let position: ToastPosition
func body(content: Content) -> some View {
GeometryReader{ geo in
ZStack{
content
if showToast{
RoundedRectangle(cornerRadius: 25)
.foregroundColor(Color(UIColor.systemBackground))
.shadow(radius: 10)
.overlay(toastContent
.minimumScaleFactor(0.2))
.frame(maxWidth: geo.size.width*0.6, maxHeight: 55 ,alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
.position(x: geo.size.width/2, y: getYPosition(height: geo.size.height, safeAreaInset: geo.safeAreaInsets))
.onAppear(){
timer = Timer.scheduledTimer(withTimeInterval: duration, repeats: false, block: {_ in
showToast = false
})
}
.onDisappear(){
print("onDisappear")
timer?.invalidate()
timer = nil
}
}
}.onTapGesture {
showToast = false
}
}
}
enum ToastPosition{
case top
case bottom
case middle
}
func getYPosition(height: CGFloat, safeAreaInset: EdgeInsets) -> CGFloat{
var result: CGFloat = 0
if position == .top{
result = 0 + safeAreaInset.top
}else if position == .bottom{
result = height - safeAreaInset.bottom
}else if position == .middle{
result = height/2
}
return result
}
}