Timer/计时器发布者与订阅者, Subscribar/文本框订阅模式 的使用

1. Timer 计时器的操作

  1.1 实现

/// 计时器 发布者与订阅者
struct TimerBootcamp: View {
    
    //  计时器 发布者 timer1/timer3/timer5 = 1.0 , timer2 = 2.0 , timer4 = 0.5
    let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
    
    // ---1---
    // 计算性属性 当前时间
    @State var currentDate = Date()
    var dateFormatter: DateFormatter{
        let formatter = DateFormatter()
        //formatter.dateStyle = .medium
        formatter.timeStyle = .medium
        return formatter
    }
    
    // ---2---
    // CountDown 倒计时
    @State var countDown: Int = 10
    @State var finishedText: String? = nil
    
    // ---3---
    // 倒计时日期 Countdown to date
    @State var timeRemaining: String = ""
    // 将来时间 提前一天
    let futureDate:Date = Calendar.current.date(byAdding: .hour, value: 1, to: Date()) ?? Date()
    // 更新时间
    func updateTimeRemaRemaining(){
        //.hour,
        let remaining = Calendar.current.dateComponents([.minute, .second], from: Date(), to: futureDate)
        //let hour = remaining.hour ?? 0
        let minute = remaining.minute ?? 0
        let second = remaining.second ?? 0
        //\(hour) :
        timeRemaining = " \(minute) minutes, \(second) seconds"
    }
    
    // ---4---
    // 动画计数器 Animation counter
    @State var countCircle: Int = 0
    
    // ---5---
    // 动画 page
    @State var count: Int = 1
    
