SwiftUI Problem GeometryReader with Zstack - swift

I'm trying to create my own PhoneTextField in and I have found a tutorial in here it's working fine if don't call it inside ZStack let's see the code example as below
countryCode View
struct CountryCodes: View {
#Binding var countryCode : String
#Binding var countryFlag : String
#Binding var y : CGFloat
var body: some View {
GeometryReader { geo in
List(self.countryDictionary.sorted(by: <), id: \.key) { key , value in
HStack {
Text("\(self.flag(country: key))")
Text("\(self.countryName(countryCode: key) ?? key)")
Spacer()
Text("+\(value)").foregroundColor(.secondary)
}.background(Color.white)
.font(.system(size: 20))
.onTapGesture {
self.countryCode = value
self.countryFlag = self.flag(country: key)
withAnimation(.spring()) {
self.y = 350
}
}
}
.padding(.bottom)
.frame(width: geo.size.width, height: 500)
.position(x: geo.frame(in: .global).midX, y: geo.frame(in: .global).maxY - -200)
}
}
let countryDictionary = ["AF":"93","AL":"355","DZ":"213","US":"1",
"AD":"376","AO":"244","AI":"1","AG":"1","AR":"54",
"AM":"374","AW":"297","AU":"61","AT":"43","AZ":"994",
"BS":"1","BH":"973","BD":"880","BB":"1","BY":"375",
"BE":"32","BZ":"501","BJ":"229","BM":"1","BT":"975",
"BA":"387","BW":"267","BR":"55","IO":"246","BG":"359",
"BF":"226","BI":"257","KH":"855","CM":"237","CA":"1",
"CV":"238","KY":"345","CF":"236","TD":"235","CL":"56","CN":"86",
"CX":"61","CO":"57","KM":"269","CG":"242","CK":"682","CR":"506",
"HR":"385","CU":"53","CY":"537","CZ":"420","DK":"45","DJ":"253",
"DM":"1","DO":"1","EC":"593","EG":"20","SV":"503","GQ":"240",
"ER":"291","EE":"372","ET":"251","FO":"298","FJ":"679","FI":"358",
"FR":"33","GF":"594","PF":"689","GA":"241","GM":"220","GE":"995",
"DE":"49","GH":"233","GI":"350","GR":"30","GL":"299","GD":"1",
"GP":"590","GU":"1","GT":"502","GN":"224","GW":"245","GY":"595","HT":"509",
"HN":"504","HU":"36","IS":"354","IN":"91","ID":"62","IQ":"964",
"IE":"353","IL":"972","IT":"39","JM":"1","JP":"81","JO":"962",
"KZ":"77","KE":"254","KI":"686","KW":"965","KG":"996","LV":"371",
"LB":"961","LS":"266","LR":"231","LI":"423","LT":"370","LU":"352",
"MG":"261","MW":"265","MY":"60","MV":"960","ML":"223",
"MT":"356","MH":"692","MQ":"596","MR":"222","MU":"230","YT":"262",
"MX":"52","MC":"377","MN":"976", "ME":"382","MS":"1","MA":"212",
"MM":"95","NA":"264","NR":"674","NP":"977","NL":"31","NC":"687",
"NZ":"64","NI":"505","NE":"227","NG":"234","NU":"683",
"NF":"672","MP":"1","NO":"47","OM":"968","PK":"92","PW":"680",
"PA":"507","PG":"675","PY":"595","PE":"51","PH":"63","PL":"48",
"PT":"351","PR":"1","QA":"974","RO":"40","RW":"250","WS":"685",
"SM":"378","SA":"966","SN":"221","RS":"381","SC":"248",
"SL":"232","SG":"65","SK":"421","SI":"386","SB":"677",
"ZA":"27","GS":"500","ES":"34","LK":"94","SD":"249","SR":"597",
"SZ":"268","SE":"46","CH":"41","TJ":"992","TH":"66","TG":"228",
"TK":"690","TO":"676","TT":"1","TN":"216","TR":"90",
"TM":"993","TC":"1","TV":"688","UG":"256","UA":"380",
"AE":"971","GB":"44","AS":"1","UY":"598","UZ":"998",
"VU":"678","WF":"681","YE":"967","ZM":"260",
"ZW":"263","BO":"591","BN":"673","CC":"61",
"CD":"243","CI":"225","FK":"500","GG":"44",
"VA":"379","HK":"852","IR":"98","IM":"44",
"JE":"44","KP":"850","KR":"82","LA":"856",
"LY":"218","MO":"853","MK":"389","FM":"691",
"MD":"373","MZ":"258","PS":"970","PN":"872",
"RE":"262","RU":"7","BL":"590","SH":"290","KN":"1",
"LC":"1","MF":"590","PM":"508","VC":"1","ST":"239",
"SO":"252","SJ":"47","SY":"963","TW":"886","TZ":"255",
"TL":"670","VE":"58","VN":"84","VG":"284","VI":"340"]
func countryName(countryCode: String) -> String? {
let current = Locale(identifier: "en_US")
return current.localizedString(forRegionCode: countryCode)
}
func flag(country:String) -> String {
let base : UInt32 = 127397
var flag = ""
for v in country.unicodeScalars {
flag.unicodeScalars.append(UnicodeScalar(base + v.value)!)
}
return flag
}
}
RegistrationView
//
// RegisterView.swift
// Instagram
//
// Created by Admin on 4/4/21.
//
import SwiftUI
import Combine
struct RegistrationView: View {
#State var isEditing: Bool = false
// navigate to verification view
#State private var selection: String? = nil
#State private var email:String = ""
#State private var phone:String = ""
#State private var fullname = ""
#State private var username = ""
#State private var password = ""
#State private var selectedImage:UIImage?
#State private var image:Image?
#Environment(\.presentationMode) var mode
#State var imagePickerPresented = false
#EnvironmentObject var viewModel: AuthViewModel
#State var isVerification = false
#State var phoneNumber = ""
#State var y : CGFloat = 350
#State var countryCode = ""
#State var countryFlag = ""
#State var isInputPhoneNumber = true
#State var isInputEmail = false
#State var tempData = DataTempo()
#State var isShowSignInButton = true
// var successClosure: (Bool) -> Void {
//
// }
#EnvironmentObject var mobileVerificationViewModel: MobileVerificationNvm
var body: some View {
ZStack {
LinearGradient(gradient: Gradient(colors: [Color.purple, Color("PrimaryColor")]), startPoint: .top, endPoint: .bottom)
.ignoresSafeArea()
VStack{
// Spacer()
VStack(spacing:20){
VStack(alignment:.leading){
Text("Sign Up")
.font(.largeTitle)
.bold()
.foregroundColor(.white)
}.padding(.horizontal,32).frame(width: UIScreen.main.bounds.width, alignment: .leading)
Toggle( isInputEmail ? "Use Email" : "Use Phone Number", isOn: $isInputEmail)
.foregroundColor(.white)
.toggleStyle(SwitchToggleStyle(tint: Color(#colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1))))
.padding(.horizontal,32)
if isInputEmail == true {
CustomTextField(text: $email, placeholder: Text("Email"), imageName: "envelope")
.padding()
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
.foregroundColor(.white)
.padding(.horizontal,32)
.onChange(of: email) {
// self.autocomplete($0) // like this
if viewModel.isValidEmail(testStr: $0){
print("email")
//phone number
}
}
}
if isInputEmail == false{
ZStack(alignment:.center) {
HStack (spacing: 0) {
Text(countryCode.isEmpty ? "🇹🇭 +66" : "\(countryFlag) +\(countryCode)")
.frame(width: 100, height: 50)
// .background(Color.secondary.opacity(0.2))
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
// .foregroundColor(countryCode.isEmpty ? .secondary : .black)
.foregroundColor(.white)
.onTapGesture {
isShowSignInButton = false
withAnimation (.spring()) {
self.y = 0
}
}
TextField("Phone Number", text: $phone)
.onChange(of: phone) { newValue in
if !phone.isEmpty {
self.isShowSignInButton = true
if viewModel.validatePhoneNumber(value:newValue){
print("phone")
//phone number
self.isInputPhoneNumber = true
self.isInputEmail = false
}
}
else{
self.isShowSignInButton = false
}
}
.frame(width: 250, height: 50)
.keyboardType(.phonePad)
.background(Color(.init(white: 1, alpha: 0.15)))
.foregroundColor(.white)
}
CountryCodes(countryCode: $countryCode, countryFlag: $countryFlag, y: $y)
.offset(y: y)
RoundedRectangle(cornerRadius: 10).stroke()
.frame(width: 350, height: 50)
.foregroundColor(.white)
} .zIndex(1).frame( height: 50, alignment: .center)
}
CustomTextField(text: $username, placeholder: Text("Username"), imageName: "person")
.padding()
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
.foregroundColor(.white)
.padding(.horizontal,32)
CustomTextField(text: $fullname, placeholder: Text("Full Name"), imageName: "person")
.padding()
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
.foregroundColor(.white)
.padding(.horizontal,32)
CustomSecureField(text: $password, placeholder: Text("Password"))
.padding()
.background(Color(.init(white: 1, alpha: 0.15)))
.cornerRadius(10)
.foregroundColor(.white)
.padding(.horizontal,32)
}
.alert(isPresented: $isInputPhoneNumber, content: {
Alert(title: Text("Message"), message: Text("you're using phone number to register"), dismissButton: .default(Text("Bye alert!"), action: {
isInputPhoneNumber = true
}))
})
.alert(isPresented: mobileVerificationViewModel.alertBinding) {
Alert(title: Text(mobileVerificationViewModel.errorMessage ?? "(unknown)"), message: nil, dismissButton: .cancel())
}
HStack{
Spacer()
Button(action: /*#START_MENU_TOKEN#*/{}/*#END_MENU_TOKEN#*/, label: {
Text("Forgot Password")
})
.font(.system(size: 13, weight: .semibold))
.foregroundColor(.white)
.padding(.top)
.padding(.trailing,28)
}
ZStack{
NavigationLink(destination:VerificationView { (pin, successClosure) in
print(pin)
mobileVerificationViewModel.codeVerification(phoneNumber: isInputEmail ? tempData.email : tempData.phone, pinCode: pin, email: email, password: password, fullname: fullname, username: username)
//navigation back
self.mode.wrappedValue.dismiss()
// print(successClosure)
}, tag: "verification", selection: $selection) { EmptyView() }
Button(action: {
self.selection = "verification"
var phoneWithCountryCode = ""
// if countryCode == "" {
// phoneWithCountryCode = "+66\(phone)"
// }
// if countryCode != ""{
// phoneWithCountryCode = "+\(countryCode)\(phone)"
// }
//
// // "+\(countryCode == "" ? "66" : countryCode + phone)"
// if (!email.isEmpty || !phone.isEmpty) && !password.isEmpty{
//
// if isInputEmail {
// MobileVerificationNvm.shared.sendVerification(phoneNumber:email)
// isVerification = true
// tempData.email = email
// }
//
// else{
// DispatchQueue.main.async {
// // print("phone:\(phoneWithCountryCode)")
// MobileVerificationNvm.shared.sendVerification(phoneNumber: phoneWithCountryCode)
// isVerification = true
// tempData.phone = phone
// }
//
// }
//
// }
// viewModel.register(withEmail: email, password: password,image: selectedImage,fullname: fullname,username: username)
}, label: {
Text("Sign Up")
}).zIndex(0)
// Spacer()
}
.font(.headline)
.frame(width: 360, height: 50)
.foregroundColor(.white)
.background(Color(#colorLiteral(red: 0.5568627715, green: 0.3529411852, blue: 0.9686274529, alpha: 1)))
// .clipShape(Capsule())
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
.padding()
.alert(isPresented: $isInputEmail, content: {
Alert(title: Text("Message"), message: Text("Now! you're using Email to register but you also register with email just swtich on togle button"), dismissButton: .default(Text("Bye alert!"), action: {
isInputEmail = true
}))
})
Button(action: {
mode.wrappedValue.dismiss()
// isVerification = true
}, label: {
Text("Already have an account? ")
.font(.system(size: 14))
+ Text("Sign In")
.font(.system(size: 14, weight: .bold))
})
.foregroundColor(.white)
.padding(.bottom,32)
}
}
.ignoresSafeArea()
}
}
extension RegistrationView{
func loadImage() {
guard let selectedImage = selectedImage else {
return
}
image = Image(uiImage: selectedImage)
}
}
//
struct RegistrationView_Previews: PreviewProvider {
static var previews: some View {
RegistrationView()
}
}
this is my issue when I tap on country code to choose the country code you will see the button sign in over countries code list
you also can check from [here]:github.com/PhanithNoch/SwiftUIPhoneTextField this is the repo that I tried to separate code from my real project as an example
I stuck with this a few days ago hope anyone can help, please.

Related

Swift: reload view after midnight or when a widget refreshes?

My app uses CoreData to store one quote for each day. There's a widget, the timeline of which is set to update after midnight and fetch the quote for that particular date. This works fine. However, I noticed that when I click on the widget with the new day's quote, the app - which has remained in the background - still shows the previous day's quote. Removing the app from the list of open ones and reopening it shows the correct information.
Is there a way to force a refresh of the DailyQuoteView and make sure that there isn't leftover data?
EDIT: Here's the daily quote struct that I'd like to always show the relevant data when brought from the background.
import SwiftUI
struct DailyQuoteView: View {
#Environment(\.scenePhase) var scenePhase
#State var id: UUID = .init()
#EnvironmentObject var unsplashModel: UnsplashModel
let managedObjectContext = CoreDataStack.shared.managedObjectContext
#FetchRequest(
sortDescriptors: [NSSortDescriptor(key: "date", ascending: true)],
predicate: NSPredicate(
format: "date >= %# AND date <= %#",
argumentArray: [
Calendar.current.startOfDay(for: Date()),
Calendar.current.date(byAdding: .minute, value: 1439, to: Calendar.current.startOfDay(for: Date()))!
]
)
) var quote: FetchedResults<QuoteEntity>
#FetchRequest(
sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
) var quotes: FetchedResults<QuoteEntity>
#State var shouldPresentSheet = false
#State var animateCard = false
#Binding var cardOffset: Double
#Binding var coverImageSize: Double
#State var showAboutScreen = false
// MARK: - ELEMENTS
var coverImage: some View {
Image(uiImage: UIImage(data: quote.first?.image ?? Data()) ?? UIImage())
.resizable()
.scaledToFill()
}
var aboutButton: some View {
Button {
showAboutScreen.toggle()
} label: {
Image(systemName: "info")
.foregroundColor(.primary)
.padding(10)
.background(Circle().fill(Color("background")))
.font(.title3)
}
.padding()
.shadow(radius: 5)
}
var titleView: some View {
Text(quote.first?.title ?? "")
.font(.largeTitle)
.multilineTextAlignment(.center)
.bold()
.minimumScaleFactor(0.5)
}
var contentsBox: some View {
ScrollView {
VStack (alignment: .leading, spacing: 10) {
Text(quote.first?.summary ?? "")
.font(.body)
.multilineTextAlignment(.leading)
.padding(.bottom, 20)
if let username = quote.first?.imageUserName {
HStack(spacing: 0) {
Text("Photo: ")
Link(
destination: URL(string: "https://unsplash.com/#\(username)?utm_source=quote_of_the_day&utm_medium=referral")!,
label: {
Text((quote.first?.imageUser)!)
.underline()
})
Text(" on ")
Link(
destination: URL(string: "https://unsplash.com/")!,
label: {
Text("Unsplash")
.underline()
})
Spacer()
}
.font(.caption)
.italic()
.foregroundColor(.gray)
.padding(.vertical, 3)
}
}
.font(.subheadline)
}
}
var buttonRow: some View {
HStack {
Menu(
content: {
Button {
shouldPresentSheet.toggle()
} label: {
Text("as image")
Image(systemName: "photo")
}
ShareLink(item: quote.first!.title, message: Text(quote.first!.summary)) {
Text("as text")
Image(systemName: "text.justify.left")
}
},
label: {
HStack {
Image(systemName: "square.and.arrow.up")
Text("Share")
}
.padding(10)
.background(RoundedRectangle(cornerRadius: 10).fill(Color("whitesand")))
.tint(Color(.secondaryLabel))
}
)
Spacer()
Link(destination: URL(string: quote.first!.link)!) {
Image(systemName: "book")
Text("Read more")
}
.padding(10)
.background(RoundedRectangle(cornerRadius: 10).fill(Color("vpurple")))
.tint(Color(.white))
}
.frame(height: 60)
.sheet(isPresented: $shouldPresentSheet) {
QuoteShare(quote: quote.first!)
}
}
var cardView: some View {
Rectangle()
.fill((Color("background")))
.cornerRadius(50, corners: [.topLeft, .topRight])
.overlay{
VStack{
titleView
.padding(.top, 30)
contentsBox
Spacer()
buttonRow
}
.padding(.horizontal, 20)
.frame(maxWidth: 600)
}
}
// MARK: - BODY
var body: some View {
ZStack{
GeometryReader{ proxy in
coverImage
.ignoresSafeArea()
.frame(width: UIScreen.main.bounds.width, height: proxy.size.height * (animateCard ? 0.30 : 1.0))
.animation(.easeInOut(duration: 1), value: animateCard)
}
GeometryReader { proxy in
aboutButton
.frame(width: 30, height: 30)
.position(x: proxy.frame(in: .global).maxX - 30, y: proxy.frame(in: .global).minY)
}
GeometryReader{ proxy in
VStack(spacing: 0){
Spacer()
cardView
.frame(height: proxy.size.height * 0.8)
.offset(y: animateCard ? 0 : cardOffset)
.animation(.spring().delay(0.2), value: animateCard)
}
}
}
.id(id)
.blur(radius: shouldPresentSheet ? 5.0 : 0.0)
.brightness(shouldPresentSheet ? -0.2 : 0.0)
.animation(.easeIn, value: shouldPresentSheet)
.onAppear {
if !imagesLoaded {
unsplashModel.loadData(imageCount: quotes.count)
imagesLoaded = true
}
animateCard = true
}
.onChange(of: unsplashModel.dailyImageData) { _ in
if unsplashModel.dailyImageData.count == quotes.count {
updateDailyImages()
}
}
.onChange(of: scenePhase, perform: { newValue in
if newValue == .active {
id = .init()
}
})
.task {
NotificationManager.shared.scheduleNotifications(notificationText: quote.first?.title ?? "")
}
}
}
EDIT 2: Code updated with lorem ipsum's suggestion below (.id and onChange of scenePhase). However, this seems to interfere with the card animation in .onAppear { withAnimation { animateCard = true } }

Change variable in other class and update view

First: Iam new to swift/swiftui
Problem: trying to update a published var:String in a observableOjbect class from a function. That var is used to for a switch later in a view.
I followed some inputs i found on my google research, but it doesnt work for me and i cant figure out why.
I tried #ObservedObjects, #EnvironmentObject, #StateObject, but i cant figure out how the string can be changed - in the view it always stays "x". getBotResponse(message: messageText) is called from another view / textField. My goal is to let the bot answer text, or with prepared buttons or images. depending on the input, the string should change to a "keyword" which is used as switch in the view to show other "view" modules i prepared.
class ChatBotVariables: ObesrvableObject{
#Published var answerCase: String = "x"
#Published var showFurtherContent: Bool = false
}
func getBotResponse(message: String) -> String {
let tempMessage = message.lowercased()
#ObservedObject var tempAnswerCase = ChatBotVariables()
if tempMessage.contains("textA"){
tempAnswerCase.answerCase = "text"
return "some text for BotAnswer"
}else if tempMessage.contains("textA"){
tempAnswerCase.answerCase = "video"
tempAnswerCase.showFurtherContent.toggle()
return "some other text for BotAnswer"
}else{
tempAnswerCase.answerCase = "questionbox"
tempAnswerCase.showFurtherContent.toggle()
return "something else"
}
}
struct ChatBot: View {
//happyBar
#State var _ratio = 60.0;
#State var _start : Bool = false;
#State private var messageText = ""
#State var messages: [String] = [""]
#State public var messageProperty = "Y"
#State private var botWriting = false
#State private var textInputEnabled = false
#ObservedObject var vm = MainMessagesViewModel() //reference to MMVM
#StateObject var cbl = ChatBotVariables()
#State var _case: String = "text"
//&#EnvironmentObject var cbl: ChatBotVariables
//#StateObject var showFurtherContent: Bool = false
init() {
initChatbot()
}
var body: some View {
NavigationView {
VStack {
ScrollView{
ForEach(messages, id: \.self){ message in
if message.contains("[USER]"){
let newMessage = message.replacingOccurrences(of:"[USER]", with: "")
HStack{
Spacer()
Text(newMessage)
.padding()
.foregroundColor(Color.white)
.background(.blue.opacity(0.8))
.cornerRadius(10)
.padding(.horizontal, 16)
.padding(.bottom, 10)
}
}else{
HStack{
Text(message)
.padding()
.background(.gray.opacity(0.15))
.cornerRadius(10)
.padding(.horizontal, 16)
.padding(.bottom, 10)
Spacer()
}
}
}.rotationEffect(.degrees(180))
if cbl.showFurtherContent {
switch cbl.$answerCase{
case "emoji":
print("asdf4")
break;
case "video":
print("asdf2")
break;
case "yesno":
print("asdf")
break;
default:
()
break;
}
cbl.showFurtherContent.toggle()
}
}.rotationEffect(.degrees(180))
.background(Color.gray.opacity(0.10))
if(messages.count > 1){
let mCount = messages.count
Text("count: \(mCount) answer: \(cbl.answerCase) Property: \(messageProperty)")
}
HStack {
if botWriting{
Image(systemName: "ellipsis.bubble.fill")
.font(.system(size: 26))
.foregroundColor(Color.blue)
}
}
Spacer()
// Show / Hide Text input bar
if textInputEnabled{
VStack{
HStack{
TextField("Text eingeben", text: $messageText)
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
.onSubmit{
sendMessage(message: messageText)
//switchHappyBar() // Debug
}
.padding(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0))
Button{
sendMessage(message: messageText)
//switchHappyBar() // Debug
}label:{
Image(systemName: "paperplane.fill")
}
.font(.system(size: 26))
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 20))
}
HStack{
Button{
// Show Options For text
}label:{
Image(systemName: "questionmark.circle")
}
.font(.system(size: 26))
.padding(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0))
Spacer()
Button{
textInputEnabled.toggle()
}label:{
Image(systemName: "keyboard.chevron.compact.down")
}
.font(.system(size: 26))
.padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 50))
Spacer()
}
}
}else{
HStack{
Button{
textInputEnabled.toggle()
}label:{
Image(systemName: "keyboard")
}
.font(.system(size: 26))
}
}
}
.navigationBarHidden(true)
}
}
func initChatbot(){
messages.append("Hallo \(vm.chatUser?.email ?? ""), wie geht es dir heute INIT?")
}
func sendMessage(message: String){
withAnimation{
messages.append("[USER]" + message)
self.messageText = ""
botWriting.toggle()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1){
withAnimation{
messages.append(getBotResponse(message: message))
botWriting.toggle()
}
}
}
}

