'SwiftUI : 1. How I can bind the value with computation?, 2. How I can use the specific variable for new variable?

First, This is a mainView for timer

import SwiftUI


struct HomeView: View {

@EnvironmentObject var tm : TimerModel

@State var timerMode : TimerMode = .normal
@State var isStarted : Bool = false

init() {
        UINavigationBar.appearance().largeTitleTextAttributes = [.font : UIFont(name: "Poppins-ExtraBold", size: 30)!]
        UINavigationBar.appearance().titleTextAttributes = [.font : UIFont(name: "Poppins-SemiBold", size: 20)!]
}

var body: some View {
    NavigationView {
        VStack{
            
            Text("Focus Time")
                .font(Font.custom("Poppins-ExtraBold", size: 25))
                .padding()
            
            
            ProgressView(progress: tm.progress, time: formatTime())
                .padding()
                .onReceive(tm.timer) { _ in
                    if timerMode == .start {
                        tm.trackProgress()
                    }
                }
            
            Text("This is focus time now.\nConcentrate on your purpose.")
                .font(Font.custom("Poppins-SemiBold", size: 15))
                .multilineTextAlignment(.center)
                .padding()
        
            HStack {
            
            Button(action: {
                switch timerMode {
                case .normal:
                    self.timerMode = .start
                    self.isStarted.toggle()
                case .start:
                    self.timerMode = .normal
                    tm.elapsedTime = tm.totalTime
                    tm.progress = 0
                    self.isStarted.toggle()
                case .pause:
                    self.timerMode = .normal
                    tm.elapsedTime = tm.totalTime
                    tm.progress = 0
                    self.isStarted.toggle()
                case .stop:
                    self.timerMode = .normal
                }
            }, label: {
                Image(systemName: isStarted ? "square.fill":"play.fill")
                    .frame(width : 60, height : 60)
                    .background(isStarted ? .red : .green)
                    .foregroundColor(.white)
                    .font(.title)
                    .cornerRadius(10)
                    .shadow(color: .gray.opacity(0.15), radius: 1, x: 1, y: 1)
            })
            .padding()
            Button(action:  {
                switch timerMode {
                case .normal:
                    return
                case .start:
                    self.timerMode = .pause
                case .pause:
                    self.timerMode = .start
                case .stop:
                    return
                }
            }, label: {
                Image(systemName: "pause.fill")
                    .frame(width : 60, height : 60)
                    .background(timerMode == .normal ? .gray : .yellow)
                    .foregroundColor(.white)
                    .font(.title)
                    .cornerRadius(10)
                    .shadow(color: .gray.opacity(0.15), radius: 1, x: 1, y: 1)
            })
            .disabled(timerMode == .normal)
            .padding()

            
            }
            Spacer()
        }
            .navigationTitle("Timer")
            .navigationBarItems(trailing:
               HStack{
                NavigationLink(destination: {
                    AddTimerView()
                }, label: {
                    Image(systemName: "plus")
                })
            }
                                    
            )
    }
   
}
}

extension HomeView {

func formatTime() -> String {
    if tm.timerStyle == .focus {
        let minute = Int(tm.focusTime) / 60 % 60
        let second = Int(tm.focusTime) % 60
        
        return String(format: "%02i:%02i", minute, second)
        
    } else if tm.timerStyle == .short {
        
        let minute = Int(tm.breakTime) / 60 % 60
        let second = Int(tm.breakTime) % 60
        
        return String(format: "%02i:%02i", minute, second)
    } else if tm.timerStyle == .long {
        let minute = Int(tm.longBreakTime) / 60 % 60
        let second = Int(tm.longBreakTime) % 60
        
        return String(format: "%02i:%02i", minute, second)
    }
    return String(format: "00:00")
}
}

This is timerModel,

import Foundation

class TimerModel : ObservableObject {

@Published var timerStyle : TimerStyle = .focus
@Published var elapsedTime : Double = 300
@Published var progress : Double = 0.0
@Published var focusTime : Double = 0.0
@Published var breakTime : Double = 0.0
@Published var longBreakTime : Double = 0.0
@Published var isAuto : Bool = false
@Published var isSkipMode : Bool = false

var totalTime : Double {
    switch timerStyle {
    case .focus:
        return focusTime
    case .short:
        return breakTime
    case .long:
        return longBreakTime
    }
}

var timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()

func trackProgress() {
    
    switch timerStyle {
    case .focus:
        focusTime -= 1
        progress = (totalTime - focusTime) / totalTime
    case .short:
        breakTime -= 1
        progress = (totalTime - breakTime) / totalTime
    case .long:
        longBreakTime -= 1
        progress = (totalTime - longBreakTime) / totalTime
      }
    }
}

