SwiftUI - Display Double rounded to 2 decimal places - double

I have a dollar ( monetary) amount that I'm trying to display using string interpolation in a Text element. Currently, it displays such as 9.9900000. I want the value just to display as 9.99.
struct ContentView: View {
var productPrice: Double = 9.99
var body: some View {
Text("\(productPrice)")
}
}

struct ContentView: View {
var productPrice: Double = 9.99
var body: some View {
Text("\(productPrice, specifier: "%.2f")")
}
}

Related

Dynamically sized #State var

I'm loading data into a struct from JSON. With this data, a new structure (itemStructures) is created and filled for use in a SwiftUI View. In this View I have a #State var which I manually initialise to have enough space to hold all items. This state var holds all parameters which are nested within the items, hence the nested array.
As long as this #State var has enough empty spaces everything works fine. But my question is, how do I modify this #State programmatically for when the number of items increases er decreases with the loading of a new JSON? I could make it really large but I'd rather have it the exact size after each load.
//Structs used in this example
struct MainViewState {
var itemStructures: [ItemStructure]
}
struct ItemStructure: Identifiable, Hashable {
var id: String {name}
var name: String
var parameters: [Parameter]
}
struct Parameter: Identifiable, Hashable {
var id: String {name}
var name: String
var value: Double
var range: [Double]
}
struct ContentView: View {
//In this model json is loaded, this seemed out of scope for this question to include this
#ObservedObject var viewModel: MainViewModel
//This is the #State var which should be dynamically allocated according to the content size of "itemStructures"
//For now 3 items with 10 parameters each are enough
#State var parametersPerItem: [[Float]] = [
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0]
]
init(viewModel: MainViewModel) {
self.viewModel = viewModel
}
var body: some View {
let itemStructures = viewModel.mainState.itemStructures
ForEach( Array(itemStructures.enumerated()), id: \.element ) { index, item in
Text(item.name)
ForEach( Array(item.parameters.enumerated()), id: \.element ) { i, parameter in
Text(parameter.name)
SliderView(
label: parameter.name,
value: Binding(
get: { self.parametersPerItem[index][i] },
set: { (newVal) in
self.parametersPerItem[index][i] = newVal
//Function to send slider values and ranges to real time processing
//doStuffWithRangesAndValues()
}
),
range: parameter.range,
showsLabel: false
).onAppear {
//Set initial value slider
parametersPerItem[index][i] = Float(parameter.value)
}
}
}
}
}
struct SliderView: View {
var label: String
#Binding var value: Float
var range: [Double]
var showsLabel: Bool
init(label: String, value: Binding<Float>, range: [Double], showsLabel: Bool = true) {
self.label = label
_value = value
self.range = range
self.showsLabel = showsLabel
}
var body: some View {
GeometryReader { geometry in
ZStack{
if showsLabel { Text(label) }
HStack {
Slider(value: $value)
.foregroundColor(.accentColor)
.frame(width: geometry.size.width * 0.8)
//In the real app range calculations are done here
let valueInRange = value
Text("\(valueInRange, specifier: range[1] >= 1000 ? "%.0f" : "%.2f")")
.foregroundColor(.white)
.font(.subheadline)
.frame(width: geometry.size.width * 0.2)
}
}
}
.frame(height: 40.0)
}
}
If you are looking for a solution where you want to initialise the array after the json has been loaded you could add a computed property in an extension to the main/root json model and use it to give the #State property an initial value.
extension MainViewState {
var parametersPerItem: [[Float]] {
var array: [[Float]] = []
if let max = itemStructures.map(\.parameters.count).max(by: { $0 < $1 }) {
for _ in itemStructures {
array.append(Array(repeating: 0.0, count: max))
}
}
return array
}
}

SwiftUI sheet shows sheet with wrong data

