If you're getting input from a text field, you give a string as the input
but what if you want the input from the text field to be used later, in other lines in your program, as an Int, Double, CGfloat, etc?
how do I do that?
If you're getting input from a text field, you give a string as the input
but what if you want the input from the text field to be used later, in other lines in your program, as an Int, Double, CGfloat, etc?
how do I do that?
Hi @arduinolego611,
I've moved your question to "Using Swift" since it's not about developing the Swift compiler. Generally, questions about using Apple's UI frameworks are best asked over in Apple's Developer Forums.
However, your specific question is about converting strings to values of other types, which is very much a basic function of the programming language that we cover here on these forums. This is something that each destination type will decide for itself how to spell, but for standard library types like Int
and Double
, you can generally convert from a string value str
by writing Int(str)
or Double(str)
.
Pay attention to the preconditions for calling any such initializers. While some are failable initializers (i.e., they will return nil
if you didn't pass a value that can be converted), others will expect that you have first validated certain properties of your input string and can crash your program if you don't.
You will also want to pay attention to other nuances in the documentation as sometimes you'll get a converted value that isn't what you'd expect because some character in the string is interpreted in some particular way.
Good answer, but I might suggest using NumberFormatter
to convert user input text to a number (or vice versa). User input is generally localized (as is representing a number as a localized string). The Int(str)
, Double(str)
, etc., are not localized. Thus you might do things like:
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
guard
let input = self.textField.text,
let value = formatter.number(from: input)?.doubleValue
else {
return
}
// use `value` here
The key point is that different locales use differing “thousands” separators and “decimal point”. NumberFormatter
is the safe and idiomatic way of converting the text
from a text field to a numeric value, and back.
The above is the typical UIKit/AppKit way of dealing with this. In SwiftUI, you’d still use a number formatter, but the applicable syntax is slightly different. But the OP didn’t mention SwiftUI, so I’ll leave it at the above.
maybe someone could explain my errors
I have been looking at references for the syntax to unwrap this optional, can't really figure it out...I think also my conversion of string to double is wrong
or if there are more problems, I would appreciate the help
import SwiftUI
struct ContentView: View {
// @State private var xRadius = 150 // default values for optional
// @State private var yRadius = 75 // default values for optional
//
// @State private var: Double(xRadius2) = 0
// @State private var: Double(yRadius2) = 0
@State private var xRadiusInput: String? = " "
@State private var yRadiusInput: String? = " "
var xRadius: CGFloat
{
CGFloat(Double?(xRadiusInput) ?? 150)
}
var yRadius: CGFloat
{
CGFloat(Double?(yRadiusInput) ?? 75)
}
var speed: CGFloat = 0.5
var body: some View {
TimelineView(.animation) { timeline in
var speed = (xRadius + yRadius) / 100
let time = timeline.date.timeIntervalSinceReferenceDate
let angle = time * speed * 2 * .pi
let x = cos(angle) * xRadius //angle is where you are on the circle, cos and sin are the x
let y = sin(angle) * yRadius
Text("X dimension: , \(xRadius)")
TextField("Enter x dimension of elliptical orbit", text: $xRadiusInput)
.textFieldStyle(RoundedBorderTextFieldStyle())
Text("Y dimension: \(yRadius)")
TextField("Enter y dimension of elliptical orbit", text: $yRadiusInput)
.textFieldStyle(RoundedBorderTextFieldStyle())
ZStack {
Ellipse()
.stroke(Color.gray.opacity(0.3), lineWidth: 2)
.frame(width: xRadius * 2, height: yRadius * 2)
// .offset(x: 50, y: 3)
Circle()
.fill(Color.blue)
.frame(width: 30, height: 30)
.offset(x: x, y: y) // x / y as in lines 22 and 23
Circle()
.fill(Color.red)
.frame(width: 75, height: 75)
}
.frame(width: xRadius * 2 + 60, height: yRadius * 2 + 60)
}
}
}
#Preview {
ContentView()
}
I posted some code in my own post on this topic. Should I open it as a new topic???
I posted some code in my own post on this topic. Should I open it as a new topic?
If you are using SwiftUI, you would not use a String
for your @State
variable, but rather use a numeric type. And you might use TextField(_:value:format:prompt:)
with a format of .number
:
struct ContentView: View {
@State private var xRadius: Double = 150
@State private var yRadius: Double = 75
…
var body: some View {
TimelineView(.animation) { timeline in
…
Text("X dimension: \(xRadius, format: .number)")
TextField("Enter x dimension", value: $xRadius, format: .number)
Text("Y dimension: \(yRadius, format: .number)")
TextField("Enter y dimension", value: $yRadius, format: .number)
…
}
}
}
The above was introduced in iOS 15, macOS 12, etc.. In earlier OS versions, one would use a NumberFormatter
. In this case, one would use TextField(_:value:formatter:)
to bind that text field to a numeric value, using the NumberFormatter
to convert to and fro:
struct ContentView: View {
@State private var xRadius: Double = 150
@State private var yRadius: Double = 75
private let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
…
var body: some View {
TimelineView(.animation) { timeline in
…
Text("X dimension: \(xRadius, format: .number)")
TextField("Enter x dimension", value: $xRadius, formatter: formatter)
…
Text("Y dimension: \(yRadius, format: .number)")
TextField("Enter y dimension", value: $yRadius, formatter: formatter)
…
}
}
}
But either way, the underlying idea is the same, that you bind your TextField
directly to the numeric state variable using the value
renditions of the TextField
initializers. In both cases, the formatter will convert back and forth between the user’s text entry and the numeric value.
at this part: TimelineView(.animation) { timeline in
im now getting this error message:
Generic parameter 'Content' could not be inferred
so sorry but I am confused again
When you have a syntax error inside TimelineView
, the compiler can sometimes manifest that as you’ve outlined, rather than producing an error message at the actual offending line. We obviously cannot comment without seeing your implementation.
In case it helps you, here is an implementation that does not produce that error:
struct ContentView: View {
@State private var xRadius: Double = 150
@State private var yRadius: Double = 75
var speed: CGFloat = 0.5
var body: some View {
TimelineView(.animation) { timeline in
let speed = (xRadius + yRadius) / 100
let time = timeline.date.timeIntervalSinceReferenceDate
let angle = time * speed * 2 * .pi
let x = cos(angle) * xRadius // `angle` is where you are on the circle, `cos` and `sin` are the `x` and `y`, respectively
let y = sin(angle) * yRadius
Text("X dimension: \(xRadius, format: .number)")
TextField("Enter x dimension", value: $xRadius, format: .number)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Text("Y dimension: \(yRadius, format: .number)")
TextField("Enter y dimension", value: $yRadius, format: .number)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
ZStack {
Ellipse()
.stroke(Color.gray.opacity(0.3), lineWidth: 2)
.frame(width: xRadius * 2, height: yRadius * 2)
Circle()
.fill(Color.blue)
.frame(width: 30, height: 30)
.offset(x: x, y: y)
Circle()
.fill(Color.red)
.frame(width: 75, height: 75)
}
.frame(width: xRadius * 2 + 60, height: yRadius * 2 + 60)
}
}
}
FWIW, I find that this “generic parameter … could not be inferred” problem is diminished if I separate out the random computational code from the Content
of the TimelineView
. E.g., perhaps:
struct ContentView: View {
@State private var xRadius: Double = 150
@State private var yRadius: Double = 75
var speed: CGFloat = 0.5
var body: some View {
TimelineView(.animation) { timeline in
Text("X dimension: \(xRadius, format: .number)")
TextField("Enter x dimension", value: $xRadius, format: .number)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Text("Y dimension: \(yRadius, format: .number)")
TextField("Enter y dimension", value: $yRadius, format: .number)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
ZStack {
Ellipse()
.stroke(Color.gray.opacity(0.3), lineWidth: 2)
.frame(width: xRadius * 2, height: yRadius * 2)
Circle()
.fill(Color.blue)
.frame(width: 30, height: 30)
.offset(offset(at: timeline))
Circle()
.fill(Color.red)
.frame(width: 75, height: 75)
}
.frame(width: xRadius * 2 + 60, height: yRadius * 2 + 60)
}
}
private func offset(at timeline: TimelineViewDefaultContext) -> CGSize {
let speed = (xRadius + yRadius) / 100
let time = timeline.date.timeIntervalSinceReferenceDate
let angle = time * speed * 2 * .pi
let x = cos(angle) * xRadius //angle is where you are on the circle, cos and sin are the x
let y = sin(angle) * yRadius
return CGSize(width: x, height: y)
}
}
If I now change something that introduces an error, it is much better at identifying the source of the problem. I also think this is cleaner, but perhaps that is subjective. Anyway, this confusing error message is a known problem when mixing non-view code inside a @ViewBuilder
closure.