How to append something to a list?

I'm new to Swift at the moment and I'm having trouble appending to a to-do list. I am not receiving an error, so I don't know what's wrong. I hope this isn't too much code to go through, but I don't even know where the problem is.
There is a sheet modal that opens and closes fine. The only issue is that when I press the save button, the information I typed in doesn't add to the list.
I've add the ViewModel, NewTaskView(sheet), TaskView(customizes list), and a bit of code of the DetailView where the list should be.
import Foundation
import SwiftUI
class TaskViewModel : Identifiable , ObservableObject {
#Published var tasks : [Task] = [
Task(taskName: "Lab", taskDate: Date(), taskCompleted: false),
Task(taskName: "Assignment 4.02", taskDate: Date(), taskCompleted: false)
]
#Published var sortType : SortType = .alphabetical
#Published var isPresented = false
#Published var searched = ""
func addTask(task : Task){
tasks.append(task)
}
func removeTask(indexAt : IndexSet){
tasks.remove(atOffsets: indexAt)
}
func sort(){
switch sortType {
case .alphabetical :
tasks.sort(by: { $0.taskName < $1.taskName })
case .date :
tasks.sort(by: { $0.taskDate > $1.taskDate })
}
}
}
struct Task : Identifiable , Equatable {
var id : String = UUID().uuidString
let taskName : String
let taskDate : Date
var taskCompleted: Bool
}
enum SortType : String , Identifiable , CaseIterable {
var id : String { rawValue }
case alphabetical
case date
}
struct NewTaskView: View {
#Environment(\.presentationMode) var presentationMode
#ObservedObject var taskVM : TaskViewModel
#State var taskName = ""
#State var taskCompleted = Bool()
#State var taskDate = Date()
var body: some View {
NavigationView {
VStack(spacing: 14) {
Spacer()
TextField("Assignment Name",text: $taskName)
.padding()
.background(Color("tan"))
.cornerRadius(5)
.font(Font.custom(FontNameManager.Montserrat.semibold, size: 15))
.padding(.bottom, 20)
HStack{
Text("Due Date")
.font(Font.custom(FontNameManager.Montserrat.semibold, size: 15))
Image(systemName: "bell.badge")
.foregroundColor(.gray)
.frame(width: 30, height: 30, alignment: .leading)
VStack{
DatePicker("Select Date", selection: $taskDate, displayedComponents: .date)
.labelsHidden()
}
}
HStack{
Text("Mark as Completed")
.font(Font.custom(FontNameManager.Montserrat.semibold, size: 15))
Button(action : {
taskCompleted.toggle()
}){
if taskCompleted == true {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(Color("orange"))
.font(.title2)
}
else {
Image(systemName: "circle")
.foregroundColor(Color("lightorange"))
.font(.title2)
}
}
}
Spacer()
Button(action:{taskVM.addTask(task: .init(taskName: taskName, taskDate: taskDate, taskCompleted: taskCompleted))
presentationMode.wrappedValue.dismiss()},
label:{
Text("Save")
})
}.padding()
.navigationBarItems(trailing: Button(action: {
presentationMode.wrappedValue.dismiss()
}) {
HStack{
Image(systemName: "xmark")
.foregroundColor(Color("orange"))
}
})
}
}
}
struct NewTaskView_Previews: PreviewProvider {
static var previews: some View {
NewTaskView(taskVM: TaskViewModel())
}
}
struct TaskView: View {
var task : Task
#Environment(\.managedObjectContext) var moc
#State var currentDate: Date = Date()
#State var taskCompleted = false
func getDateFormatString(date:Date) -> String
{
var dateString = ""
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM. dd"
dateString = dateFormatter.string(from: date)
return dateString
}
var body: some View {
HStack {
Button(action : {
self.taskCompleted.toggle()
try? self.moc.save()
}){
if self.taskCompleted {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(Color("orange"))
.font(.title2)
}
else {
Image(systemName: "circle")
.foregroundColor(Color("lightorange"))
.font(.title2)
}
}.padding()
VStack (alignment: .leading, spacing: 2){
Text("\(task.taskName)")
.font(Font.custom(FontNameManager.Montserrat.bold, size: 18))
.foregroundColor(Color("dark"))
.padding([.leading, .trailing], 1)
if self.taskCompleted {
Text("Completed")
.font(Font.custom(FontNameManager.Montserrat.bold, size: 12))
.foregroundColor(Color("burntorange"))
.frame(width: 95, height: 20)
.background(Color("lightorange"))
.cornerRadius(4)
}
else if currentDate > task.taskDate {
Text("Late")
.font(Font.custom(FontNameManager.Montserrat.semibold, size: 12))
.foregroundColor(.white)
.frame(width: 95, height: 18)
.background(Color("lightbrown"))
.cornerRadius(4)
}
else {
let diffs = Calendar.current.dateComponents([.day], from: currentDate, to: task.taskDate )
Text("\(diffs.day!) days left")
.font(Font.custom(FontNameManager.Montserrat.semibold, size: 12))
.foregroundColor(Color("burntorange"))
.frame(width: 95, height: 20)
.background(Color("lightorange"))
.cornerRadius(4)
}
}
Spacer()
Text(getDateFormatString(date: task.taskDate ))
.font(Font.custom(FontNameManager.Montserrat.medium, size: 12))
.padding()
}
.padding(10)
.cornerRadius(10)
.background(
RoundedRectangle(cornerRadius: 10 , style: .continuous)
.foregroundColor(.white))
}
}
struct TaskView_Previews: PreviewProvider {
static var previews: some View {
TaskView(task: Task(id: "", taskName: "Task Name", taskDate: Date(), taskCompleted: false))
}
}
struct DetailsView: View {
#Environment(\.managedObjectContext) var moc
#ObservedObject var developers : Developer
#ObservedObject var taskVM : TaskViewModel
#Environment(\.presentationMode) var presen
#Environment(\.editMode) var editButton
#State var taskCompleted = false
#State var show = false
#State var showSearch = false
#State var txt = ""
#State var currentDate: Date = Date()
#State var image : Data = .init(count: 0)
#State var indices : [Int] = []
func getDateFormatString(date:Date) -> String
{
var dateString = ""
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM. dd"
dateString = dateFormatter.string(from: date)
return dateString
}
var body: some View {
NavigationView {
ZStack {
ZStack{
VStack{
Color("lightorange")
.clipShape(CustomCorners(corners: [.bottomLeft, .bottomRight], size: 70))
.ignoresSafeArea(.all, edges: .top)
.frame(width: 420, height: 175)
Spacer()
}
VStack{
HStack {
if !self.showSearch{
Button(action: {
withAnimation(.spring()){
self.presen.wrappedValue.dismiss()
}
}) {
Image(systemName: "chevron.left")
.font(.title2)
.foregroundColor(Color("dark"))
.padding(10)
}
}
Spacer(minLength: 0)
HStack {
if self.showSearch{
Image(systemName: "magnifyingglass")
.font(.title2)
.foregroundColor(Color("dark"))
.padding(.horizontal, 8)
TextField("Search", text: $taskVM.searched , onEditingChanged: { (isBegin) in
if isBegin {
showSearch = true
} else {
showSearch = false
}
})
Button(action: {
withAnimation{
self.showSearch.toggle()
}
}) {
Image(systemName: "xmark").foregroundColor(.black).padding(.horizontal, 8)
}
}
else {
Button(action: {
withAnimation {
self.showSearch.toggle()
}
}) {
Image(systemName: "magnifyingglass")
.font(.title2)
.foregroundColor(Color("dark"))
.padding(10)
}
}
}.padding(self.showSearch ? 10 : 0)
.background(Color("lightorange"))
.cornerRadius(20)
}
ZStack{
RoundedRectangle(cornerRadius: 25)
.foregroundColor(Color("tan"))
.frame(width: 335, height: 130)
HStack {
VStack (alignment: .leading){
// Image(uiImage: UIImage(data: developers.imageD ?? self.image)!)
// .resizable()
// .frame(width: 70, height: 70)
// .clipShape(RoundedRectangle(cornerRadius: 20))
Text("\(developers.username ?? "")")
.font(Font.custom(FontNameManager.Montserrat.bold, size: 24))
.foregroundColor(Color("dark"))
Text("\(developers.descriptions ?? "")")
.font(Font.custom(FontNameManager.Montserrat.semibold, size: 18))
.foregroundColor(.gray)
}
.padding([.leading, .trailing], 70)
.padding(.bottom, 80)
Spacer()
}
}
HStack{
Text("Assignments")
.font(Font.custom(FontNameManager.Montserrat.bold, size: 20))
Spacer(minLength: 0)
EditButton()
}
.padding([.leading, .trailing], 20)
Spacer()
List {
ForEach (taskVM.tasks.filter {
self.taskVM.searched.isEmpty ? true : $0.taskName.localizedCapitalized.contains(self.taskVM.searched)} ){ task in
TaskView(task: task)
.listRowSeparator(.hidden)
}
.onDelete(perform: {
taskVM.removeTask(indexAt: $0)
})
}
.listStyle(InsetListStyle())
}
}
VStack {
Spacer()
HStack {
Spacer()
ExpandableFAB(taskVM: TaskViewModel(), show: $show)
}
}
}.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
}
}
}
struct DetailsView_Previews: PreviewProvider {
static let persistenceController = PersistenceController.shared
static var developers: Developer = {
let context = persistenceController.container.viewContext
let developers = Developer(context: context)
developers.username = "Math"
developers.descriptions = "Calculus"
return developers
}()
static var previews: some View {
DetailsView(developers: developers, taskVM: TaskViewModel()).environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
struct ExpandableFAB: View {
#Environment(\.managedObjectContext) var moc
#ObservedObject var taskVM : TaskViewModel
#Binding var show: Bool
var body: some View{
VStack(spacing: 20){
if self.show{
Button(action: {
taskVM.isPresented.toggle()
}) {
Image(systemName: "list.bullet.rectangle.portrait").resizable().frame(width: 25, height: 25).padding()
}
.foregroundColor(.white)
.background(Color("orange"))
.clipShape(Circle())
Button(action: {
self.show.toggle()
}) {
Image(systemName: "doc.badge.plus").resizable().frame(width: 25, height: 25).padding()
}.foregroundColor(.white)
.background(Color("orange"))
.clipShape(Circle())
}
Button(action: {
self.show.toggle()
}) {
Image(systemName: "plus").resizable().frame(width: 25, height: 25).padding()
}.foregroundColor(.white)
.background(Color("orange"))
.clipShape(RoundedRectangle(cornerRadius: 20))
.padding()
.rotationEffect(.init(degrees: self.show ? 180 : 0))
}.fullScreenCover(isPresented: $taskVM.isPresented, content: {
NewTaskView(taskVM: taskVM)
})
}
}
#burnsi I don't think I have that anywhere. Where should there be one?
Of course you have one and you need one:
ExpandableFAB(taskVM: TaskViewModel(), show: $show)
This line is causing the issue.
You are somehow injecting a TaskViewModel in your DetailsView (You are not showing where), but not using it. Try:
ExpandableFAB(taskVM: taskVM, show: $show)
And the place you create your ViewModel ('TaskViewModel()') should have a #StateObject wrapper.
Explanation:
As you add something to your ViewModel the views depending on the ViewModel get rebuild. So your TaskViewModel ends up beeing recreated and you still have your 2 initial values in your array.

How to show data from a ObservableObject in Swiftui

iam trying to make an small Social Media App with SwiftUI.
I creat a ObservableObject class called user and an Sign up view where the var get there value. When iam tying to show the Data in my ProfilView a Error happens.
Thread 1: Fatal error: No ObservableObject of type User found. A View.environmentObject(_:) for User may be missing as an ancestor of this view.
Home is the Main View where i switch between the views with a bar when an Button is pressed a bool change in AppInformation.
struct ProfileView: View {
#EnvironmentObject var appUser: User
#State var beschreibung: String = ""
#State var benutzername: String = ""
#State var name: String = ""
var body: some View {
init() {
benutzername = appUser.username
name = appUser.name
}
ZStack {
Rectangle()
.frame(width: 400, height: 720)
.cornerRadius(50)
.foregroundColor(.gray)
.overlay(
HStack {
Image(systemName: "person.circle")
.resizable()
.frame(width: 100, height: 100)
.onTapGesture {
print("pressed")
}
.padding(20)
.overlay(
ZStack{
Rectangle()
.frame(width: 20, height: 20)
.offset(x: 35, y: -35)
.foregroundColor(.white)
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 30, height: 30)
.offset(x: 35, y: -35)
.foregroundColor(.blue)
}
)
VStack {
Text(benutzername)
.font(.largeTitle)
.frame(width: 240 ,alignment: .leading)
.offset(x: -10, y: -25)
.lineLimit(1)
Text(name)
.frame(width: 220, alignment: .leading)
.offset(x: -15,y: -20)
.lineLimit(1)
}
}
.frame(width: 400, height: 720, alignment: .topLeading)
)
.padding()
ZStack {
Rectangle()
.foregroundColor(.secondary)
.frame(width: 380, height: 510)
.cornerRadius(45)
}
.frame(width: 400, height: 700, alignment: .bottom)
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
}
}
class User: ObservableObject{
#Published var username: String = ""
#Published var name: String = ""
var password: String = ""
#Published var email: String = ""
#Published var beschreibung: String = ""
}
#State var isSecured: Bool = true
#State var noPassword: Int = 0
#State var noEmail: Int = 0
#State var noUsername: Int = 0
#State var noName: Int = 0
#State var password: String = ""
#State var username: String = ""
#State var name: String = ""
#State var email: String = ""
#EnvironmentObject var appInfo: AppInformation
#EnvironmentObject var appUser: User
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 25)
.frame(width: 380, height: 650)
.overlay(Text("Erstelle Accout ")
.foregroundColor(.white)
.frame(width: 360, height: 510, alignment: .top)
.font(.headline))
ZStack {
VStack {
TextField("Username", text: $username)
.frame(width:180 ,height:40 ,alignment: .center)
.multilineTextAlignment(.center)
.font(.headline)
.background(Color.gray.opacity(0.25))
.clipShape(Capsule())
.foregroundColor(.white)
.border(.red, width: CGFloat(noUsername))
Spacer()
.frame(height: 35)
TextField("Full name", text: $name)
.frame(width:180 ,height:40 ,alignment: .center)
.multilineTextAlignment(.center)
.font(.headline)
.background(Color.gray.opacity(0.25))
.clipShape(Capsule())
.foregroundColor(.white)
.border(.red, width: CGFloat(noName))
Spacer()
.frame(height: 35)
TextField("Email", text: $email)
.frame(width:180 ,height:40 ,alignment: .center)
.multilineTextAlignment(.center)
.font(.headline)
.background(Color.gray.opacity(0.25))
.clipShape(Capsule())
.foregroundColor(.white)
.border(.red, width: CGFloat(noEmail))
Spacer()
.frame(height: 35)
HStack {
Spacer()
.frame(width: 37)
if isSecured {
SecureField("Password", text: $password)
.frame(width:180 ,height:40 ,alignment: .center)
.multilineTextAlignment(.center)
.font(.headline)
.background(Color.gray.opacity(0.25))
.clipShape(Capsule())
.foregroundColor(.white)
.border(.red, width: CGFloat(noPassword))
} else {
TextField("Password", text: $password)
.frame(width:180 ,height:40 ,alignment: .center)
.multilineTextAlignment(.center)
.font(.headline)
.background(Color.gray.opacity(0.25))
.clipShape(Capsule())
.foregroundColor(.white)
.border(.red, width: CGFloat(noPassword))
}
ZStack {
Button(" ") {
isSecured.toggle()
print(isSecured)
}
Image(systemName: self.isSecured ? "eye" : "eye.slash")
.foregroundColor(.gray)
}
}
Spacer()
.frame(height: 60)
Button("Erstellen"){
if username == "" {
noUsername = 1
if name == "" {
noName = 1
if email == "" {
noEmail = 1
if password == "" {
noPassword = 1
}
}
else if password == "" {
noPassword = 1
}
}
else if email == "" {
noEmail = 1
if password == "" {
noPassword = 1
}
}
else if password == "" {
noPassword = 1
}
}
else if name == "" {
noName = 1
if email == "" {
noEmail = 1
if password == "" {
noPassword = 1
}
}
else if password == "" {
noPassword = 1
}
}
else if email == "" {
noEmail = 1
if password == "" {
noPassword = 1
}
}
else if password == "" {
noPassword = 1
}
else {
appUser.username = username
appUser.email = email
appUser.password = password
appUser.name = name
appInfo.finished = true
}
}
.font(.headline)
.frame(width: 150, height: 50)
.background(.blue)
.foregroundColor(.white)
.clipShape(Capsule())
}
}
}
}
}
class AppInformation: ObservableObject{
#Published var home: Bool = true
#Published var camera: Bool = false
#Published var friends: Bool = false
#Published var profil: Bool = false
#Published var showBar: Bool = true
#Published var finished: Bool = false
}
struct Home: View {
#EnvironmentObject var appInfo: AppInformation
#EnvironmentObject var appUser: User
var body: some View {
ZStack {
if appInfo.finished {
if appInfo.home {
HomeView()
}
else if appInfo.camera {
MakePostView()
}
else if appInfo.friends {
FriendsView()
}
else if appInfo.profil {
ProfileView()
.environmentObject(appUser)
}
if appInfo.showBar {
NavigationBar()
}
}
else {
ErstellenView().environmentObject(appInfo)
.environmentObject(appUser)
}
}
}
}
In ProfileView you must not have init() inside the view body.
Remove init() completely, and instead, use appUser.username and appUser.name directly, such as:
Text(appUser.username) and Text(appUser.name) . This is the purpose of using a User ObservableObject model. There is no need to create
local variables. When you try to access appUser in init() the EnvironmentObject will not be ready, it will be ...missing as an ancestor of this view. In other words, you should not use EnvironmentObject in init().
User should be a struct and make it a published property on the appInfo object. We usually only have one environment object holding the model structs and we usually call it model rather than appInfo.
Most of your existing bools in appInfo should be in an #State var bool in the View struct and related ones could be together in an #State var struct and you can use mutating funcs to manipulate the data.

