I am trying to figure out why the amount value is not showing as empty when no value is entered for the currencyfield. How do I see what the default value is for #state var amount: Decimal?
I think if I can solve for this I can rework the modifiers for the Text value - "Balance"
#State var amount: Decimal?
ZStack(alignment: .leading){
CurrencyField("",value: Binding(get: {
mortgageAmount.map { NSDecimalNumber(decimal: $0) }
}, set: { number in mortgageAmount = number?.decimalValue}))
Text("Balance")
.foregroundColor(String(describing: amount).isEmpty ? Color(Design.TextDark!) : Color(Design.accent!))
.font(Font.custom(Design.PrimaryFont, size: 16))
.offset(y: String(describing: amount).isEmpty ? 0 :-25)
.scaleEffect(String(describing: amount).isEmpty ? 1: 0.8, anchor: .leading)
}
Try to use
.scaleEffect( amount == nil ? 1: 0.8, anchor: .leading)
String(describing: amount) will be equal to "nil" so it will not be an empty
Related
I have a dilution calculator that works with no issues. However, there is always a "0" over the placeholder in the textfield for Container Size and Dilution Ratio. I don't mind the "0", I actually want a "0" there. But I have to erase it every time I tap on the textfield to input a number. Even if I remove the placeholder it's still there. How do I make it so that I don't have to keep erasing the "0" every time I want to input a number but keep the placeholder.
struct CalculatorView: View {
#State private var containerSize = 0
#State private var dilutionRatio = 0
#State private var totalProduct = 0.0
#State private var totalWater = 0.0
#FocusState private var amountIsFocused: Bool
#FocusState private var focusedInput: Field?
func totalProductAmount() -> Double {
let firstValue = Double(containerSize)
let secondValue = Double(dilutionRatio + 1)
let totalProduct = Double(firstValue / secondValue)
return totalProduct
}
func totalWaterAmount() -> Double {
let firstValue = Double(containerSize)
let secondValue = Double(dilutionRatio + 1)
let totalProduct = Double(firstValue / secondValue)
let totalWater = Double(firstValue - totalProduct)
return totalWater
}
var body: some View {
NavigationView {
VStack(alignment: .center) {
Image("Logo")
.padding(.horizontal, 30)
HStack {
//Container Size
ZStack {
Image("Container Size (Oz)")
.padding(.vertical, -15)
TextField("", value: $containerSize, format: .number)
.frame(width: 200.0, height: 60.0)
.multilineTextAlignment(.center)
.font(Font.system(size: 50, design: .default))
.foregroundColor(.white)
.keyboardType(.decimalPad)
.focused($amountIsFocused)
.focused($focusedInput, equals: .containerSize)
}
}
//Dilution Ratio
ZStack {
Image("Dilution Ratio - 2")
.padding(.vertical, -10)
TextField("", value: $dilutionRatio, format: .number)
.frame(width: 200.0, height: 60.0)
.multilineTextAlignment(.center)
.font(Font.system(size: 50, design: .default))
.foregroundColor(.white)
.keyboardType(.decimalPad)
.focused($amountIsFocused)
.focused($focusedInput, equals: .dilutionRatio)
}
//Go Button
Button(action: {
totalProduct = totalProductAmount()
totalWater = totalWaterAmount()
amountIsFocused = false
}, label: {
Image("Go Button")
})
//Results
HStack{
ZStack {
Image("Total Product (Oz)")
Text("\(totalProduct, specifier: "%.1f")")
.font(Font.system(size: 60, design: .default))
.foregroundColor(.white)
}
ZStack {
Image("Total Water (Oz)")
Text("\(totalWater, specifier: "%.1f")")
.font(Font.system(size: 60, design: .default))
.foregroundColor(.white)
}
Make containerSize and dilutionRatio an optional Int with no default value.
#State private var containerSize: Int?
#State private var dilutionRatio: Int?
TextField("0", value: $containerSize ?? "", format: .number)
I’m working on a dilution calculator. I have it 98% working, however, I want it to work a certain way and I’m not sure to do that. This is my first app so I’m new at this.
So I want the user to be able to input the numbers and hit a button to get the calculation. I’ve been using #State and through my research and understanding, using that instantly updates any changes the user makes.
So how do I go about making the app wait till the user hits the “Go” button.
Hers my code so far.
#State private var ContainerSize = 0
#State private var DilutionRatio = 0
#State private var Go = ""
#State private var TotalProduct = 0.0
#State private var TotalWater = 0.0
#FocusState private var amountIsFocused: Bool
var totalProductAmount: Double {
let firstValue = Double(ContainerSize)
let secondValue = Double(DilutionRatio + 1)
let totalProduct = Double(firstValue / secondValue)
return totalProduct
}
var totalWaterAmount: Double {
let firstValue = Double(ContainerSize)
let secondValue = Double(DilutionRatio + 1)
let totalWater = Double(firstValue - secondValue)
return totalWater
}
//Container Size
ZStack {
Image("Container Size (Oz)")
.padding(.vertical, -15)
TextField("", value: $ContainerSize, format: .number)
.frame(width: 200.0, height: 60.0)
.multilineTextAlignment(.center)
.font(Font.system(size: 50, design: .default))
.keyboardType(.decimalPad)
.focused($amountIsFocused)
}
}
//Dilution Ratio
ZStack {
Image("Dilution Ratio - 2")
.padding(.vertical, -10)
TextField("", value: $DilutionRatio, format: .number)
.frame(width: 200.0, height: 60.0)
.multilineTextAlignment(.center)
.font(Font.system(size: 50, design: .default))
.keyboardType(.decimalPad)
.focused($amountIsFocused)
}
//Go Button
Button(action: {}, label: {
Image("Go Button")
})
//Results
HStack{
ZStack {
Image("Total Product (Oz)")
Text("\(totalProductAmount, specifier: "%.1f")")
.font(Font.system(size: 60, design: .default))
}
ZStack {
Image("Total Water (Oz)")
Text(totalWaterAmount, format: .number)
.font(Font.system(size: 60, design: .default))
}
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Spacer(
Button("Done") {
amountIsFocused = false
}
}
struct CalculatorIView_Previews: PreviewProvider {
static var previews: some View {
CalculatorIView()
}
}
The calculator works as is but I want the user to input numbers, hit the “Go” button, and the results are shown.
You can create func for calculation and call it from button action. You should remove calculated properties, var totalProductAmount: Double and var totalWaterAmount: Double and do the calculation inside the function. You can check the example below.
https://www.hackingwithswift.com/quick-start/swiftui/how-to-create-a-tappable-button
var body: some View{
Button(action: {
someCalculation()
}, label: {
Image("Go Button")
})
}
func someCalculation(){
// do some calculation and you can set #State variables or you can return some value. For example 'func someCalculation()->Double'
}
So far you do the calculation in calculated properties and display them directly. So they'll update every time one of their underlying #State values change.
If you only want to show results on button press, you should display your #State result vars, and update inside the button action.
Side note: property names should start lowercase.
struct ContentView: View {
// side note: var names should start lowerCase
#State private var containerSize = 0
#State private var dilutionRatio = 0
#State private var totalProduct = 0.0
#State private var totalWater = 0.0
// for clarity change calculations to funcs
func totalProductAmount() -> Double {
let firstValue = Double(containerSize)
let secondValue = Double(dilutionRatio + 1)
let totalProduct = Double(firstValue / secondValue)
return totalProduct
}
func totalWaterAmount() -> Double {
let firstValue = Double(containerSize)
let secondValue = Double(dilutionRatio + 1)
let totalWater = Double(firstValue - secondValue)
return totalWater
}
var body: some View {
VStack {
//Container Size
Text("Container Size (Oz)")
.padding(.vertical, -15)
TextField("", value: $containerSize, format: .number)
.frame(width: 200.0, height: 60.0)
.multilineTextAlignment(.center)
.font(Font.system(size: 50, design: .default))
.keyboardType(.decimalPad)
//Dilution Ratio
Text("Dilution Ratio - 2")
.padding(.vertical, -10)
TextField("", value: $dilutionRatio, format: .number)
.frame(width: 200.0, height: 60.0)
.multilineTextAlignment(.center)
.font(Font.system(size: 50, design: .default))
.keyboardType(.decimalPad)
//Go Button
Button(action: {
// Calculate here, and set State vars with results
totalProduct = totalProductAmount()
totalWater = totalWaterAmount()
}, label: {
Text("Go Button")
})
.buttonStyle(.borderedProminent)
.padding()
//Results
// Show the state vars, not the calculation vars!
HStack{
VStack {
Text("Total Product (Oz)")
Text("\(totalProduct, specifier: "%.1f")")
.font(Font.system(size: 60, design: .default))
}
VStack {
Text("Total Water (Oz)")
Text(totalWater, format: .number)
.font(Font.system(size: 60, design: .default))
}
}
}
}
}
I have two pickers on one screen of my app. What I want to achieve is when one selection is made in either picker, the selection updates in both pickers.
So, for example, if I choose 'heat pump' in the user system picker, the current system picker also updates to 'heat pump'. I would like for this situation to work in both directions.
Here is the first picker struct:
struct CurrentSystemPicker: View {
// Array of dummy data
let currentSystems: [String] = ["Air Conditioning", "Furnace", "Furance Air Conditioning", "Heat Pump"]
#State var selectedCurrentSystem: String = "Current System"
var body: some View {
Menu {
Picker("picker", selection: $selectedCurrentSystem) {
ForEach(currentSystems, id: \.self) { system in
Text(system).tag(system)
}
}
.labelsHidden()
.pickerStyle(InlinePickerStyle())
} label: {
HStack {
Rectangle()
.foregroundColor(Color(.systemBackground))
}
.fixedSize()
.frame(width: ESConstants().IS_IPAD ? 275 : 110, height: ESConstants().IS_IPAD ? 75 : 50)
.padding(.horizontal)
.background(Color.white)
.cornerRadius(ESConstants().IS_IPAD ? 15 : 10)
.overlay(
RoundedRectangle(
cornerRadius: ESConstants().IS_IPAD ? 15 : 10)
.stroke(Color.eSaverGray, lineWidth: 1))
.overlay(
Text("\(selectedCurrentSystem)"))
.foregroundColor(Color.black)
}
}
}
Here is the second picker struct:
struct UserSystemPicker: View {
// Array of dummy data
let userSystems: [String] = ["Air Conditioning", "Furnace", "Furance Air Conditioning", "Heat Pump"]
#State var selectedUserSystem: String = "Future system"
var body: some View {
Menu {
Picker("picker", selection: $selectedUserSystem) {
ForEach(userSystems, id: \.self) { system in
Text(system).tag(system)
}
}
.labelsHidden()
.pickerStyle(InlinePickerStyle())
} label: {
HStack {
Rectangle()
.foregroundColor(Color(.systemBackground))
}
.fixedSize()
.frame(width: ESConstants().IS_IPAD ? 275 : 110, height: ESConstants().IS_IPAD ? 75 : 50)
.padding(.horizontal)
.background(Color.white)
.cornerRadius(ESConstants().IS_IPAD ? 15 : 10)
.overlay(
RoundedRectangle(
cornerRadius: ESConstants().IS_IPAD ? 15 : 10)
.stroke(Color.eSaverGray, lineWidth: 1))
.overlay(
Text("\(selectedUserSystem)"))
.foregroundColor(Color.black)
}
}
}
Are these two structs exist in the same main view?
If they exist in the same main view, you can use #Binding inside both structs, with 1 #State variable in the main view for passing data between these two structs. example:
struct MainView: View {
#State var getData: String = ""
var body: some View {
VStack {
CurrentSystemPicker(bindingData: $getData)
UserSystemPicker(bindingData: $getData)
}
}
I am a real newbie in swift, but I am stuck at this problem...
I am building a picker to change an optical prescription, but I am not able to change SPH and CYL values (doubles), while AX works good. Any help?
Also the way that text comes out, I tried to add .format modifier just to show 2 decimals, but still no luck.
Thanks!
import SwiftUI
struct ContentView: View {
#State var selectedsph = 0.0
#State var selectedcyl = 0.0
#State var selectedax = 0
var sph : [Double] = Array(stride(from: -20.00, through: 20.00, by: 0.25))
var cyl : [Double] = Array(stride(from: -10.00, through: 10.00, by: 0.25))
var ax = [Int](0...180)
var body: some View {
GeometryReader { geometry in
VStack {
Spacer()
HStack(spacing:0) {
Picker(selection: self.$selectedsph, label: Text("")) {
ForEach(0 ..< self.sph.count) { index in
Text("Sph " + "\(self.sph[index])").tag(index)
}
}
.pickerStyle(.wheel)
.frame(width: geometry.size.width/3, height: 150)
.compositingGroup()
.clipped()
Picker(selection: self.$selectedcyl, label: Text("Picker")) {
ForEach(0 ..< self.cyl.count) { index in
Text("Cyl " + "\(self.cyl[index])").tag(index)
}
}
.pickerStyle(.wheel)
.frame(width: geometry.size.width/3.5, height: 150)
.compositingGroup()
.clipped()
Picker(selection: self.$selectedax, label: Text("Picker")) {
ForEach(0 ..< self.ax.count) { index in
Text("AX " + "\(self.ax[index])").tag(index)
}
}
.pickerStyle(.wheel)
.frame(width: geometry.size.width/3, height: 150)
.compositingGroup()
.clipped()
}.padding()
}
HStack(alignment: .center) {
Text("Occhio Destro: SF: \(selectedsph) Cyl: \(selectedcyl) Ax: \(selectedax)")
.fontWeight(.medium).multilineTextAlignment(.center).padding(.all)
}
Spacer()
}
}
}
The picker values are the indices of the selected values. Change your #State vars to hold Int:
#State var selectedsph = 0
#State var selectedcyl = 0
#State var selectedax = 0
And change your Text to use the indices to look up the values. Add specifier: "%.2f" to show just 2 decimal places:
Text("Occhio Destro: SF: \(sph[selectedsph], specifier: "%.2f") Cyl: \(cyl[selectedcyl], specifier: "%.2f") Ax: \(ax[selectedax])")
.fontWeight(.medium).multilineTextAlignment(.center).padding(.all)
So I am trying to build a custom look to a Text Input in SwiftUI.
I have attempting to do this with a few different tricks, and the one I came up with was using labels and i was going to hide a TextInput behind the labels and let it basically act like a textinput without actually being a text input.
I dont even know if this is possible. If not I need to go back to the drawing board.
This is what I want it to look like blank
Black numbers, with the custom spacing and font etc.
Once numbers start to be entered they turn white.
however as you can see, the textInput is visible. Is it possible to hide it?
import SwiftUI
struct CustomInput: View {
#State var id: String = " "
#State var label1: Character = Character(" ")
#State var label2: Character = Character(" ")
#State var label3: Character = Character(" ")
#State var label4: Character = Character(" ")
#State var label5: Character = Character(" ")
#State var label6: Character = Character(" ")
#State var label7: Character = Character(" ")
#State var label8: Character = Character(" ")
#State var label9: Character = Character(" ")
var body: some View {
ZStack {
Color.green
HStack(spacing: 15){
ForEach(0 ..< 9) { index in
Text(String(id[safe: index] ?? "0"))
.font(.custom("regular", size: 32))
.frame(height: 48)
.foregroundColor(id.count <= index ? .black : .white)
}
}
.frame(width: 311, height: 48)
TextField("", text: $id)
.frame(width: 311, height: 48)
}
.frame(width: 311, height: 48)
}
}
extension StringProtocol {
subscript(safe offset: Int) -> Character? {
guard 0 ..< count ~= offset else {
return nil
}
return self[index(startIndex, offsetBy: offset)]
}
}
Here is a workable solution for you to continue off of. Honestly your best bet is to probably use a UIViewRepresentable but I'll leave that up to you.
NOTE: You'll need to handle a few things here. It'll need a few possible errors to be handled.
Bug if you attempt to use a character, non-digit.
Disable Context Menu actions (Copy, Paste, etc..) I hid it by setting accent to .clear
Add your styling.
struct FirstView: View {
#State var numbers = [0, 0, 0, 0, 0, 0, 0, 0, 0]
#State var editedNumbers = ""
var body: some View {
ZStack {
HStack {
ForEach(0..<numbers.count) { index in
Text(String(numbers[index]))
.padding(.horizontal, 2)
.foregroundColor(numbers[index] != 0 ? .white : .black)
}
}
.frame(width: UIScreen.main.bounds.width * 0.8)
.padding(.vertical)
.background(Color.green)
TextField("", text: $editedNumbers)
.frame(width: UIScreen.main.bounds.width * 0.8)
.padding(.vertical)
.foregroundColor(.clear)
.accentColor(.clear)
.onChange(of: editedNumbers, perform: { value in
numbers = [0, 0, 0, 0, 0, 0, 0, 0, 0]
for (index, char) in editedNumbers.enumerated() {
if index >= 0 && index <= 8 {
numbers[index] = char.wholeNumberValue ?? 0
}
}
})
}
}
}