Довольно просто - и интересно - построить «тост» в SwiftUI!
Давайте сделаем это!
struct Toast<Presenting>: View where Presenting: View {
/// The binding that decides the appropriate drawing in the body.
@Binding var isShowing: Bool
/// The view that will be "presenting" this toast
let presenting: () -> Presenting
/// The text to show
let text: Text
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .center) {
self.presenting()
.blur(radius: self.isShowing ? 1 : 0)
VStack {
self.text
}
.frame(width: geometry.size.width / 2,
height: geometry.size.height / 5)
.background(Color.secondary.colorInvert())
.foregroundColor(Color.primary)
.cornerRadius(20)
.transition(.slide)
.opacity(self.isShowing ? 1 : 0)
}
}
}
}
Объяснение тела:
GeometryReader
дает нам предпочтительный размер суперпредставления, позволяя таким образом идеально подобрать размеры для наших Toast
. ZStack
стеков друг над другом. - Логикатривиально: если тост не предполагается увидеть (
isShowing == false
), то мы визуализируем представление presenting
.Если необходимо представить тост (isShowing == true
), то мы визуализируем представление presenting
с небольшим размытием - потому что мы можем - и мы создаем наш тост следующим образом. - Тост - это просто
VStack
с Text
, с пользовательским размером рамки, некоторыми дизайнерскими колокольчиками (цветами и радиусом угла) и переходом по умолчанию slide
.
Я добавил этот метод на View
чтобы упростить создание Toast
:
extension View {
func toast(isShowing: Binding<Bool>, text: Text) -> some View {
Toast(isShowing: isShowing,
presenting: { self },
text: text)
}
}
И небольшая демонстрация того, как его использовать:
struct ContentView: View {
@State var showToast: Bool = false
var body: some View {
NavigationView {
List(0..<100) { item in
Text("\(item)")
}
.navigationBarTitle(Text("A List"), displayMode: .large)
.navigationBarItems(trailing: Button(action: {
withAnimation {
self.showToast.toggle()
}
}){
Text("Toggle toast")
})
}
.toast(isShowing: $showToast, text: Text("Hello toast!"))
}
}
Я использовал NavigationView
, чтобы убедиться, что вид заполняетсявесь экран, поэтому Toast
имеет размеры и правильное расположение.
Блок withAnimation
обеспечивает применение перехода Toast
.
Как это выглядит:
Расширение Toast
с помощью DSL SwiftUI легко.
Text
свойство может легко стать закрытием @ViewBuilder
для размещения наиболее экстравагантных макетов.
Чтобы добавить его в представление содержимого :
struct ContentView : View {
@State private var liked: Bool = false
var body: some View {
VStack {
LikeButton(liked: $liked)
}
// make it bigger by using "frame" or wrapping it in "NavigationView"
.toast(isShowing: $liked, text: Text("Hello toast!"))
}
}
Как скрыть тост после 2 секунд (по запросу) :
Добавить этот код после .transition(.slide)
в тосте VStack
.
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation {
self.isShowing = false
}
}
}