Hice un temporizador de cuenta regresiva circular, funciona, pero tiene un pequeño problema.
En lugar de pasar de un círculo naranja y una cuenta regresiva a... un círculo gris, siempre hay una pequeña porción de naranja en el círculo, y cuando se alcanza el tiempo de entrega estimado, todo es gris, como se esperaba.
Así queda: Foto 1
Así es como espero ser: Foto 2
Mostraré el código a continuación y explicaré las cosas importantes.
Esto es WaitingOrderView.
struct WaitingOrderView: View { @State private var timer: AnyCancellable? @EnvironmentObject var syncViewModel : SyncViewModel var body: some View { ZStack { if syncViewModel._order.id == 0 && syncViewModel._order.status == 0 { CartView() } else if syncViewModel._order.status == syncViewModel.statusList.first(where: { status in status.key == StatusKey.accepted.rawValue })?.id { OrderConfirmedView() } } .onAppear() { startFetchStatus() } } func startFetchStatus() { timer = Timer.publish(every: 20, on: .main, in: .common) .autoconnect() .sink { _ in syncViewModel.fetchOrder(id: syncViewModel._order.id) } } }La función startFetchStatus() obtiene los datos del backend cada 20 segundos y se ve así, por ejemplo: Obtener respuesta
Esta es la vista CircularTimer:
let timer = Timer .publish(every: 1, on: .main, in: .common) .autoconnect() @available(iOS 15, *) struct CircularTimer: View { var orderDate : Date var orderDeliveryDate : Date @State var onTick: CGFloat = 1 let date = Date() var body: some View { VStack(spacing : 0){ Image("clock_button") ZStack{ Circle() .fill(Color.clear) .frame(width: 250, height: 250) .overlay( Circle().stroke(Color.gray.opacity(22/100), lineWidth: 5) ) Circle() .fill(Color.clear) .frame(width: 250, height: 250) .overlay( Circle().trim(from:0, to: onTick) .stroke( style: StrokeStyle( lineWidth: 5, lineCap: .round , lineJoin:.round ) ) .foregroundColor( ( completed() ? Color.orange: Color.orange) ).animation( .easeInOut(duration: 0.2) ) ) .rotationEffect(Angle(degrees: 270.0)) Image("indicator_ellipse") .resizable() .frame(width: 230, height: 230) } }.onReceive(timer) { time in progress(time: Int(time.timeIntervalSince1970)) } } func completed() -> Bool { return onTick == 1 } func progress(time: Int) { let minutesOrderDeliveryDate = Int(orderDeliveryDate.timeIntervalSince1970) let minutesOrderDate = Int(orderDate.timeIntervalSince1970) let minutesCurrentDate = time let totalMinutes = minutesOrderDeliveryDate - minutesOrderDate let remainingMinutes = minutesOrderDeliveryDate - minutesCurrentDate onTick = CGFloat(remainingMinutes) / CGFloat(totalMinutes) * 0.01 print(onTick) } func dateFormatTime(date : String) -> Date { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" dateFormatter.timeZone = .current return dateFormatter.date(from: date) ?? Date.now } }Este es OrderConfirmedView:
struct OrderConfirmedVieww: View { @EnvironmentObject var syncViewModel : SyncViewModel @State var nowDate: Date = Date() var body: some View { VStack { Spacer() Text(Texts.orderConfirmedText1) .font(.title) .fontWeight(.semibold) .foregroundColor(.colorGrayDark) .multilineTextAlignment(.center) .lineLimit(3) .padding(40) CircularTimer(orderDate: dateFormatTime(date: syncViewModel._order.date ?? ""), orderDeliveryDate: dateFormatTime(date: syncViewModel._order.deliveryDate ?? "")) .padding() // Spacer() Text(Texts.orderOraLivrareText) .font(.headline) .fontWeight(.thin) .padding(.bottom) Text(dateFormatTime(date: syncViewModel._order.deliveryDate ?? ""), style: .time) .font(.title2) .fontWeight(.bold) Spacer() Spacer() Button { } label: { Text(Texts.orderButtonText) .font(.headline) .foregroundColor(.white) .frame(height: 55) .frame(maxWidth: .infinity) .background(Color.onboardingColor) .cornerRadius(20) } .padding() } } func dateFormatTime(date : String) -> Date { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" dateFormatter.timeZone = .current return dateFormatter.date(from: date) ?? Date.now } }Se solucionó el problema. Solo necesitaba eliminar * 0.01 de
onTick = CGFloat(remainingMinutes) / CGFloat(totalMinutes) * 0.01
Gracias a todos por todas las respuestas. !