enum TimerMode {
    case normal, start, pause, stop
}

enum TimerStyle {
    case focus, short, long
}

Finally, This is AddtimerView, By picker in this View, I will use the picked value for manager variable by binding,

import SwiftUI

struct AddTimerView: View {

@EnvironmentObject var tm : TimerModel

@State var isAuto : Bool = false
@State var isTimePicker : Bool = false

var body: some View {
        Form {
            Section {
                HStack{
                    Image(systemName: "timer")
                        .frame(width : 30, height : 30)
                        .background(.green)
                        .foregroundColor(.white)
                        .cornerRadius(5)
                    Text("Timer Icon")
                        .font(Font.custom("Poppins-SemiBold", size: 15))
                    Spacer()
                    Menu("🍅") {
                        Button(action: {
                            
                        }, label: {
                            Text("🍅")
                                .frame(width : 30, height : 30)
                                .background(.gray.opacity(0.4))
                                .foregroundColor(.white)
                                .cornerRadius(5)
                        })
                    }
                    
                }
                .buttonStyle(PlainButtonStyle())
            }
            
            Section{
                HStack{
                    Image(systemName: "target")
                        .frame(width : 30, height : 30)
                        .background(.red)
                        .foregroundColor(.white)
                        .cornerRadius(5)
                    Text("Focus Time")
                        .font(Font.custom("Poppins-SemiBold", size: 15))
                    Spacer()
                    
                    Picker("Time", selection: $tm.focusTime) {
                        ForEach(0..<61) {item in
                            Text("\(item)min")
                        }
                    }
                    .pickerStyle(.menu)
                }
                .buttonStyle(PlainButtonStyle())
                
                HStack{
                    Image(systemName: "sun.max.fill")
                        .frame(width : 30, height : 30)
                        .background(.yellow)
                        .foregroundColor(.white)
                        .cornerRadius(5)
                    Text("Short Break")
                        .font(Font.custom("Poppins-SemiBold", size: 15))
                    Spacer()
                    Picker("Time", selection: $tm.breakTime) {
                        ForEach(0..<61) {item in
                            Text("\(item)min")
                        }
                    }
                    .pickerStyle(.menu)
                }
                .buttonStyle(PlainButtonStyle())
                
                HStack{
                    Image(systemName: "moon.fill")
                        .frame(width : 30, height : 30)
                        .background(.blue)
                        .foregroundColor(.white)
                        .cornerRadius(5)
                    Text("Long Break")
                        .font(Font.custom("Poppins-SemiBold", size: 15))
                    Spacer()
                    Picker("Time", selection: $tm.longBreakTime) {
                        ForEach(0..<61) {item in
                            Text("\(item)min")
                        }
                    }
                    .pickerStyle(.menu)
                }
                .buttonStyle(PlainButtonStyle())
            }
            
            Section {
                HStack{
                    Text("Auto Continue")
                        .font(Font.custom("Poppins-SemiBold", size: 15))
                    Spacer()
                    Toggle("", isOn: $tm.isAuto)
                }
                
                HStack{
                    Text("Skip Break")
                        .font(Font.custom("Poppins-SemiBold", size: 15))
                    Spacer()
                    Toggle("", isOn: $tm.isSkipMode)
                }
            }
            
            
            
        }
    .navigationTitle("New Timer")
}
}

My question are,

  1. How I can bind the value with computation, for example

    Picker("Time", selection: $tm.focusTime) { ForEach(0..<61) {item in Text("(item)min") } } .pickerStyle(.menu)

in this code, I want to use the item*60 so, I tried ,selection : $tm.focustime * 60, but got error.

  1. if I want to total time = focusTime(that is selected by addView()), can I do this?

lazy var totalTime : Double = focusTime

Actually, I tried but, got error as well.

Please help me, thanks !



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source