Berserker
Berserker

Reputation: 55

How to fix a Circular countdown timer?

I've made a Circular countdowntimer, it works, but is have a little issue.

Instead of going from a orange circle and countdown to .. a grey circle, is always a little portion of orange in the circle, and when the estimated delivery time is achieved , is all grey, as expected.

This is how it stays : Picture 1

This is how I'm expecting to be : Picture 2

I'll Show the code bellow and I will explain the important things.

This is 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)
            }
    }
    }

The function startFetchStatus() gets the data from the backend every 20 seconds, and it looks like this, for example: Fetch response

This is the CircularTimer View :

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

This is 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
    }
    
}


Upvotes: 2

Views: 214

Answers (1)

Berserker
Berserker

Reputation: 55

Fixed the issue. I just needed to delete * 0.01 from

onTick = CGFloat(remainingMinutes) / CGFloat(totalMinutes) * 0.01

Thanks everyone for all the replies. !

Upvotes: 1

Related Questions