'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,
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.
- 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 |
|---|
