如何在使用 tabview 更改选项卡时使 SwiftUI 中的计时器继续触发
How to make a timer in SwiftUI keep firing when changing tab with tabview
我有一个每半秒触发一次的计时器,它会调用一个函数,该函数输出一组用于显示特定日期倒计时的字符串。当我创建一个新事件然后切换到包含倒计时信息的选项卡时它起作用,但是当我切换回添加事件选项卡然后返回时它停止倒计时。
计时器是用这个制作的:
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
稍后使用这个运行
ForEach(eventNames.indices, id: \.self) { index in
VStack{
Text("Your event " + "\(self.eventNames[index])" + " is in " + "\(self.string[index])")
.onReceive(self.timer) { input in
self.differenceDate(numbers: index)
}
}
}
最后,它调用这个函数
func differenceDate(numbers: Int) {
self.formatter.unitsStyle = .full
self.formatter.allowedUnits = [.day, .hour, .minute, .second]
//self.formatter.maximumUnitCount = 2
self.now = Date();
if self.now > self.eventDates[numbers] {
self.eventNames[numbers] = "";
}
else {
self.string[numbers] = self.formatter.string(from: self.now, to: self.eventDates[numbers]) ?? ""
}
}
这是完整代码
import SwiftUI
struct ContentView: View {
@State private var selection = 0
@State private var eventDates = [Date]()
@State private var eventNames = [String]()
@State private var currentName = "";
@State private var counter = 0;
@State private var placeholderText = "Event Name";
@State private var selectedDate = Date();
var numbers = 0;
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
@State var now = Date();
@State var string = [String]();
var formatter = DateComponentsFormatter();
func differenceDate(numbers: Int) {
self.formatter.unitsStyle = .full
self.formatter.allowedUnits = [.day, .hour, .minute, .second]
//self.formatter.maximumUnitCount = 2
self.now = Date();
if self.now > self.eventDates[numbers] {
self.eventNames[numbers] = "";
}
else {
self.string[numbers] = self.formatter.string(from: self.now, to: self.eventDates[numbers]) ?? ""
}
}
var body: some View {
TabView(selection: $selection){
//Page 1
VStack{
Text("Add New Event")
.underline()
.font(.title)
.padding(15)
// .onReceive(self.timer) { input in
// self.differenceDate(numbers: index)
// //}
// }
// .minimumScaleFactor(0.1)
TextField("\(placeholderText)", text: $currentName)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.gray, lineWidth: 1)
.padding(5)
)
Text("When is your event?")
DatePicker("Please enter a date", selection: $selectedDate, displayedComponents: .date)
.labelsHidden()
.scaledToFill()
Button(action: {
if self.currentName != "" {
self.eventNames.append(self.currentName)
self.eventDates.append(self.selectedDate)
self.string.append("")
self.currentName = "";
}
})
{
Text("Add Event")
.font(.headline)
.foregroundColor(.black)
}
.padding(25)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.gray, lineWidth: 3)
.padding(5)
)
}
//Tab 1
.tabItem {
VStack {
Image(systemName: "calendar")
Text("Add Event")
}
}
.tag(1)
//Page 2
VStack{
Text("Your Events").underline()
.font(.title)
.padding(15)
ForEach(eventNames.indices, id: \.self) { index in
VStack{
Text("Your event " + "\(self.eventNames[index])" + " is in " + "\(self.string[index])")
.onReceive(self.timer) { input in
self.differenceDate(numbers: index)
}
}
}
}
//Tab 2
.font(.title)
.tabItem {
VStack {
Image(systemName: "flame.fill")
Text("Countdowns")
}
}
.tag(0)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
我想知道是否有解决方法或如何在选项卡更改时保持计时器触发或在选项卡更改时暂停它然后在选项卡换回时再次启动它。
它需要将 .onReceive
附加到 TabView
并且它将在所有选项卡上接收,例如
TabView {
...
// << all tab items here
...
}
.onReceive(self.timer) { _ in
self.differenceDate()
}
并在处理程序中迭代索引
func differenceDate() {
for numbers in eventNames.indices {
// current body here
}
}
我有一个每半秒触发一次的计时器,它会调用一个函数,该函数输出一组用于显示特定日期倒计时的字符串。当我创建一个新事件然后切换到包含倒计时信息的选项卡时它起作用,但是当我切换回添加事件选项卡然后返回时它停止倒计时。
计时器是用这个制作的:
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
稍后使用这个运行
ForEach(eventNames.indices, id: \.self) { index in
VStack{
Text("Your event " + "\(self.eventNames[index])" + " is in " + "\(self.string[index])")
.onReceive(self.timer) { input in
self.differenceDate(numbers: index)
}
}
}
最后,它调用这个函数
func differenceDate(numbers: Int) {
self.formatter.unitsStyle = .full
self.formatter.allowedUnits = [.day, .hour, .minute, .second]
//self.formatter.maximumUnitCount = 2
self.now = Date();
if self.now > self.eventDates[numbers] {
self.eventNames[numbers] = "";
}
else {
self.string[numbers] = self.formatter.string(from: self.now, to: self.eventDates[numbers]) ?? ""
}
}
这是完整代码
import SwiftUI
struct ContentView: View {
@State private var selection = 0
@State private var eventDates = [Date]()
@State private var eventNames = [String]()
@State private var currentName = "";
@State private var counter = 0;
@State private var placeholderText = "Event Name";
@State private var selectedDate = Date();
var numbers = 0;
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect()
@State var now = Date();
@State var string = [String]();
var formatter = DateComponentsFormatter();
func differenceDate(numbers: Int) {
self.formatter.unitsStyle = .full
self.formatter.allowedUnits = [.day, .hour, .minute, .second]
//self.formatter.maximumUnitCount = 2
self.now = Date();
if self.now > self.eventDates[numbers] {
self.eventNames[numbers] = "";
}
else {
self.string[numbers] = self.formatter.string(from: self.now, to: self.eventDates[numbers]) ?? ""
}
}
var body: some View {
TabView(selection: $selection){
//Page 1
VStack{
Text("Add New Event")
.underline()
.font(.title)
.padding(15)
// .onReceive(self.timer) { input in
// self.differenceDate(numbers: index)
// //}
// }
// .minimumScaleFactor(0.1)
TextField("\(placeholderText)", text: $currentName)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.gray, lineWidth: 1)
.padding(5)
)
Text("When is your event?")
DatePicker("Please enter a date", selection: $selectedDate, displayedComponents: .date)
.labelsHidden()
.scaledToFill()
Button(action: {
if self.currentName != "" {
self.eventNames.append(self.currentName)
self.eventDates.append(self.selectedDate)
self.string.append("")
self.currentName = "";
}
})
{
Text("Add Event")
.font(.headline)
.foregroundColor(.black)
}
.padding(25)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.gray, lineWidth: 3)
.padding(5)
)
}
//Tab 1
.tabItem {
VStack {
Image(systemName: "calendar")
Text("Add Event")
}
}
.tag(1)
//Page 2
VStack{
Text("Your Events").underline()
.font(.title)
.padding(15)
ForEach(eventNames.indices, id: \.self) { index in
VStack{
Text("Your event " + "\(self.eventNames[index])" + " is in " + "\(self.string[index])")
.onReceive(self.timer) { input in
self.differenceDate(numbers: index)
}
}
}
}
//Tab 2
.font(.title)
.tabItem {
VStack {
Image(systemName: "flame.fill")
Text("Countdowns")
}
}
.tag(0)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
我想知道是否有解决方法或如何在选项卡更改时保持计时器触发或在选项卡更改时暂停它然后在选项卡换回时再次启动它。
它需要将 .onReceive
附加到 TabView
并且它将在所有选项卡上接收,例如
TabView {
...
// << all tab items here
...
}
.onReceive(self.timer) { _ in
self.differenceDate()
}
并在处理程序中迭代索引
func differenceDate() {
for numbers in eventNames.indices {
// current body here
}
}