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()
}
}