我对SwiftUI框架还很陌生,还没有完全理解,所以请耐心等待。
当绑定更改时,是否可以从“另一个视图”内部触发“叠加视图”?参见下图:
我认为此“重叠视图”将包裹我的所有视图。我不确定如何执行此操作-也许可以使用ZIndex
。我还猜想,当绑定更改时,我需要某种回调,但是我也不知道该怎么做。
到目前为止,这是我得到的:
内容查看
struct ContentView : View {
@State private var liked: Bool = false
var body: some View {
VStack {
LikeButton(liked: $liked)
}
}
}
Run Code Online (Sandbox Code Playgroud)
Like按钮
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
}
}
Run Code Online (Sandbox Code Playgroud)
我觉得我内部需要某种回调LikeButton
,但是我不确定在Swift中这一切如何工作。
任何帮助,将不胜感激。提前致谢!
Mat*_*ini 29
在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)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
正文说明:
GeometryReader
为我们提供了首选的超级视图尺寸,从而为我们提供了完美的尺寸Toast
。ZStack
将视图彼此堆叠。isShowing == false
),则可以渲染presenting
视图。如果必须显示(isShowing == true
),则我们将presenting
视图渲染得有些模糊-因为我们可以-然后我们接下来创建烤面包。VStack
with a Text
, with custom frame sizing, some design bells and whistles (colors and corner radius), and a default slide
transition.I added this method on View
to make the Toast
creation easier:
extension View {
func toast(isShowing: Binding<Bool>, text: Text) -> some View {
Toast(isShowing: isShowing,
presenting: { self },
text: text)
}
}
Run Code Online (Sandbox Code Playgroud)
And a little demo on how to use it:
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!"))
}
}
Run Code Online (Sandbox Code Playgroud)
I used a NavigationView
to make sure the view fills the entire screen, so the Toast
is sized and positioned correctly.
The withAnimation
block ensures the Toast
transition is applied.
How it looks:
It's easy to extend the Toast
with the power of SwiftUI DSL.
The Text
property can easily become a @ViewBuilder
closure to accomodate the most extravagant of the layouts.
To add it to your content view:
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!"))
}
}
Run Code Online (Sandbox Code Playgroud)
How to hide the toast afte 2 seconds (as requested):
Append this code after .transition(.slide)
in the toast VStack
.
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation {
self.isShowing = false
}
}
}
Run Code Online (Sandbox Code Playgroud)
Tested on Xcode 11.1
我修改了上面 Matteo Pacini 的精彩回答,加入了一些评论,让 Toast 在延迟后淡入淡出。我还修改了 View 扩展,使其更加通用,并接受类似于 .sheet 工作方式的尾随闭包。
ContentView.swift:
struct ContentView: View {
@State private var lightsOn: Bool = false
@State private var showToast: Bool = false
var body: some View {
VStack {
Button(action: {
if (!self.showToast) {
self.lightsOn.toggle()
withAnimation {
self.showToast = true
}
}
}){
Text("switch")
} //Button
.padding(.top)
Image(systemName: self.lightsOn ? "lightbulb" : "lightbulb.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.padding(.all)
.toast(isPresented: self.$showToast) {
HStack {
Text("Lights: \(self.lightsOn ? "ON" : "OFF")")
Image(systemName: self.lightsOn ? "lightbulb" : "lightbulb.fill")
} //HStack
} //toast
} //VStack
} //body
} //ContentView
Run Code Online (Sandbox Code Playgroud)
查看+Toast.swift:
extension View {
func toast<Content>(isPresented: Binding<Bool>, content: @escaping () -> Content) -> some View where Content: View {
Toast(
isPresented: isPresented,
presenter: { self },
content: content
)
}
}
Run Code Online (Sandbox Code Playgroud)
Toast.swift:
struct Toast<Presenting, Content>: View where Presenting: View, Content: View {
@Binding var isPresented: Bool
let presenter: () -> Presenting
let content: () -> Content
let delay: TimeInterval = 2
var body: some View {
if self.isPresented {
DispatchQueue.main.asyncAfter(deadline: .now() + self.delay) {
withAnimation {
self.isPresented = false
}
}
}
return GeometryReader { geometry in
ZStack(alignment: .bottom) {
self.presenter()
ZStack {
Capsule()
.fill(Color.gray)
self.content()
} //ZStack (inner)
.frame(width: geometry.size.width / 1.25, height: geometry.size.height / 10)
.opacity(self.isPresented ? 1 : 0)
} //ZStack (outer)
.padding(.bottom)
} //GeometryReader
} //body
} //Toast
Run Code Online (Sandbox Code Playgroud)
有了这个,你可以敬酒文本,或图像(或两者,如下所示),或任何其他视图。
如果您希望它在应用程序范围内使用,请将其放在应用程序范围内的某个位置!例如,您可以将其添加到MyProjectApp.swift
(或sceneDelegate
UIKit/AppDelegate 项目中)文件中,如下所示:
请注意,按钮和状态只是为了更多解释,您可以考虑以您喜欢的方式更改它们
@main
struct SwiftUIAppPlaygroundApp: App { // <- Note that where we are!
@State var showToast = false
var body: some Scene {
WindowGroup {
Button("App-Wide Button") { showToast.toggle() }
ZStack {
ContentView() // <- The app flow
if showToast {
MyCustomToastView().ignoresSafeArea(.all, edges: .all) // <- App-wide overlays
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
看?现在,您可以在屏幕的任何位置添加任何类型的视图,而不会阻塞动画。只需将 @State 转换为某种 AppState,例如Observable
s 或Environment
s 即可!你做到了!
请注意,这是一个演示,您应该使用environment
变量或 smt 以便能够从该视图主体之外更改它
归档时间: |
|
查看次数: |
2193 次 |
最近记录: |