Change textfield to double with button click - ios

Im trying to use a textfield to take a string and then when the save button is clicked, it saves that value typed as a double using CoreData. But it keeps giving an error stating that "Value of optional type 'Double?' must be unwrapped to a value of type 'Double'" Any ideas?
hoursSlept is the variable the textfield is bonded to
newSleep is the variable for the struct SleepModel (my sleep type)
Im taking the textfield value and trying to force into a double using Double() with a new variable. But it keeps giving the error.
Button(action: {
var hoursSleptDouble = Double(hoursSlept)
newSleep!.hoursSlept = hoursSleptDouble
if newSleep != nil {
coreDataViewModel.saveRecord(sleepModel: newSleep!) {
print("Success")
}
}
})
{
Capsule()
.foregroundColor(Color.green)
.frame(height: 44)
.overlay(Text("Add Entry").foregroundColor(Color.white).bold())
}.padding()

Based on someones advise I switched the code up a bit. I initialized the sleepModel inside of a function which is called when the button is pushed. Then inside that function I create the doubles from the user input textfields. That way there are no optionals!

Related

How to cancel a change made to a projectedValue

So I have a view that allows a user to update a CoreData property.
They then have the option to cancel or save
The TextField is linked with the projectedValue of a CoreData class
TextField("New Value", text: $coreDataClass.value)
Its very nice that CoreData has been linked with Combine so that we can attach $ to get the two-way binding feature. However on this particular view is causing me some problems because if a user hits cancel after inserting any value then the value persists even after the view is dismissed.
How would I cancel the change if the user hits cancel... I thought using a lazy var would be perfect since its only read once but that didn't work. I thought about using a class to capture the initial value and then use it in the cancel method but that did not work either.
Any guidance on the issue would be greatly appreciated!
Answer for question in comment Unrelated
import SwiftUI
extension Text {
func buttonView(type: ButtonType) -> some View {
func selectColor(type: ButtonType) -> Color {
switch type {
case .default:
return .blue
case .destructive:
return .red
case .utility:
return .purple
}
}
return self.bold()
.foregroundColor(.white)
.padding(.horizontal)
.padding(.vertical, 5)
.background(selectColor(type: type))
.cornerRadius(8)
}
}
enum ButtonType {
case `default`
case destructive
case utility
}
As you may know, when you edit CoreData field, it's not getting saved to the database until you run viewContext.save().
In case you need to revert changes there's another one: viewContext.rollback()
But note, that it'll discard all changes made for all objects since the last save. So if you have many changes and wanna discard only a single one, you need to do it manually: in the init fill #State value with a value from your object field, and on Save write #State to your object field.

SwiftUI numberpad(TextField) fail to update the binding variable

I am trying to create a Textfield which allows user to enter information and update the binding variable. Generally, user needs to hit the "return" button after entering the data, so that the compiler will update the data. However, I am now using number pad which have no "return" button. Hence, the compiler fail to update the binding object. What should I do ?
Here is my code:
class UserSettings: ObservableObject {
#Published var systemCost = 0
}
#EnvironmentObject var usersettings: UserSettings
TextField("Please enter here", value: $usersettings.cost, formatter: NumberFormatter())
.keyboardType(.numberPad)
.textFieldStyle(RoundedBorderTextFieldStyle())
.font(.title)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color("DB"),lineWidth: 1))
The cost keep on showing 0 (where the initial value of cost is = 0) after entering a new value.
Thank you so much

SwiftUI: Hiding Text with an if statement if text value is nil or N/A

