Swift UI multiple Picker with "empty" and 0...9 - swiftui

i want to create a picker with three numbers each from 0..9 and "empty" to select, and then getten these values liek number1 number2 and number3
found a way wo use with 0..9 but how can i use also a empty blank selection can have the value of empty
regards Alex

I have made a quick example below of how you would did this. It allows you to select any number, and the empty value is marked as nil. This should make it much easier to get a value from, rather than number1 number2 and number3, etc.
struct ContentView: View {
#State private var selection: Int?
private var formattedSelection: String {
selection != nil ? String(selection!) : "Empty"
}
var body: some View {
VStack {
Text("Selection: \(formattedSelection)")
Picker("Select number", selection: $selection) {
Text("Empty").tag(nil as Int?)
// Ranges from 0 to 9
ForEach(0 ..< 10) { value in
Text(String(value)).tag(value as Int?)
}
}
}
}
}

Related

SwiftUI Toggle Switch without Binding

This may seem like an odd question but I have looked around and can't seem to find an answer for it.
I would like to create toggles in a view that are not binded to any variable. Imagine a list of toggle switches that are toggle-able but don't actually do anything.
I have tried using .constant butt as you would expect, that doesn't allow me to toggle the switch. Obviously leaving It blank throws an error.
//Can't be changed
Toggle(isOn: .constant(true)) {
Text("Checkbox")
}
//Throws an error
Toggle() {
Text("Checkbox")
}
Is there anything that can be passed in the isOn: parameter to allow for that?
Edit:
In theory I could just have a #State variable in my view and binding to the toggle and simple not used that variable anywhere else in my view. Only thing is, I do not know ahead of time how many toggles will be displayed in my view so I can't just declare a bunch of #State variables. And if I were too only create one #State variable and blind it to all of my toggles, they would all be in sync, which is not what I am looking for, I would like them to all be independent.
Below is a simplified example of the layout of my view
private var array: [String]
var body: some View {
ForEach((0..<self.array.count), id: \.self) {
Toggle("Show welcome message", isOn: *binding here*)
}
}
Thank you
You can simply create a single #State variable of type [Bool], an array containing all the toggle booleans.
Here is some example code:
struct ContentView: View {
var body: some View {
ToggleStack(count: 5)
}
}
struct ToggleStack: View {
#State private var toggles: [Bool]
private let count: Int
init(count: Int) {
self.count = count
toggles = Array(repeating: true, count: count)
}
var body: some View {
VStack {
ForEach(0 ..< count) { index in
Toggle(isOn: $toggles[index]) {
Text("Checkbox")
}
}
}
}
}
Result:

How do I make a switch.toggle() take effect within a ForEach loop?

I am trying to alternate at the background color of a List/ForEach using a #State var and toggling it on each repetition. The result is alway that the last color behind the entire List. I have set a breakpoint a Text view inside the ForEach and executing it, I see a stop once per item in the input array, then a display on the screen as expected (i.e. every second row is red and the rest are blue). Then, for some reason, we iterate through the code again, one for each item and leave the loop with the background color of all rows being blue.
The code below is a simplified version of my original problem, which iterates over a Realm Results and is expected to handle a NavigationLink rather than the Text-view and handle deleting items as well.
struct ContentView: View {
let array = ["olle", "kalle", "ville", "valle", "viktor"]
#State var mySwitch = false
var body: some View {
List {
ForEach(array, id: \.self) { name in
Text(name)
.onAppear() {
mySwitch.toggle()
print("\(mySwitch)")
}
.listRowBackground(mySwitch ? Color.blue : Color.red)
}
}
}
}
It because of #State var mySwitch = false variable is attached with all row of ForEach so whenever your mySwitch var change it will affect your all row.
So if you want to make some alternative way, you can use the index of item and check whether your number is even or not and do your stuff according to them.
Demo:
struct ContentView: View {
let array = ["olle", "kalle", "ville", "valle", "viktor"]
#State var mySwitch = false
var body: some View {
List {
ForEach(array.indices, id: \.self) { index in
Text(array[index])
.onAppear() {
mySwitch.toggle()
print("\(mySwitch)")
}
.listRowBackground(index.isMultiple(of: 2) ? Color.blue : Color.red)
}
}
}
}

Stop character repetition in TextField without NumberFormatter

For example in a textfield I want to be able to repeat all numbers, but user should be able to use decimal point only once. So, the user should not be able to do: "12...4" but should only be able to do it once "12.4". Any particular way to do do it? Is it possible to do it with SwiftUI without using UIKit?
import SwiftUI
import Combine
struct TipsView: View {
#State private var amount = ""
var body: some View {
NavigationView {
Form {
Section (header: Text("Amount")) {
HStack (spacing: 1) {
Text("£")
TextField("Amount", text: $amount)
.onReceive(Just(amount)) { _ in
}
}
}
}
}
}
}
I have used a String separator to separate the input string into two different arrays. Meaning, one array would contain values before the decimal point and the other would contain values after the decimal point.
import SwiftUI
struct DecimalView: View {
#State private var amount = ""
var body: some View {
Form {
Section(header: Text("Amount")) {
HStack {
Text("£")
TextField("Enter amount", text: $amount)
.onChange(of: amount) { _ in
let filtered = amount.filter {"0123456789.".contains($0)}
if filtered.contains(".") {
let splitted = filtered.split(separator: ".")
if splitted.count >= 2 {
let preDecimal = String(splitted[0])
let afterDecimal = String(splitted[1])
amount = "\(preDecimal).\(afterDecimal)"
}
}
}
}
}
}
}
}