    // 背景View
    var backgroundView: some View{
        // 渐变
        RadialGradient(
            gradient: Gradient(colors: [Color(#colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1)), Color(#colorLiteral(red: 0.09019608051, green: 0, blue: 0.3019607961, alpha: 1))]),
            center: .center,
            startRadius: 5,
            endRadius: 500)
        .ignoresSafeArea()
    }
    
    var body: some View{
        //timer1
        //timer2
        //timer3
        //timer4
        timer5
    }
    
    // 方式五
    var timer5: some View{
        ZStack {
            // 背景 View
            backgroundView
            
            TabView(selection: $count) {
                Rectangle()
                    .foregroundColor(.red)
                    .tag(1)
                Rectangle()
                    .foregroundColor(.blue)
                    .tag(2)
                Rectangle()
                    .foregroundColor(.green)
                    .tag(3)
                Rectangle()
                    .foregroundColor(.orange)
                    .tag(4)
                Rectangle()
                    .foregroundColor(.gray)
                    .tag(5)
            }
            .frame(height: 200)
            .tabViewStyle(.page)
        }
        // 监听计时器
        .onReceive(timer) { _ in
            // 添加动画
            withAnimation(.default){
                count = count == 5 ? 1 : count + 1
            }
        }
    }
    
    // 方式四
    var timer4: some View{
        ZStack {
            // 背景 View
            backgroundView
            
            HStack(spacing: 15) {
                Circle()
                    .offset(y: countCircle == 1 ? -20 : 0)
                Circle()
                    .offset(y: countCircle == 2 ? -20 : 0)
                Circle()
                    .offset(y: countCircle == 3 ? -20 : 0)
            }
            .frame(width: 150)
            .foregroundColor(.white)
        }
        // 监听计时器
        .onReceive(timer) { _ in
            // 添加动画
            withAnimation(.easeInOut(duration: 0.5)){
                countCircle = countCircle == 3 ? 0 : countCircle + 1
            }
        }
    }
    
    // 方式三
    var timer3: some View{
        ZStack {
            // 背景 View
            backgroundView
            
            // 显示文字
            Text(timeRemaining)
                .font(.system(size: 100, weight: .semibold, design: .rounded))
                .foregroundColor(.white)
                .lineLimit(1) // 保持1行
                .minimumScaleFactor(0.1) // 最小比例因子为 0.1
        }
        // 监听计时器
        .onReceive(timer) { _ in
            updateTimeRemaRemaining()
        }
    }
    
    // 方式二
    var timer2: some View{
        ZStack {
            // 背景 View
            backgroundView
            
            // 显示文字
            Text(finishedText ?? "\(countDown)")
                .font(.system(size: 100, weight: .semibold, design: .rounded))
                .foregroundColor(.white)
                .lineLimit(1) // 保持1行
                .minimumScaleFactor(0.1) // 最小比例因子为 0.1
        }
        // 监听计时器
        .onReceive(timer) { _ in
            if countDown <= 1 {
                finishedText = "Wow!"
            }else{
                countDown -= 1
            }
        }
    }
    
    // 方式一
    var timer1: some View{
        ZStack {
            // 背景View
            backgroundView
            
            // 显示文字
            Text(dateFormatter.string(from: currentDate))
                .font(.system(size: 100, weight: .semibold, design: .rounded))
                .foregroundColor(.white)
                .lineLimit(1) // 保持1行
                .minimumScaleFactor(0.1) // 最小比例因子为 0.1
        }
        // 监听计时器
        .onReceive(timer) { output in
            currentDate = output
        }
    }
}

  1.2 效果图:

     

          

2. Subscribar 输入文本框监听文本订阅者模式

  2.1 实现

import Combine

/// ViewModel
class SubscribarViewModel: ObservableObject{
    @Published var count: Int = 0
    
    // var timer: AnyCancellable?
    var canncellables = Set<AnyCancellable>()
    
    // 发布者: 可以订阅发布者改动时发布的值
    @Published var textFieldText: String = ""
    @Published var textIsValid: Bool = false
    @Published var showButton: Bool = false
    
    init() {
        setUpTimer()
        addTextFieldSubscriber()
        addButtonSubscriber()
    }
    
    // 设置计时器
    func setUpTimer(){
        // 定时发送  timer =
        Timer
            .publish(every: 1, on: .main, in: .common)
            .autoconnect()
            .sink { [weak self] _ in
                guard let self = self else { return }
                self.count += 1
                //if self.count >= 10 {
                //self?.count = 0
                //self.timer?.cancel()
                //for item in canncellables{
                //    item.cancel()
                //}
                //}
            }
            .store(in: &canncellables)
    }
    
    // 输入文本订阅者
    func addTextFieldSubscriber(){
        $textFieldText
        // debounce 去抖动 输入停止时,隔 0.5 秒执行后面的代码
            .debounce(for: .seconds(0.5), scheduler: DispatchQueue.main)
            .map { text in
                if text.count > 3 {
                    return true
                }
                return false
            }
        // 不建议使用 .assign ,因为使用的是强引用,没法修改 ,建议使用 .sink
        //.assign(to: \.textIsValid, on: self)
            .sink(receiveValue: {[weak self] isValid in
                guard let self = self else { return }
                self.textIsValid = isValid
            })
        // 可随时取消
            .store(in: &canncellables)
    }
    
    // 添加按钮订阅者
    func addButtonSubscriber(){
        $textIsValid
        // 组合最新值
            .combineLatest($count)
            .sink { [weak self] isValid, count in
                guard let self = self else { return }
                if isValid && count >= 5 {
                    self.showButton = true
                }else{
                    self.showButton = false
                }
            }
            .store(in: &canncellables)
    }
}

// 订阅者模式
struct SubscriberBootcamp: View {
    @StateObject var viewModel = SubscribarViewModel()
    
    var body: some View {
        VStack {
            Text("\(viewModel.count)")
                .font(.largeTitle)
            
            TextField("Type something here...", text: $viewModel.textFieldText)
                .frame(height: 55)
                .padding(.horizontal)
                .font(.headline)
                .background(Color.gray.opacity(0.3))
                .cornerRadius(10)
                .overlay(alignment: .trailing) {
                    ZStack() {
                        Image(systemName: "xmark")
                            .foregroundColor(.red)
                            .opacity(viewModel.textFieldText.count < 1 ? 0.0:
                                        viewModel.textIsValid ? 0.0 : 1.0)
                        Image(systemName: "checkmark")
                            .foregroundColor(.accentColor)
                            .opacity(viewModel.textIsValid ? 1.0 : 0.0)
                    }
                    .font(.title)
                    .padding(.trailing)
                }
            
            Button {
            } label: {
                Text("Submit")
                    .font(.headline)
                    .foregroundColor(.white)
                    .frame(height: 55)
                    .frame(maxWidth: .infinity)
                    .background(Color.accentColor)
                    .cornerRadius(10)
                    .opacity(viewModel.showButton ? 1.0 : 0.5)
            }
            .disabled(!viewModel.showButton)
        }
        .padding()
    }
}

  2.2 效果图:

猜你喜欢

转载自blog.csdn.net/u011193452/article/details/133382361