I am trying to do something that was simple to me in UIKit - but cannot get working in SwiftUI.
I am pulling data from an API. That data is dynamic - some parts of the call may not be present every time. I have protected the app by making all of that data optional and using a nil operator to return "N/A" in the text fields where there is no data. In UIKit I was able to simple use an if statement:
if self.cityLabel.text == "N/A" {
self.cityLabel.isHidden = true
}
Now in SwiftUI, I have the following:
HStack() {
Text(self.model?.city ?? "N/A")
}
When the data is present, it displays without any issue. However, I am not sure where to access this property or put an if statement because this data is in a View class and does not accept functions.
So basically, how do I hide that text and have the blocks around it "move up" (like display: none) in HTML while in SwiftUI? How can I implement if statements in the View code?
I'm sure it's probably simple, but assistance would be much appreciated! :)
if, if-let, and if-else now also work inside View (I think it's introduced in Xcode 12). This should work:
var body: some View {
HStack() {
if let city = self.model?.city {
Text(city)
}
}
}
Edit: If you don't care about what's inside self.model?.city, just do a simple boolean test for nil.
var body: some View {
HStack() {
if self.model?.city != nil {
/// self.model?.city is not nil!
Text("Not nil")
}
}
}
Edit 2: Ok, so if the text is "N/A", you don't want to display it. However, because the text itself (self.model?.city) is an optional, you need to unwrap it first.
var body: some View {
HStack() {
if let city = self.model?.city { /// unwrap self.model?.city
if city != "N/A" { /// city is not "N/A"!
/// show the text
Text(city)
} /// else, don't show anything
}
}
}

What does 'identity' mean in SwifUI and how do we change the 'identity' of something

I'm new to SwiftUI and I'm having problems with presenting Alerts back-to-back.
The description of the .alert(item:content:) modifier has this written in it's definition:
/// Presents an alert.
///
/// - Parameters:
/// - item: A `Binding` to an optional source of truth for the `Alert`.
/// When representing a non-nil item, the system uses `content` to
/// create an alert representation of the item.
///
/// If the identity changes, the system will dismiss a
/// currently-presented alert and replace it by a new alert.
///
/// - content: A closure returning the `Alert` to present.
public func alert<Item>(item: Binding<Item?>, content: (Item) -> Alert) -> some View where Item : Identifiable
I'm particularly interested in the If the identity changes, the system will dismiss a currently-presented alert and replace it by a new alert part. Since I want Alerts to be presented back-to-back, if I'm somehow able to change the 'identity', I'll be able to achieve the functionality that I want - which is having the system dismiss the currently-presented alert and replacing the old Alert with a new Alert (back-to-back).
If someone can explain to me what 'identity' is and how I can change the 'identity' of something I'll be extremely grateful.
(Or if you know a better way to present alerts back-to-back that'll also be very very helpful.)
Thanks in advance!
Find below demo of alert-by-item usage. And some investigation results about changing identity as documented.
As you find experimenting with example (by tap on row) alert activated by user interaction works fine, but changing identity programmatically, as documented, seems not stable yet, however alert is really updated.
Tested with Xcode 11.4 / iOS 13.4
struct SomeItem: Identifiable { // identifiable item
var id: Int // identity
}
struct DemoAlertOnItem: View {
#State private var selectedItem: SomeItem? = nil
var body: some View {
VStack {
ScrollView (.vertical, showsIndicators: false) {
ForEach (0..<5) { i in
Text("Item \(i)").padding()
.onTapGesture {
self.selectedItem = SomeItem(id: i)
// below simulate change identity while alert is shown
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.selectedItem = nil // works !!
// self.selectedItem?.id = 100 // crash !!
// self.selectedItem = SomeItem(id: 100) // crash !!
}
}
}
}
}
.alert(item: self.$selectedItem) { item in
Alert(title: Text("Alert"), message: Text("For item \(item.id)"), dismissButton: .default(Text("OK")))
}
}
}
Notice how this method requires a Binding<Item?>, and that Item should be Identifiable. For the Binding<Item?> parameter, you're supposed to pass in a "source of truth" that controls what the alert shown looks like, or whether the alert shows at all. When this source of truth changes (i.e. becomes something else), the view will update the alert.
But here's the problem, how does SwiftUI know what does "change" mean in the context of your model? Let's say Item is a Person class that you wrote. Person has a name and age. It is your job to tell SwiftUI, that "a Person becomes a totally different person when its name changes". (Of course, you could have some other definition of what is meant by "a person changes" here. This definition is just an example.)
struct Person : Identifiable {
var id: String {
name
}
let name: String
let age: Int
}
This is why Item must be Identifiable. Item.id is thus the "identity".
Note that Identifiable is different from Equatable, in that Identifiable asks the question "what makes this person a different person?" whereas Equatable asks "what result would you want == to give?". See this for another example.
how do we change the 'identity' of something?
Just change the binding you pass in (e.g. setting the #State that the binding is based on) in such a way that its id changes.

How can I connect a variable value to a slider?

I am trying to insert a value that continuously changes into a slider. I want to see the slider move on its own as this value changes.
I have a slider structure set up and I tried inserting that value I want in it but it is not moving. I know that my variable value is changing as I want it to.
struct SliderCounter : View {
#State var timer1 : Float
var body: some View {
return Slider(value: $timer1, in : 0...100, step:1)
.padding()
}
}
SliderCounter(timer1: Float(changingValue))
I am calling this from another file but the slider is only showing up and not moving as the value changes
Since you are using SliderCounter as a reusable view, you need to user #Binding as this reusable view does not own the data.

Resources