SwiftUI How to Have a ForEach loop iterate based on how many days of a selected month in a Picker is

So I created a line of code that takes two values, a year and a month, and depending on what values you give it, it will calculate the number of days in a given month for a given year. Initially I was going to use State variables so it would just update automatically, but because of how Structs work, I couldn't use the variables I had just barely initialized. (as in the "Cannot use instance member 'year' within property initializer; property initializers run before 'self' is available" Error). The reason I want this code is because I want a forEach Loop to automatically iterate based on that number (as the app I am making will have a list for every day for the next two years). Here is my code:
struct YearView: View {
#State var year = [2020, 2021, 2022]
//monthSymbols gets an array of all the months
#State var monthArray = DateFormatter().monthSymbols!
#State var yearIndex = 0
#State var monthIndex = 0
#State var month = 0
#State var daysInMonth = Calendar.current.range(of: .day, in: .month, for: Calendar.current.date(from: DateComponents(year: year, month: month + 1))!)!.count
var body: some View {
NavigationView {
List {
Section {
VStack {
Picker("Years", selection: $yearIndex) {
ForEach(0 ..< year.count) { index in
Text(String(self.year[index])).tag(index)
}
}
.pickerStyle(SegmentedPickerStyle())
Divider()
if yearIndex == 0 {
Picker("Month", selection: $monthIndex) {
ForEach(6 ..< monthArray.count) { index in
Text(self.monthArray[index]).tag(index)
}
}
.padding(.bottom, 2)
} else {
Picker("Month", selection: $monthIndex) {
ForEach(0 ..< monthArray.count) { index in
Text(self.monthArray[index]).tag(index)
}
}
.padding(.bottom, 2)
}
}
}
Section {
ForEach(0..<10) { i in
NavigationLink(destination: ContentView(day: dayData[i])) {
DayRow(day: dayData[i])
}
}
}
}
.navigationBarTitle(Text("\(monthArray[monthIndex + indexTest]) \(String(year[yearIndex]))"))
}
.listStyle(InsetGroupedListStyle())
}
}
The ForEach loop that is by the Navigation Link is the one I want to be iterated. I have tried creating a function as such:
func getRange(year: Int, month: Int) -> Int {
return Calendar.current.range(of: .day, in: .month, for: Calendar.current.date(from: DateComponents(year: year, month: month + 1))!)!.count
}
I'm not sure where I would run that however, if that would even work. I'm new to SwiftUI and GitHub, so if there's any more info I can give that would help, just ask!
As far as I can tell you should be able to call your getRange(year:, month:) function in your ForEach. Passing in yearIndex and monthIndex.
I wrote up this little sample code to quickly test the theory. Let me know if this is what you're looking for.
-Dan
struct ContentView: View {
#State private var year = 3
#State private var month = 2
var body: some View {
List {
ForEach(0 ..< getRange(year: year, month: month)) { i in
Text("\(i)")
}
}
}
func getRange(year: Int, month: Int) -> Int {
return Calendar.current.range(of: .day, in: .month, for: Calendar.current.date(from: DateComponents(year: year, month: month + 1))!)!.count
}
}
edit to address the question in your comment;
To update your Picker label properly try changing your code to this:
Picker("Years", selection: $year) {
ForEach(0 ..< year) { index in
Text("\(index)")
}
}
See if that gives you the desired response. It's not what you're doing is entirely wrong, I'm just not super clear on what your ultimate intent is. But play around with the code, I think you're on the right track.

SwiftUI value in text doesn’t change

I don’t understand why the Text Value doesn’t change. if I remove the TextField, the Text value change :/ is there something about combine or SwiftUI I am missing ?
struct ContentView2: View{
#State private var numTouches: Int = 0
#State private var num: String = ""
var body: some View {
VStack{
Button("Touch me pls"){
self.numTouches += 1
}
Text("\(numTouches)")
TextField("Hello enter a number", text: $num)
}.onReceive(Just(num)) { newValue in
if newValue == "" {
self.numTouches = 0
} else {
self.numTouches = Int.init(newValue)!
}
}
}
}
What happens is that when a button is touched, it increases numTouches, which causes the view's body to re-render. .onReceive subscribes to the Just publisher that immediately publishes the value num, which is empty "" in the beginning, and that sets numTouches back to 0.
It sounds that you have really just a single variable, which is being updated from two places:
via TextField
via Button's action
So, keep it as single #State var numTouches: Int:
struct ContentView2: View{
#State private var numTouches: Int = 0
var body: some View {
VStack{
Button("Touch me pls"){
self.numTouches += 1
}
Text("\(numTouches)")
TextField("Hello enter a number",
text: $numTouches, formatter: NumberFormatter()))
// .keyboardType(.numberPad) // uncomment for number pad keyboard
}
}
}