swift - SwiftUI:可以从任何视图触发的全局叠加层
问题描述
我对 SwiftUI 框架还很陌生,还没有完全理解它,所以请多多包涵。
当绑定发生变化时,有没有办法从“另一个视图”内部触发“覆盖视图”?见下图:
我认为这个“叠加视图”会包含我所有的视图。我还不确定如何做到这一点 - 也许使用ZIndex
. 我还想当绑定发生变化时我需要某种回调,但我也不知道该怎么做。
这是我到目前为止所得到的:
内容视图
struct ContentView : View {
@State private var liked: Bool = false
var body: some View {
VStack {
LikeButton(liked: $liked)
}
}
}
喜欢按钮
struct LikeButton : View {
@Binding var liked: Bool
var body: some View {
Button(action: { self.toggleLiked() }) {
Image(systemName: liked ? "heart" : "heart.fill")
}
}
private func toggleLiked() {
self.liked = !self.liked
// NEED SOME SORT OF TOAST CALLBACK HERE
}
}
我觉得我需要在我的内部进行某种回调LikeButton
,但我不确定这一切在 Swift 中是如何工作的。
对此的任何帮助将不胜感激。提前致谢!
解决方案
在 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
为我们提供了 superview 的首选大小,从而为我们的Toast
.ZStack
将视图堆叠在一起。- 逻辑很简单:如果 toast 不应该被看到(
isShowing == false
),那么我们渲染presenting
视图。如果必须呈现 toast ( ),那么我们用一点点模糊来isShowing == true
渲染视图 - 因为我们可以 - 然后我们创建我们的 toast。presenting
- toast 只是一个
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!"))
}
}
我使用 aNavigationView
来确保视图填满整个屏幕,以便Toast
正确调整大小和位置。
该withAnimation
块确保Toast
应用过渡。
它看起来如何:
Toast
使用 SwiftUI DSL 的强大功能很容易扩展。
该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)
在 toast之后附加此代码VStack
。
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation {
self.isShowing = false
}
}
}
在 Xcode 11.1 上测试
推荐阅读
- c# - TryParse 不工作,如果其他条件不工作
- android - 在目的地更改时重新路由此处地图。这是一个移动的目标
- python - 我想将 matplotlib 中的现有图形添加到我的 wxpython GUI
- sql - SQL:使用多个条件选择每个名称的单个项目
- sql - 使用 Node.Js 更新 mssql 中的太多列
- ios - UIStepper 不会注册任何事件
- jfreechart - JFreeChart:十字准线标签自定义位置
- copy - Pytorch 复制张量的首选方式
- dataset - 用于图像分类的不平衡和小数据集
- python - Python 当前工作目录设置为主路径而不是脚本目录