iOS | SwiftUI | Undesirable result to exchange items between TimerView and TaskView through ContentView, #State isn't delivering results

I have three Views exchanging information, ContentView, TimerView and TaskView. I used #Binding in TaskView to bring data from TaskView to ContentView, now I want to use that data to pass into TimerView.
I created a #State variable in ContentView to store the data from TaskView, but when I try to use that #State variable to pass data to TimerView it isn't giving me any desirable results.
Now if you see the TasksView call in ContentView, I get the output for the print statement, but in the main TimerView, it doesn't change from the default I set as #State variable in timerInfo.
Any help is appreciated, thank you.
Code for ContentView ->
//
// ContentView.swift
// ProDActivity
//
// Created by Vivek Pattanaik on 5/27/21.
//
import SwiftUI
struct TimerInfo : Identifiable {
let id = UUID()
// let taskIndex : Int
// let taskProjectName : String
var timerTaskName : String
var timerMinutes : Float
let timerIntervals : Int
var timerPriority : String
var timerShortbreakMinute : Float
var timerLongbreakMinute : Float
var timerLongbreakInterval : Int
}
struct ContentView: View {
init() {
UITabBar.appearance().backgroundColor = UIColor.init(Color("TabBar "))
}
// #State var selection: Tab = .dasboard
#State var timerInfo = TimerInfo(timerTaskName: "Sample Task 1", timerMinutes: 30, timerIntervals: 10, timerPriority: "High Priority", timerShortbreakMinute: 5, timerLongbreakMinute: 15, timerLongbreakInterval: 3)
var body: some View {
TabView {
TimerView(defaultTimeRemaining: self.timerInfo.timerMinutes * 60, timeRemaining: self.timerInfo.timerMinutes * 60)
.tabItem {
Image(systemName: "clock.fill")
Text("Timer")
}.tag(0)
TasksView(didClickTimer: { info in
self.timerInfo.timerTaskName = info.timerTaskName
self.timerInfo.timerMinutes = info.timerMinutes
self.timerInfo.timerPriority = info.timerPriority
self.timerInfo.timerShortbreakMinute = info.timerShortbreakMinute
self.timerInfo.timerLongbreakMinute = info.timerLongbreakMinute
self.timerInfo.timerLongbreakInterval = info.timerLongbreakInterval
print("\(self.timerInfo.timerMinutes)ContentView")
})
.tabItem {
Image(systemName: "doc.plaintext.fill")
Text("Tasks")
}.tag(1)
StatisticsView()
.tabItem {
Image(systemName: "chart.pie.fill")
Text("Statistics")
}.tag(3)
SettingsView()
.tabItem {
Image(systemName: "gearshape.fill")
Text("Settings")
}.tag(4)
}
.font(.headline)
.accentColor(Color("AccentColor"))
.environment(\.colorScheme, .dark)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Code for TasksView ->
//
// TasksView.swift
// ProDActivity
//
// Created by Vivek Pattanaik on 5/30/21.
//
import SwiftUI
struct TaskLabels : Identifiable {
let id = UUID()
// let taskIndex : Int
// let taskProjectName : String
let taskName : String
let taskPriority : String
let taskIntervals : String
let taskMinutes : String
let shortBreakMinutes : String
let longBreakMinutes : String
let longBreakIntervals : String
}
struct TaskRow: View {
let tasks : TaskLabels
var body: some View {
HStack(alignment: .center, spacing: 10) {
Image(tasks.taskPriority)
.frame(width: 40, height: 40, alignment: .center)
VStack(alignment: .leading, spacing:4){
Text(tasks.taskName)
.font(.system(size: 15))
Text("\(tasks.shortBreakMinutes) Min Breaks")
.font(.system(size: 13))
}
.frame(width: 100, height: 20, alignment: .leading)
Spacer()
VStack(alignment: .trailing, spacing:4){
Text("0/\(tasks.taskIntervals)")
Text("\(tasks.taskMinutes) Min Tasks")
.font(.system(size: 13))
}
.frame(width: 90, height: 20, alignment: .trailing)
.padding()
}
}
}
struct TasksView: View {
// #Binding var timeSelected : Float
#State var addTasksModalView: Bool = false
#State var taskLabels : [TaskLabels] = []
var didClickTimer : (TimerInfo) -> ()
var body: some View {
NavigationView{
if taskLabels.count != 0 {
List{
ForEach(taskLabels) { task in
HStack {
Button(action: {
self.didClickTimer(.init(timerTaskName: task.taskName, timerMinutes: Float(task.taskMinutes)!, timerIntervals: Int(task.taskIntervals)!, timerPriority: task.taskPriority, timerShortbreakMinute: Float(task.shortBreakMinutes)!, timerLongbreakMinute: Float(task.longBreakMinutes)!, timerLongbreakInterval: Int(task.longBreakIntervals)!))
print(task.taskMinutes)
}, label: {
TaskRow(tasks: task)
})
}
}
.onDelete(perform: self.deleteRow)
}
.navigationBarTitle("Tasks")
.navigationBarItems(trailing: Button(action: {
self.addTasksModalView = true
}, label: {
Image(systemName: "plus.square.on.square")
.resizable()
.frame(width: 26, height: 26, alignment: .leading)
.foregroundColor(Color.accentColor)
}))
.sheet(isPresented: $addTasksModalView, content: {
AddTasks(addTaskPresented: $addTasksModalView) { tasks in
taskLabels.append(tasks)
}
})
} else {
Text("")
.navigationTitle("Tasks")
.navigationBarTitle("Tasks")
.navigationBarItems(trailing: Button(action: {
self.addTasksModalView = true
}, label: {
Image(systemName: "plus.square.on.square")
.resizable()
.frame(width: 26, height: 26, alignment: .leading)
.foregroundColor(Color.accentColor)
}))
.sheet(isPresented: $addTasksModalView, content: {
AddTasks(addTaskPresented: $addTasksModalView) { tasks in
taskLabels.append(tasks)
}
})
}
}
.environment(\.colorScheme, .dark)
}
private func deleteRow(at indexSet: IndexSet){
self.taskLabels.remove(atOffsets: indexSet)
}
}
//struct TasksView_Previews: PreviewProvider {
// static var previews: some View {
// TasksView()
// }
//}
TimerView Code ->
//
// TimerView.swift
// ProDActivity
//
// Created by Vivek Pattanaik on 5/27/21.
//
import SwiftUI
struct TimerView: View {
let lineWidth : CGFloat = 11
let radius : CGFloat = 150
// to pass into the struct
// #State var taskTime = 300
#State var defaultTimeRemaining : Float
#State var timeRemaining : Float
#State private var isActive = false
#State private var showButtons = false
#State private var stopAlert = false
#State private var pausePressed = false
#State private var stopPressed = false
// #State private var timeRemainingSeconds : CGFloat = 25
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
var body: some View {
// self.defaultTimeRemaining = timerInfo.timerMinutes
VStack(spacing : 60) {
ZStack{
RoundedRectangle(cornerRadius: 7)
.frame(width: 300, height: 70)
.foregroundColor(Color("TabBar "))
}
ZStack(alignment: Alignment(horizontal: .center, vertical: .center)) {
Circle()
.stroke(Color.gray, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round))
.opacity(0.2)
Circle()
.trim(from: 1 - CGFloat(((defaultTimeRemaining-timeRemaining)/defaultTimeRemaining)), to: 1 )
.stroke(Color.accentColor, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round))
.rotationEffect( .degrees(-90))
.animation(.easeInOut)
// VStack for the timer and seesions
VStack {
// Text("\(Int(timeRemaining)):\(Int(timeRemainingSeconds))")
Text("\(timeString(time: Int(timeRemaining)))")
.font(.system(size: 50)).fontWeight(.medium)
Text("0 of 5 Sessions")
.font(.system(size: 20)).fontWeight(.medium)
}
}.frame(width: radius*2, height: radius*2)
// BEGIN, STOP, PAUSE BUTTONS
HStack(spacing: 25){
if showButtons == false {
Button(action: {
}, label: {
ZStack {
Rectangle()
.frame(width: 176, height: 55, alignment: .center)
.foregroundColor(Color.accentColor)
.cornerRadius(5)
Button(action: {
self.showButtons.toggle()
isActive.toggle()
}, label: {
Text("BEGIN")
.foregroundColor(Color.white)
.font(.system(size: 23).weight(.medium))
.frame(width: 176, height: 55, alignment: .center)
})
}
})
} else if showButtons == true {
HStack {
ZStack {
Rectangle()
.frame(width: 152, height: 55, alignment: .center)
.foregroundColor(Color("LightDark"))
.cornerRadius(5)
.border(Color.accentColor, width: 2)
Button(action: {
self.stopPressed.toggle()
self.pausePressed = true
if isActive == true {
isActive.toggle()
self.stopAlert = true
} else {
self.stopAlert = true
}
}, label: {
Text("STOP")
.foregroundColor(Color.accentColor)
.font(.system(size: 23).weight(.medium))
.frame(width: 152, height: 55, alignment: .center)
})
.alert(isPresented: $stopAlert) {
Alert(title: Text("Are you sure you want to stop?"),
message: Text("This will stop the timer and task associated with it."),
primaryButton: .destructive(Text("Yes"), action: {
self.showButtons = false
timeRemaining = defaultTimeRemaining
}),
secondaryButton: .cancel({
isActive = false
pausePressed = true
})
)
}
}
ZStack {
Rectangle()
.frame(width: 152, height: 55, alignment: .center)
.foregroundColor(pausePressed ? Color.accentColor : Color("LightDark"))
.cornerRadius(5)
.border(pausePressed ? Color.accentColor : Color.accentColor, width: 2)
Button(action: {
pausePressed.toggle()
if pausePressed == true {
isActive = false
} else {
isActive = true
}
}, label: {
Text("\(pausePressed ? "RESUME" : "PAUSE")")
.foregroundColor(pausePressed ? Color("TabBar ") : Color.accentColor)
.font(.system(size: 23).weight(.medium))
.frame(width: 152, height: 55, alignment: .center)
})
}
}
}
}
}.onReceive(timer, perform: { _ in
guard isActive else {return}
if timeRemaining > 0 {
timeRemaining -= 1
} else {
isActive = false
showButtons = false
self.timer.upstream.connect().cancel()
}
})
}
func timeString(time: Int) -> String {
let minutes = Int(time) / 60 % 60
let seconds = Int(time) % 60
return String(format:"%02i:%02i", minutes, seconds)
}
}
//struct TimerView_Previews: PreviewProvider {
// static var previews: some View {
// TimerView()
// }
//}
//func buttonChange(){
//
//}