半相关问题:定时器运行时SwiftUI ActionSheet不解散
我目前正在处理的项目中遇到警报问题。当有一个定时器在后台运行时,呈现的警报将不会消失。大多数情况下,它需要点击几下解除按钮才能消失。我在一个示例项目中以尽可能少的开销重现了这个问题。
我的主要项目在尝试在不同视图上显示警报时存在此问题,但我无法在示例项目中重现该问题。通过在计时器正在运行的同一视图上切换警报,可以可靠地复制这个问题。我还通过从文本字段中删除绑定来阻止文本字段视图更新进行了测试。在第一次单击时警报仍然无法解除。我不确定是否有办法解决这个问题,我正在寻找任何可能的建议。
Xcode 13.0/iOS 15.0和出现在iOS 14.0也
Timerview.swift
struct TimerView: View {
@ObservedObject var stopwatch = Stopwatch()
@State var isAlertPresented:Bool = false
var body: some View {
VStack{
Text(String(format: "%.1f", stopwatch.secondsElapsed))
.font(.system(size: 70.0))
.minimumScaleFactor(0.1)
.lineLimit(1)
Button(action:{
stopwatch.actionStartStop()
}){
Text("Toggle Timer")
}
Button(action:{
isAlertPresented.toggle()
}){
Text("Toggle Alert")
}
}
.alert(isPresented: $isAlertPresented){
Alert(title:Text("Error"),message:Text("I am presented"))
}
}
}
Stopwatch.swift
class Stopwatch: ObservableObject{
@Published var secondsElapsed: TimeInterval = 0.0
@Published var mode: stopWatchMode = .stopped
func actionStartStop(){
if mode == .stopped{
start()
}else{
stop()
}
}
var timer = Timer()
func start() {
secondsElapsed = 0.0
mode = .running
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
self.secondsElapsed += 0.1
}
}
func stop() {
timer.invalidate()
mode = .stopped
}
enum stopWatchMode {
case running
case stopped
}
}
编辑:将按钮移动到自定义视图中解决了最初的问题,但是当按钮需要与Observable对象交互时,是否有解决方案?
Button(action:{
do{
try stopwatch.actionDoThis()
}catch{
isAlertPresented = true
}
}){
Text("Toggle Alert")
}.alert(isPresented: $isAlertPresented){
Alert(title:Text("Error"),message:Text("I am presented"))
每次计时器运行时,UI都会重新创建,因为"是一个可观察对象。SwiftUI将自动监控"secondsElapsed"的变化,并重新调用视图的body属性。为了避免这种情况,我们需要将按钮和Alert分开到另一个视图,如下所示。
struct TimerView: View {
@ObservedObject var stopwatch = Stopwatch()
@State var isAlertPresented:Bool = false
var body: some View {
VStack{
Text(String(format: "%.1f", stopwatch.secondsElapsed))
.font(.system(size: 70.0))
.minimumScaleFactor(0.1)
.lineLimit(1)
Button(action:{
stopwatch.actionStartStop()
}){
Text("Toggle Timer")
}
CustomAlertView(isAlertPresented: $isAlertPresented)
}
}
}
struct CustomAlertView: View {
@Binding var isAlertPresented: Bool
var body: some View {
Button(action:{
isAlertPresented.toggle()
}){
Text("Toggle Alert")
}.alert(isPresented: $isAlertPresented){
Alert(title:Text("Error"),message:Text("I am presented"))
}
}
}
如果你真的需要ObservedObject或它的任何属性来执行任何操作,在"OK"提示的动作,你可以做一个变通。
struct TimerView: View {
@ObservedObject var stopwatch = Stopwatch()
@State var isResetAccepted: Bool = false
var body: some View {
VStack{
Text(String(format: "%.1f", stopwatch.secondsElapsed))
.font(.system(size: 70.0))
.minimumScaleFactor(0.1)
.lineLimit(1)
Button(action:{
stopwatch.actionStartStop()
}){
Text("Toggle Timer")
}
CustomAlertView(isResetAccepted: $isResetAccepted)
.onChange(of: isResetAccepted) { newValue in
if newValue {
isResetAccepetd = false
stopwatch.reset()
}
}
}
}
}
struct CustomAlertView: View {
@Binding var isResetAccepted: Bool
@State var isAlertPresented: Bool = false
var body: some View {
Button(action:{
isAlertPresented.toggle()
}){
Text("Toggle Alert")
}.alert(isPresented: $isAlertPresented){
Alert(title:Text("Error"),
message:Text("I am presented"),
primaryButton: .destructive(Text("Cancel"), action: {
self.isResetAccepted = false
self.isAlertPresented = false
}),
secondaryButton: .default(Text("OK"), action: {
self.isResetAccepted = true
self.isAlertPresented = false
}))
}
}
}