Advertisement

SwiftUI与Combine教程之做个乒乓球的动画效果(PassthroughSubject)

阅读量:

本文价值与收获

看完本文后,您将能够作出下面的界面

Jietu20200520-154510@2x.jpg
Jietu20200520-154436.gif

看完本文您将掌握的技能

  • 学习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)

还没有任何评论哟~