SwiftUI与Combine教程之做个乒乓球的动画效果(PassthroughSubject)
发布时间
阅读量:
阅读量
本文价值与收获
看完本文后,您将能够作出下面的界面


看完本文您将掌握的技能
- 学习Combine中Subject的作用机制
- 深入解析Combine中的PassthroughSubject属性
- 分析CurrentValueSubject在Combine框架中的应用场景
- 探讨 PassthroughSubject与CurrentValueSubject之间的区别与联系
- 通过 Passthought 技术实现 SwiftUI 中乒乓球效果的整合展示
Subject
Subj是发布机构的特殊形态;订阅后能够动态更新内容。目前有两类不同的类型
- PassthroughSubject:如果您注册它,则会获取所有未来事件。
- CurrentValueSubject:为此,在注册后此序列会呈现最新元素,并展示后续所有内容。
PassthroughSubject
第一步要做的是建立一个真实的传递主题示例。这是一项非常简单的任务,并不需要进行复杂的配置设置——我们可以通过调用默认初始化器来轻松完成这一操作
let passthroughObject = PassthroughSubject<String,Error>()
代码解读
下面是send常用函数
- send(input:String)通过输入参数字符串实现对subject的新值赋值。
- 当发送一个完成了的complete时
- 当发送一个包含错误信息的complete时
passthroughObject.send("Hello")
passthroughObject.send("World")
代码解读
如果采用 send() 方法注入 'Hello' 和 'word' 这两个字符串,则接收方将不会捕获到这些内容。您将只收到由订阅主题触发的响应内容。
let passThroughSubject = PassthroughSubject<String, Error>()
passThroughSubject.send("Hello")
passThroughSubject.send("Hello")
let subscription = passThroughSubject.sink(
receiveCompletion: { completion in
print("Received completion (sink)", completion)
},
receiveValue: { value in
print("Received value (sink)", value)
})
代码解读
现在,我们发送一个值后,我们订阅和vola。将打印该值。
let passThroughSubject = PassthroughSubject<String, Error>()
passThroughSubject.send("Hello")
let subscription = passThroughSubject.sink(
receiveCompletion: { completion in
print("Received completion (sink)", completion)
},
receiveValue: { value in
print("Received value (sink)", value)
})
passThroughSubject.send("Hello")
代码解读
输出
hello
代码解读
实战代码
import SwiftUI
import Combine
struct ContentView: View {
let blinkPublisher = PassthroughSubject<Void, Never>()
var body: some View {
VStack(spacing: 10) {
Text("SwiftUI和Combine乒乓球的动画效果")
.frame(maxWidth:.infinity)
.background(Color.orange)
.padding()
Button("启动看看效果") {
self.blinkPublisher.send()
}
.padding()
Text("一号选手")
.addOpacityBlinker(subscribedTo: blinkPublisher)
Text("二号选手")
.addOpacityBlinker(subscribedTo: blinkPublisher, duration: 1)
Text("三号选手")
.addOpacityBlinker(subscribedTo: blinkPublisher, duration: 2)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
extension View {
// the generic constraints here tell the compiler to accept any publisher
// that sends outputs no value and never errors
// this could be a PassthroughSubject like above, or we could even set up a TimerPublisher
// that publishes on an interval, if we wanted a looping animation
// (we'd have to map it's output to Void first)
func addOpacityBlinker<T: Publisher>(subscribedTo publisher: T, duration: Double = 1)
-> some View where T.Output == Void, T.Failure == Never {
// here I take whatever publisher we got and type erase it to AnyPublisher
// that just simplifies the type so I don't have to add extra generics below
self.modifier(OpacityBlinker(subscribedTo: publisher.eraseToAnyPublisher(),
duration: duration))
}
}
// you could call the .modifier(OpacityBlinker(...)) on your view directly,
// but I like the View extension method, as it just feels cleaner to me
struct OpacityBlinker: ViewModifier {
// this is just here to switch on and off, animating the blur on and off
@State private var isBlurred = false
var publisher: AnyPublisher<Void, Never>
// The total time it takes to blur and unblur
var duration: Double
// this initializer is not necessary, but allows us to specify a default value for duration,
// and the call side looks nicer with the 'subscribedTo' label
init(subscribedTo publisher: AnyPublisher<Void, Never>, duration: Double = 1) {
self.publisher = publisher
self.duration = duration
}
func body(content: Content) -> some View {
content
.blur(radius: isBlurred ? 10 : 0)
// This basically subscribes to the publisher, and triggers the closure
// whenever the publisher fires
.onReceive(publisher) { _ in
// perform the first half of the animation by changing isBlurred to true
// this takes place over half the duration
withAnimation(.linear(duration: self.duration / 2)) {
self.isBlurred = true
// schedule isBlurred to return to false after half the duration
// this means that the end state will return to an unblurred view
DispatchQueue.main.asyncAfter(deadline: .now() + self.duration / 2) {
withAnimation(.linear(duration: self.duration / 2)) {
self.isBlurred = false
}
}
}
}
}
}
代码解读
技术交流
QQ:3365059189
SwiftUI技术交流QQ群:518696470
全部评论 (0)
还没有任何评论哟~
