I'm new to swift and I'm trying to develop a Velocity Calculator.
Here is my Code:
struct VelocityCalc: View {
#State var velocityNumbers1 : [String] = []
var body: some View {
VStack {
VStack {
Text("Headline")
TextField("e.g., 1, 3, 5, 8,...", text: $velocityNumbers1)
Button {
print("Button works")
} label: {
Text("Tap me")
}
}
}
}
What I want to develop is that the User can type in for example: 12, 14, 12, 10, ...
This Numbers needs to be sorted and so on.
Maybe someone can help me with this Issue or give me some advisory for that.
Big thanks for your help :)
I have seen answers, however what I have found out that when you enter the numbers the way you showed us on your question ex: 2, 1, 5, 9 with Space or WhiteSpace it won't work as expected so here it is a solution to overcome this problem:
#State var velocityNumbers = ""
func reorderTheArray(velocity: String) -> [String] {
let orderVelocity = velocity.components(separatedBy: ",").compactMap{
Int($0.trimmingCharacters(in: .whitespaces))
}
return orderVelocity.sorted().compactMap {
String($0)
}
}
var body: some View {
VStack {
Text("Headline")
TextField("example", text: self.$velocityNumbers)
Button(action: {
self.velocityNumbers = reorderTheArray(velocity: self.velocityNumbers).joined(separator: ",")
print(self.velocityNumbers)
}) {
Text("Reorder")
}
}
}
Now when you click the Reorder button, everything will be reordered on your textfield directly.
Try something like this,
Get the numbers as a string
Split them using separator(')
Convert them into Int and sort
struct ContentView: View {
#State var velocityNumber : String = ""
var body: some View {
VStack {
VStack {
Text("Headline")
TextField("e.g., 1, 3, 5, 8,...", text: $velocityNumber)
Button {
let allNumbers = velocityNumber.split(separator: ",").compactMap {
Int($0)
}
print(allNumbers.sorted())
} label: {
Text("Tap me")
}
}
}
}
}
I would see it like this:
First i would take all numbers as a string, then split the string using the separator ",", then convert all strings to an int array and sort
struct VelocityCalc: View {
#State var velocityNumbers1 : String
var body: some View {
VStack {
VStack {
Text("Headline")
TextField("e.g., 1, 3, 5, 8,...", text: $velocityNumbers1)
Button {
let velocityNumbersArray = velocityNumbers1
.components(separatedBy: ",")
.map { Int($0)! }.sorted()
print(velocityNumbersArray)
} label: {
Text("Tap me")
}
}
}
}
}
I think it makes more sense to enter one value at a time and use a separate field to display the entered values
#State var velocityNumbers : [Int] = []
#State var velocity: String = ""
var body: some View {
VStack {
VStack {
Text("Headline")
TextField("velocity", text: $velocity)
Button {
if let value = Int(velocity) {
velocityNumbers.append(value)
velocityNumbers.sort()
}
velocity = ""
} label: {
Text("Add")
}
.keyboardShortcut(.defaultAction)
Divider()
Text(velocityNumbers.map(String.init).joined(separator: ", "))
}
}
}
Related
I'm currently trying to change the data the picker will display based on the value in the series text field. I'm not getting the picker to show up, I'm not getting any errors but I'm getting this warning "Non-constant range: not an integer range" for both the ForEach lines below.
struct ConveyorTracks: View {
#State private var series = ""
#State private var selectedMaterial = 0
#State private var selectedWidth = 0
#State private var positRack = false
let materials8500 = ["HP", "LF", "Steel"]
let widths8500 = ["3.25", "4", "6"]
let materials882 = ["HP", "LF", "PS", "PSX"]
let widths882 = ["3.25", "4.5", "6","7.5", "10", "12"]
var materials: [String] {
if series == "8500" {
return materials8500
} else if series == "882" {
return materials882
} else {
return []
}
}
var widths: [String] {
if series == "8500" {
return widths8500
} else if series == "882" {
return widths882
} else {
return []
}
}
var body: some View {
VStack(alignment: .leading) {
HStack {
Text("Series:")
TextField("Enter series", text: $series)
}.padding()
HStack {
Text("Material:")
Picker("Materials", selection: $selectedMaterial) {
ForEach(materials.indices) { index in
Text(self.materials[index])
}
}.pickerStyle(SegmentedPickerStyle())
}.padding()
HStack {
Text("Width:")
Picker("Widths", selection: $selectedWidth) {
ForEach(widths.indices) { index in
Text(self.widths[index])
}
}.pickerStyle(SegmentedPickerStyle())
}.padding()
HStack {
Text("Positive Rack:")
Toggle("", isOn: $positRack)
}.padding()
}
}
}
struct ConveyorTrack_Previews: PreviewProvider {
static var previews: some View {
ConveyorTracks()
}
}
I would like the pickers to change based on which value is input in the series text field, for both materials and width.
Perhaps pickers isn't the best choice, I am open to any suggestions.
Thanks!
ForEach(materials.indices)
Needs to be
ForEach(materials.indices, id: \.self)
Because you are not using a compile-time constant in ForEach.
In general for fixed selections like this your code can be much simpler if you make everything enums, and make the enums Identifiable. This simplified example only shows one set of materials but you could return an array of applicable materials depending on the selected series (which could also be an enum?)
enum Material: Identifiable, CaseIterable {
case hp, lf, steel
var id: Material { self }
var title: String {
... return an appropriate title
}
}
#State var material: Material
...
Picker("Material", selection: $material) {
ForEach(Material.allCases) {
Text($0.title)
}
}
My app uses TextFields everywhere to modify CoreData entities' String attributes. They work very poorly - typing a space or getting an auto correct event seems to make the cursor jump to the end of the window. Keystrokes are missed and the whole experience is laggy. TextEditors, on the other hand, work fine. The behavior doesn't appear on the simulator, only on (multiple) real devices.
What am I doing wrong here? Am I using TextFields wrong?
Code is below, it's basically the starter Xcode app with a "text: String?" attribute added to the "item" CoreData entity.
struct Detail: View {
#ObservedObject var item: Item
var body: some View {
VStack {
Form {
Section(content: {
TextField("Title", text: $item.text ?? "")
}, header: {
Text("TextField")
})
Section(content: {
TextEditor(text: $item.text ?? "")
}, header: {
Text("TextEditor")
})
}
}
}
}
// Optional binding used
func ??<T>(lhs: Binding<Optional<T>>, rhs: T) -> Binding<T> {
Binding(
get: { lhs.wrappedValue ?? rhs },
set: { lhs.wrappedValue = $0 }
)
}
Update:
I ended up just putting the TextFields into a subview and then writing their value back to the NSManagedObject via a binding every time the value changes.
I have no idea why, but this fixes the problem for me.
struct CustomTextField: View {
#Binding var string: String?
#State var localString: String
let prompt: String
init(string: Binding<String?>, prompt: String) {
_string = string
_localString = State(initialValue: string.wrappedValue ?? "")
self.prompt = prompt
}
var body: some View {
TextField(prompt, text: $localString, axis: .vertical)
.onChange(of: localString, perform: { _ in
string = localString
})
}
}
Example of using onSubmit, which does not cause CoreData to save the data on every input by the keyboard.
struct Detail: View {
#ObservedObject var item: Item
#State var text: String = "" // for starting with an empty textfield
// Alternative if you want the data from item:
// #State var text: String = item.text.wrappedValue // This only works if text is a binding.
var body: some View {
VStack {
Form {
Section(content: {
TextField("Title", text: $text)
.onSubmit {
item.text = self.text
}
}, header: {
Text("TextField")
})
Section(content: {
TextEditor(text: $text)
.onSubmit {
item.text = self.text
}
}, header: {
Text("TextEditor")
})
}
}
}
}
If that does not help, it would be nice to know how Item looks like.
I have created a form in SwiftUI, now I am trying to add a search bar and make the List searchable without system built NavigationView. I was wondering is there any possible way to do so?
One easy way is you can customise TextField in SwiftUI, via text field text to filter data.
struct Sample006: View {
#State var dataSource: [String] = ["1", "2", "3"]
#State var filterData: [String] = []
#State var filterText: String = ""
var body: some View {
VStack {
TextField("Input Text", text: self.$filterText)
.textFieldStyle(.roundedBorder)
.padding(.horizontal, 20)
Form {
ForEach(filterData, id: \.self) { data in
Text(data)
}
}
}
.onAppear {
reset()
}
.onChange(of: filterText) { newValue in
guard !newValue.isEmpty else {
reset()
return
}
filterData = dataSource.filter { text in
text == newValue
}
}
}
private func reset() {
filterData = dataSource
}
}
I have an array of some integer that represents
some Toggle button. I want to delete the integer from the array according to the toggle button switched off!
struct CleanData: View {
var numberArray = [101, 102, 109, 143]
#State var numToDelete: [Bool] = [true, true, true, true]
var body: some View {
ForEach(0 ..< numberArray.count) {
if !self.numToDelete[$0] {
numberArray.remove(at: $0)
//to save modified array permanently
}
}
return ForEach(0 ..< numberArray.count) {
Toggle("Days: \(self.numberArray[$0])", isOn: self.$numToDelete[$0])
.padding()
}
}
}
Or trying differently but no luck!
struct CleanData: View {
var numberArray = [101, 102, 109, 143]
var body: some View {
return ForEach(0 ..< numberArray.count) { num in
RowView(mID: num, numArray: self.numberArray)
}
}
}
struct RowView: View {
#State var isShow = true
var mID: Int
var numArray: [Int]
var body: some View {
if(!isShow) {
numArray.remove(at: mID)
//to save modified array permanently
}
return Toggle("Days: \(self.numArray[mID])", isOn: self.$isShow)
.padding()
}
}
Here is possible approach
struct CleanData: View {
#State private var numberArray = [101, 102, 109, 143]
#State private var numToDelete = [true, true, true, true]
var body: some View {
VStack {
ForEach(Array(numberArray.enumerated()), id: \.element.self) { (i, number) -> AnyView in
let toDelete = Binding<Bool>(get: { self.numToDelete[i] },
set: {
self.numToDelete[i] = $0
// delayed to give time for toggle animation
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.numberArray.remove(at: i)
self.numToDelete.remove(at: i)
}
}
)
return AnyView(Toggle("Days: \(number)", isOn: toDelete))
}
}.padding()
}
}
I am trying to have a Picker that shows which option is currently selected.
Try out the following code which correctly selects the right option but the picker does not show which option is selected:
import SwiftUI
struct ContentView: View {
#State var selectedIndex: Int = 0
let strings: [String] = {
var strings: [String] = []
for i in 0..<10 {
strings.append("\(i)")
}
return strings
}()
var body: some View {
NavigationView {
VStack {
Form {
Picker(selection: $selectedIndex,
label: Text("Selected string: \(strings[selectedIndex])")) {
ForEach(0..<strings.count) {
Text(self.strings[$0]).tag($0)
}
}
}
}
.navigationBarTitle("Form Picker",
displayMode: NavigationBarItem.TitleDisplayMode.inline)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Anyone know what could be wrong? It's observed using Xcode 11.1 and iOS 13.1
I created the simple picker I call "ListPicker" which should fit the bill. I've written it so it works well in a Form; if you need it outside of a Form you will have to tinker with it. If you see any way to improve the code, please add a comment; this is still a learning experience for all of us.
// MARK: - LIST PICKER (PUBLIC)
struct ListPicker<Content: View>: View {
#Binding var selectedItem: Int
var label: () -> Content
var data: [Any]
var selectedLabel: String {
selectedItem >= 0 ? "\(data[selectedItem])" : ""
}
var body: some View {
NavigationLink(destination: ListPickerContent(selectedItem: self.$selectedItem, data: self.data)) {
ListPickerLabel(label: self.label, value: "\(self.selectedLabel)")
}
}
}
// MARK: - INTERNAL
private struct ListPickerLabel<Content: View>: View {
let label: () -> Content
let value: String
var body: some View {
HStack(alignment: .center) {
self.label()
Spacer()
Text(value)
.padding(.leading, 8)
}
}
}
private struct ListPickerContentItem: View {
let label: String
let index: Int
let isSelected: Bool
var body: some View {
HStack {
Text(label)
Spacer()
if isSelected {
Image(systemName: "checkmark")
.foregroundColor(.accentColor)
}
}.background(Color.white) // so the entire row is selectable
}
}
private struct ListPickerContent: View {
#Environment(\.presentationMode) var presentationMode
#Binding var selectedItem: Int
var data: [Any]
var body: some View {
List {
ForEach(0..<data.count) { index in
ListPickerContentItem(label: "\(self.data[index])", index: index, isSelected: index == self.selectedItem).onTapGesture {
self.selectedItem = index
self.presentationMode.wrappedValue.dismiss()
}
}
}
}
}
Then you can use it like this:
#State var selectedCar: Int = 0
let cars = ["Jaguar", "Audi", "BMW", "Land Rover"]
Form {
ListPicker(
selectedItem: self.$selectedCar,
label: {
Text("Cars")
},
data: self.cars
)
}