I'm trying to make a basic distance conversion app, and my solution is to convert the user input to meters first before converting it to their desired unit. However, I don't know how to implement it. How would I go about that?
struct ContentView: View {
#State private var inputNumber = ""
#State private var inputUnit = 0
#State private var outputUnit = 1
let inputUnits = ["Meters", "Kilometers", "Feet", "Yard", "Miles"]
let outputUnits = ["Meters", "Kilometers", "Feet", "Yard", "Miles"]
var conversions: Double {
//conversions computed here
}
var body: some View {
NavigationView {
Form {
Section {
TextField("Enter measurement", text: $inputNumber)
.keyboardType(.numberPad)
Picker("Your unit", selection: $inputUnit) {
ForEach(0 ..< inputUnits.count) {
Text("\(self.inputUnits[$0])")
}
}
}
Section(header: Text("What unit would you like to convert to?")) {
Picker("Convert to...", selection: $outputUnit) {
ForEach(0 ..< outputUnits.count) {
Text("\(self.outputUnits[$0])")
}
}
}
Section {
Text("\(meterToKM)")
}
}
.navigationBarTitle("How Far?")
}
}
}
You can do the conversion in a method, say calculateConvertedValue and invoke it in a button within your section and make changes to an #State property say convertedValue which then displays the final value in your Text View component. Here's an example:
struct ContentView: View {
#State private var inputNumber = ""
#State private var inputUnit = 0
#State private var outputUnit = 1
#State private var convertedValue = ""
let inputUnits = ["Meters", "Kilometers", "Feet", "Yard", "Miles"]
let outputUnits = ["Meters", "Kilometers", "Feet", "Yard", "Miles"]
var body: some View {
NavigationView {
Form {
Section {
TextField("Enter measurement", text: $inputNumber)
.keyboardType(.numberPad)
Picker("Your unit", selection: $inputUnit) {
ForEach(0 ..< inputUnits.count) {
Text("\(self.inputUnits[$0])")
}
}
}
Section(header: Text("What unit would you like to convert to?")) {
Picker("Convert to...", selection: $outputUnit) {
ForEach(0 ..< outputUnits.count) {
Text("\(self.outputUnits[$0])")
}
}
}
Section {
Button(action: {
self.calculateConversion()
}) { Text("Convert") }
Text(convertedValue)
}
}
.navigationBarTitle("How Far?")
}
}
func calculateConvertedValue() {
convertedValue = "New converted value."
}
}
Related
I am trying to show checkbox in foreach loop but when i click on any one of them all are selected.
How can we separate them.
struct Screen: View {
#State private var checked = true
var data = ["1","2", "3"]
var body: some View {
ForEach( data.indices, id:\.self ) { item in
HStack {
CheckBoxView(checked: $checked)
Text(data[item])
}
}
}
struct CheckBoxView: View {
#Binding var checked: Bool
var body: some View {
Image(systemName: checked ? "checkmark.square.fill" : "square")
.foregroundColor(checked ? Color(UIColor.systemBlue) : Color.secondary)
.onTapGesture {
self.checked.toggle()
}
}
}
Thank You for help.
The easiest way is to use an array of state variables:
struct Screen: View {
#State private var checked: [Bool] = [true, true, true]
var data = ["1","2", "3"]
var body: some View {
VStack {
ForEach( data.indices, id:\.self ) { index in
HStack {
CheckBoxView(checked: $checked[index])
Text(data[index])
}
}
}
}
struct CheckBoxView: View {
#Binding var checked: Bool
var body: some View {
Image(systemName: checked ? "checkmark.square.fill" : "square")
.foregroundColor(checked ? Color(UIColor.systemBlue) : Color.secondary)
.onTapGesture {
self.checked.toggle()
}
}
}
}
However, I personally do not like this solution, because the state array is not dynamic in size to your data. With this initialization your state array is always the same size as your data.
struct Screen: View {
var data = ["1","2", "3"]
#State private var checked: [Bool]
init() {
_checked = State(initialValue: [Bool](repeating: false, count: data.count))
}
var body: some View {
VStack {
ForEach( data.indices, id:\.self ) { index in
HStack {
CheckBoxView(checked: $checked[index])
Text(data[index])
}
}
}
}
struct CheckBoxView: View {
#Binding var checked: Bool
var body: some View {
Image(systemName: checked ? "checkmark.square.fill" : "square")
.foregroundColor(checked ? Color(UIColor.systemBlue) : Color.secondary)
.onTapGesture {
self.checked.toggle()
}
}
}
}
Here is an answer that will allow you to check one or more boxes, and keep track of which values were selected. It uses an .onChanged to keep track of the actual value that has been selected as the check box itself is just UI:
struct Screen: View {
var data = ["1","2","3"]
#State var selectedItems: Set<String> = [] // Use a Set to keep track of multiple check boxes
#State var selectedItems = "" // Use a String to keep track of only one.
var body: some View {
List {
ForEach( data, id:\.self ) { item in
CheckBoxRow(title:item, selectedItems: $selectedItems, isSelected: selectedItems.contains(item))
}
}
}
}
struct CheckBoxRow: View {
var title: String
#Binding var selectedItems: Set<String>
#State var isSelected: Bool
var body: some View {
GeometryReader { geometry in
HStack {
CheckBoxView(checked: $isSelected, title: title)
.onChange(of: isSelected) { _ in
if isSelected {
selectedItems.insert(title)// or
selectedItems = title
} else {
selectedItems.remove(title)// or
selectedItems = ""
}
}
}
}
}
}
struct CheckBoxView: View {
#Binding var checked: Bool
#State var title: String
var body: some View {
HStack {
Image(systemName: (checked ? "checkmark.square" : "square"))
Text(title)
.padding(.leading)
}
.onTapGesture {
self.checked.toggle()
}
}
}
First, sorry for my bad English! I'm absolutely new to SwiftUI and I tried to create a Quiz App with multiple Choices and multiple Answers. I created a Button with a ForEach to Display the possible answers. Now I want to select the correct Answer and tap the check Button to validate the chosen Answer. There can be more then 1 correct Answer.
I tried this function but its only return, if there are one or two
//MARK:- Funktionen
func checkAnswer() {
if validateAnswer == quiz.correctAnswer {
print("Richtig")
} else {
print("Falsch")
}
}
I have no idea how to validate the chosen Answers with the correct answers. Can anyone help me?
Here is my Code:
QuizModel
struct Quiz: Identifiable {
var id: String = UUID().uuidString
var question: String
var howManyAnswers: String
var options: [PossibleAnswer]
var correctAnswer: [String]
var explain: String
}
extension Quiz: Equatable {}
struct PossibleAnswer : Identifiable, Equatable {
let id = UUID()
let text : String
}
ContentView
struct ContentView: View {
var quiz: Quiz
#State var isChecked:Bool = false
#State private var showAlert: Bool = false
#State var validateAnswer: [String] = ["Antwort 3", "Antwort 4"]
//MARK:- Answers
VStack {
ForEach(quiz.options) { answerOption in
QuizButtonView(isChecked: isChecked, title: answerOption.text)
.foregroundColor(.black)
.padding(2.0)
}
Spacer()
Divider()
HStack {
//MARK:- Button Überprüfen & Zurück
Button(action: {
print("Ich gehe zurück")
}, label: {
Text("Zurück")
})
Button(action: {
checkAnswer()
print("Ich überprüfe...")
self.showAlert.toggle()
}, label: {
Text("Überprüfen")
})
.padding(.leading, 200)
And my CheckButtonView
struct QuizButtonView: View {
#State var isChecked:Bool = false
var title:String
func toggle(){
isChecked.toggle()
if self.isChecked == true {
print("Antwort wurde ausgewählt")
} else if self.isChecked == false {
print("Antwort wurde wieder abgewählt")
}
}
var body: some View {
VStack {
Button(action: toggle) {
HStack{
Text(title)
.font(.system(size: 16))
.foregroundColor(.black)
.lineLimit(3)
Spacer()
Image(systemName: isChecked ? "checkmark.square.fill": "square")
}
}
Thank you!
You just need to create a state variable of an array of booleans:
struct ContentView: View {
private let quiz: Quiz
#State private var userSelections: [Bool]
init(quiz: Quiz) {
self.quiz = quiz
_userSelections = State(initialValue: Array(repeating: false, count: quiz.options.count))
}
var body: some View {
VStack {
ForEach(0..<quiz.options.count) { index in
QuizButtonView(isChecked: userSelections[index], title: quiz.options[index].text)
.foregroundColor(.black)
.padding(2.0)
}
}
}
func checkAnswer() {
let userSelectionTexts = Set(userSelections.enumerated().map({ quiz.options[$0.offset].text }))
let correctAnswers = Set(quiz.correctAnswer)
let isAllSelectionsTrue = userSelectionTexts == correctAnswers
let isAllSelectionsFalse = userSelectionTexts.intersection(correctAnswers).isEmpty
let isAnySelectionsTrue = !isAllSelectionsFalse
}
}
I'm using SwiftUi version 2 for my application development. I'm facing issue with textfield available in SwiftUI. I don't want to use UITextField anymore. I want to limit the number of Characters in TextField. I searched a lot and i find some answer related to this but those answer doesn't work for SwiftUI version 2.
class textBindingManager: ObservableObject{
let characterLimit: Int
#Published var phoneNumber = "" {
didSet {
if phoneNumber.count > characterLimit && oldValue.count <= characterLimit {
phoneNumber = oldValue
}
}
}
init(limit: Int = 10) {
characterLimit = limit
}
}
struct ContentView: View {
#ObservedObject var textBindingManager = TextBindingManager(limit: 5)
var body: some View {
TextField("Placeholder", text: $textBindingManager.phoneNumber)
}
}
No need to use didSet on your published property. You can add a modifier to TextField and limit the string value to its prefix limited to the character limit:
import SwiftUI
struct ContentView: View {
#ObservedObject var textBindingManager = TextBindingManager(limit: 5)
var body: some View {
TextField("Placeholder", text: $textBindingManager.phoneNumber)
.padding()
.onChange(of: textBindingManager.phoneNumber, perform: editingChanged)
}
func editingChanged(_ value: String) {
textBindingManager.phoneNumber = String(value.prefix(textBindingManager.characterLimit))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
class TextBindingManager: ObservableObject {
let characterLimit: Int
#Published var phoneNumber = ""
init(limit: Int = 10){
characterLimit = limit
}
}
The following should be the simpliest. It limits the number of characters to 10.
struct ContentView: View {
#State var searchKey: String = ""
var body: some View {
TextField("Enter text", text: $searchKey)
.onChange(of: searchKey) { newValue in
if newValue.count > 10 {
self.searchKey = String(newValue.prefix(10))
}
}
}
}
This solution wraps everything up in a new Component. You could adapt this to perform other parsing / pattern checking quite easily.
struct ContentView : View {
#State private var myTextValue: String = ""
var body: some View {
LimitedTextField(value: $myTextValue, charLimit: 2)
}
}
struct LimitedTextField : View {
#State private var enteredString: String = ""
#Binding var underlyingString: String
let charLimit : Int
init(value: Binding<String>, charLimit: Int) {
_underlyingString = value
self.charLimit = charLimit
}
var body: some View {
HStack {
TextField("", text: $enteredString, onCommit: updateUnderlyingValue)
.onAppear(perform: { updateEnteredString(newUnderlyingString: underlyingString) })
.onChange(of: enteredString, perform: updateUndelyingString)
.onChange(of: underlyingString, perform: updateEnteredString)
}
}
func updateEnteredString(newUnderlyingString: String) {
enteredString = String(newUnderlyingString.prefix(charLimit))
}
func updateUndelyingString(newEnteredString: String) {
if newEnteredString.count > charLimit {
self.enteredString = String(newEnteredString.prefix(charLimit))
underlyingString = self.enteredString
}
}
func updateUnderlyingValue() {
underlyingString = enteredString
}
}
I'm new at Swift and I'm looking to make a unit converter app to test my skills. Here's my attempt at making a picker:
struct ContentView: View {
#State private var inputNumber = ""
#State private var inputUnit = 2
#State private var outputUnit = ""
let inputUnits = ["meters", "kilometers", "feet", "yard", "miles"]
let outputUnits = ["meters", "kilometers", "feet", "yard", "miles"]
var body: some View {
NavigationView {
Form {
Section {
TextField("Enter measurement", text: $inputNumber)
.keyboardType(.decimalPad)
Picker("Your unit", selection: $inputUnit) {
ForEach(0 ..< inputUnits.count) {
Text("\(self.inputUnits)")
}
You need to use the parameter to the closure, which is an index
Picker(selection: $inputUnit, label: Text("Your unit")) {
ForEach(0 ..< inputUnits.count) {
Text("\(self.inputUnits[$0])") //<- $0 is the index for your array
}
}
I'm trying to create a distance conversion app to learn Swift. I'm planning to create a method to convert all values. How would I start doing that?
If there are other ways I can convert my values, tips would be appreciated. Thanks.
struct ContentView: View {
#State private var inputNumber = ""
#State private var inputUnit = 0
#State private var outputUnit = 1
#State private var convertedValue = ""
let inputUnits = ["Meters", "Kilometers", "Feet", "Yard", "Miles"]
let outputUnits = ["Meters", "Kilometers", "Feet", "Yard", "Miles"]
var body: some View {
NavigationView {
Form {
Section {
TextField("Enter measurement", text: $inputNumber)
.keyboardType(.numberPad)
Picker("Your unit", selection: $inputUnit) {
ForEach(0 ..< inputUnits.count) {
Text("\(self.inputUnits[$0])")
}
}
}
Section(header: Text("What unit would you like to convert to?")) {
Picker("Convert to...", selection: $outputUnit) {
ForEach(0 ..< outputUnits.count) {
Text("\(self.outputUnits[$0])")
}
}
}
Section {
Button(action: {
self.calculateConversion()
}) { Text("Convert") }
Text(convertedValue)
}
}
.navigationBarTitle("How Far?")
}
}
func calculateConvertedValue() {
convertedValue = "New converted value."
}
}
I'd just make an Dictonary [String:Double] with all Units.
And just take 1 meter as the base value and for every other unit the corresponding value which converts to 1 meter.
And then in your method just divide the old value by the double value from the old unit in the dictionary and multiplied by the double value of the unit you want it in.