1. Timer operation
1.1 Implementation
/// Timer publisher and subscriber struct TimerBootcamp: View { // Timer publisher timer1/timer3/timer5 = 1.0, timer2 = 2.0, timer4 = 0.5 let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() // ---1--- // Computed property current time @State var currentDate = Date() var dateFormatter: DateFormatter{ let formatter = DateFormatter() //formatter.dateStyle = .medium formatter.timeStyle = .medium return formatter } // ---2--- // CountDown countdown @State var countDown: Int = 10 @State var finishedText: String? = nil // ---3--- // Countdown to date @State var timeRemaining: String = "" // Future time one day ahead let futureDate:Date = Calendar.current.date(byAdding: .hour, value: 1, to: Date()) Date() //update time 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 Animation counter @State var countCircle: Int = 0 // ---5--- // animation page @State var count: Int = 1 //Background View var backgroundView: some View{ // gradient 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 } // Method five var timer5: some View{ ZStack { // background 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) } // Listening timer .onReceive(timer) { _ in // add animation withAnimation(.default){ count = count == 5 ? 1 : count + 1 } } } // Method 4 var timer4: some View{ ZStack { // background 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) } // Listening timer .onReceive(timer) { _ in // add animation withAnimation(.easeInOut(duration: 0.5)){ countCircle = countCircle == 3 ? 0 : countCircle + 1 } } } // Method three var timer3: some View{ ZStack { // background view backgroundView // display text Text(timeRemaining) .font(.system(size: 100, weight: .semibold, design: .rounded)) .foregroundColor(.white) .lineLimit(1) // keep 1 line .minimumScaleFactor(0.1) //The minimum scale factor is 0.1 } // Listening timer .onReceive(timer) { _ in updateTimeRemaRemaining() } } // Method 2 var timer2: some View{ ZStack { // background view backgroundView // display text Text(finishedText "\(countDown)") .font(.system(size: 100, weight: .semibold, design: .rounded)) .foregroundColor(.white) .lineLimit(1) // keep 1 line .minimumScaleFactor(0.1) //The minimum scale factor is 0.1 } // Listening timer .onReceive(timer) { _ in if countDown <= 1 { finishedText = "Wow!" }else{ countDown -= 1 } } } // method one var timer1: some View{ ZStack { //Background View backgroundView // display text Text(dateFormatter.string(from: currentDate)) .font(.system(size: 100, weight: .semibold, design: .rounded)) .foregroundColor(.white) .lineLimit(1) // keep 1 line .minimumScaleFactor(0.1) //The minimum scale factor is 0.1 } // Listening timer .onReceive(timer) { output in currentDate = output } } }
1.2 Rendering:
2. Subscribar input text box monitors text subscriber mode
2.1 Implementation
import Combine /// ViewModel class SubscribarViewModel: ObservableObject{ @Published var count: Int = 0 // var timer: AnyCancellable? var canncellables = Set<AnyCancellable>() // Publisher: You can subscribe to the value published when the publisher changes @Published var textFieldText: String = "" @Published var textIsValid: Bool = false @Published var showButton: Bool = false init() { setUpTimer() addTextFieldSubscriber() addButtonSubscriber() } //Set timer func setUpTimer(){ //Send regularly 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: & amp;canncellables) } // Enter text subscriber func addTextFieldSubscriber(){ $textFieldText // debounce debounce. When the input stops, execute the following code every 0.5 seconds. .debounce(for: .seconds(0.5), scheduler: DispatchQueue.main) .map { text in if text.count > 3 { return true } return false } // It is not recommended to use .assign because it uses a strong reference and cannot be modified. It is recommended to use .sink //.assign(to: \.textIsValid, on: self) .sink(receiveValue: {[weak self] isValid in guard let self = self else { return } self.textIsValid = isValid }) // Cancel at any time .store(in: & amp;canncellables) } //Add button subscriber func addButtonSubscriber(){ $textIsValid // Combine latest values .combineLatest($count) .sink { [weak self] isValid, count in guard let self = self else { return } if isValid & amp; & amp; count >= 5 { self.showButton = true }else{ self.showButton = false } } .store(in: & amp;canncellables) } } // Subscriber mode 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() } }