I have a list that displays 1, 2, and 3. When the text is tapped the app opens a sheet with the number tapped. However, if I tap the text in the second or third row, the number displayed in the sheet is still 1. What am I doing wrong?
import SwiftUI
struct ContentView: View {
var numbers = [1, 2, 3]
#State private var shouldPresentSheet = false
var body: some View {
List(self.numbers, id: \.self) { number in
Text("number: \(number)").sheet(isPresented: self.$shouldPresentSheet) {
Text("This is sheet number \(number)")
}.onTapGesture {
self.shouldPresentSheet.toggle()
}
}
}
}
You need to create one sheet instead of multiple sheets.
If you want to use .sheet(isPresented:content:) you can do the following:
struct ContentView: View {
var numbers = [1, 2, 3]
#State private var selectedNumber: Int?
var body: some View {
List(numbers, id: \.self) { number in
Text("number: \(number)")
.onTapGesture {
self.selectedNumber = number
}
}
.sheet(isPresented: binding) {
Text("This is a sheet number \(self.selectedNumber ?? 0)")
}
}
var binding: Binding<Bool> {
.init(
get: { self.selectedNumber != nil },
set: { _ in }
)
}
}
Alternatively you can use .sheet(item:content:):
struct Model: Identifiable {
let id: Int
}
struct ContentView: View {
var numbers = [1, 2, 3].map(Model.init)
#State private var selectedNumber: Model?
var body: some View {
List(numbers, id: \.id) { number in
Text("number: \(number.id)")
.onTapGesture {
self.selectedNumber = number
}
}
.sheet(item: $selectedNumber) { item in
Text("This is a sheet number \(item.id)")
}
}
}
EDIT
If you decide to use .sheet(item:content:) you can add an extension to Int (instead of creating a custom struct conforming to Identifiable):
extension Int: Identifiable {
public var id: Int { self }
}
as proposed in Asperi's answer.

How to raise number through while loop in SwiftUI View

I'm SwiftUI Beginner! I'm trying to add Auto Numbering
But, I can't do it..
I don't know how to raise number of variable x
I want to add through while loop or for loop!
import SwiftUI
struct ContentView : View{
#State var x : Int = 1;
var body : some View{
Text(x)
}
}
As #El Tomato has said you can use ForEach. Here's an example:
struct ContentView: View {
#State var numberOfRows: Int = 5
var body: some View {
ForEach(0..<numberOfRows) {
Text("\($0)")
}
}
}

How to pass function return to modal in SwiftUI

I only have one week experience with swift/programming so bear with me. I stripped down the app I am working on to simplify my problem. I am trying to pass the return values from my function grandCalc to my modal so that I can show the total and tip value on both my main view and my modal. I'm on Xcode 11.5, SwiftUI.
Here is the modal where I would like to show the tip value
struct ModalView: View {
var body: some View {
// Want to show tip value here
// Error: use of unresolved identifier grandCalc
Text("\(grandCalc().theTipValue)")
}
}
Here is all of the code. I could use a push in the right direction, thanks
import SwiftUI
struct ContentView: View {
#State var checkAmount = "10.00"
#State var tipValue = 15.0
#State var modalDisplayed = false
var body: some View {
NavigationView {
VStack {
Text("\(grandCalc().theGrandTotal)")
Text("\(grandCalc().theTipValue)")
Button(action: {
self.modalDisplayed.toggle()
}) {
Text("Details")
}
.sheet(isPresented: $modalDisplayed) {
ModalView()
}
}
}
}
func grandCalc() -> (theTipValue: Double, theGrandTotal: Double) {
let tipSelection = Double(tipValue)
let orderAmount = Double(checkAmount) ?? 0
let tipValue = orderAmount / 100 * tipSelection
let grandTotal = orderAmount + tipValue
return (tipValue, grandTotal)
}
}
struct ModalView: View {
var body: some View {
// Want to show tip value here
Text("\(grandCalc().theTipValue)")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Can i put multiple text lines in a single list section in SwiftUI?

I want to put multiple lines of text in one section of a list (before the first divider). The issue being any new text added gets its own new line.
Is there any way for me to put multiple lines in one list section? Here's my code:
struct ChestWorkouts: View {
#State var BPWeight: String = ""
#State var GoalBPWeight: String = ""
var body: some View {
List{
Text("Bench Press").fontWeight(.bold)
TextField("Goal Weight", text: $GoalBPWeight).keyboardType(.numberPad)
TextField("Weight", text: $BPWeight).keyboardType(.numberPad)
}.environment(\.defaultMinListRowHeight, 90)
You can put them in a VStack
struct ContentView: View {
#State var BPWeight: String = ""
#State var GoalBPWeight: String = ""
var body: some View {
List{
VStack {
Text("Bench Press").fontWeight(.bold)
TextField("Goal Weight", text: $GoalBPWeight).keyboardType(.numberPad)
TextField("Weight", text: $BPWeight).keyboardType(.numberPad)
}
}.environment(\.defaultMinListRowHeight, 90)
}
}