Im fairly new to Swift and I'm trying to produce a HStack (that will be used in a progress bar) of element and to be able to add elements with a button.
Im not sure if I should use a variable in the ForEach(1..<Variable) section or use another method. Here is the code I have so far but it did not work.
struct ContentView: View {
#State var fill : CGFloat = 0
#State var NumberOfCircles : Int = 0
var body: some View {
HStack(spacing:100) {
ForEach(0..<NumberOfCircles){ _ in
MyShape()
}
Button(action: {NumberOfCircles = 5}, label: {
Text("Button")
})
}
ForEach in SwiftUI needs a constant range to loop over. However,
as the error suggests, if you conform to Identifiable or use ForEach(_:id:content:) and provide an explicit id it is happy. So try this:
struct ContentView: View {
#State var fill: CGFloat = 0
#State var NumberOfCircles: Int = 0
var body: some View {
HStack(spacing: 20) {
ForEach(0..<NumberOfCircles, id: \.self){ _ in // <-- here
MyShape()
}
Button(action: {NumberOfCircles = 5}){
Text("Button")
}
}
}
}
I'm not sure what's your problem, but I tested this code and it works:
struct ContentView: View {
#State var numberOfCircles = 1
var body: some View {
VStack {
HStack {
ForEach(0..<numberOfCircles, id:\.self) { _ in
Circle()
.frame(width: 30, height: 30)
}
}
Button { numberOfCircles = 5 } label: {
Text("Add Circles")
}
}
}
}
Btw, naming convention for variables in Swift is camelCase. That means that declaring a variable you should name it numberOfCircles , not NumberOfCircles . The first uppercase letter is reserved for naming Classes, Structs and Protocols.
Related
A similar question was asked before, and with the help of several netizens, some codes have been changed.
When press .. "Show Next View" >> "Change-Me" >> "Return" .. why the variable "price" can not change to 9 ....
I am a novice, I know there code have something wrong I can't find it and hope someone can guide me, Thanks!
class ExtenalClass {
let SubFrutis = ContentView()
func ExtChangePrice() {
SubFrutis.fChangePrice()
}
}
struct ContentView: View {
#State var price: Int = 0
#State var vPresent = false
func fChangePrice() {
price = 9
print("Price Value is: \(String(price))")
}
var body: some View {
VStack {
Text(String(price)).padding()
Button("Show Next View") { self.vPresent = true }
.sheet(isPresented: $vPresent) {
SecondView(vPresent: self.$vPresent)
}.padding()
Button("Add-Me") { price += 1 }.padding()
Button("RESET") { price = 1 }.padding()
}
}
}
struct SecondView: View {
#Binding var vPresent: Bool
var vExtenal = ExtenalClass()
var body: some View {
Button("Change-Me") {
vExtenal.ExtChangePrice()
// ContentView().fChangePrice()
}.padding()
Button("Return") { self.vPresent = false }
}
}
What is the best way to go about resetting state variables, using a button. I've tried a load of different funcs but none work.
I'm trying to use this button:
primaryButton: .destructive(Text("Delete")) {
Code
},secondaryButton:
.cancel()
To reset these State variables:
#State var statsValue1 = 0
#State var statsValue2 = 0
#State var statsValue3 = 0
#State var statsValue4 = 0
#State var statsValue5 = 0
#State var statsValue6 = 0
(which are in the main content view)
How about using a view model, the #Published property wrapper notifies about any changes of the model and the reset function creates a new instance
struct Model {
var value1 = 0
var value2 = 0
var value3 = 0
}
class ViewModel : ObservableObject {
#Published var model = Model()
func reset() {
model = Model()
}
}
and a simple test logic in the content view
struct ContentView : View {
#StateObject var viewModel = ViewModel()
var body : some View {
VStack(spacing: 20) {
Text("Value 1: \(viewModel.model.value1)")
Text("Value 2: \(viewModel.model.value2)")
Text("Value 3: \(viewModel.model.value3)")
Divider()
Button ( "Delete", role: .destructive, action: viewModel.reset )
Button { viewModel.model.value1 += 1 } label: { Text("Increment value 1") }
Button { viewModel.model.value2 += 1 } label: { Text("Increment value 2") }
Button { viewModel.model.value3 += 1 } label: { Text("Increment value 3") }
}
}
}
Has anyone created a custom View to use a picker? I would like to use many pickers without clogging my view definition.
Here is my easy to use custom view to use SwiftUI picker:
import SwiftUI
struct MyPicker : View {
#State var title:String = ""
#State var display:Bool = false
#State var chosenItem:Int = 0
var choices:[String] = []
var setPickerValue:(Int)->Void
var body: some View {
HStack {
Text("\(title)")
Button(action: {
self.display.toggle()
}) {
Text("\(choices[chosenItem])")
}
if display {
Picker(selection: $chosenItem, label:Text("")) {
ForEach(0 ..< choices.count) {
Text(self.choices[$0])
}
}
.onTapGesture{
self.display.toggle()
self.setPickerValue(self.chosenItem)
}
}
}
}
init(t:String, c:[String], initChoice: Int, funcSetValue:#escaping (Int)->Void) {
_title = State(initialValue: t)
choices = c
_chosenItem = State(initialValue: initChoice)
setPickerValue = funcSetValue
}
}
struct MyView : View {
#State var imageChoice:Int = 0
var imageList:[String] = ["imageOne", "imageTwo", "imageThree"]
var body: some View {
MyPicker(t:"Images", c:imageList, initChoice:0, funcSetValue:setImageChoice)
}
func setImageChoice(v:Int) -> Void {
imageChoice = v
}
}
I trying to make a SwiftUI app where after entering one letter in a TextField the cursor automatically moves to the next TextField. The UI is pretty much like this.
In Swift/IB, it looks like this was done with delegates and adding a target like in this post:
How to move to the next UITextField automatically in Swift
But can't find any documentation for using delegates/targets in SwiftUI.
I tried following this post:
SwiftUI TextField max length
But this has not worked for me. Setting the .prefix(1) does not seem to make a difference. The TextField still accepts any amount of characters and when moved to the next TextField does not reduce the characters entered to only the first character.
In SwiftUI's current state, is it possible to automatically move to the next TextField after 1 character is entered?
Thanks for any help!
It can be done in iOS 15 with FocusState
import SwiftUI
///Sample usage
#available(iOS 15.0, *)
struct PinParentView: View {
#State var pin: Int = 12356
var body: some View {
VStack{
Text(pin.description)
PinView(pin: $pin)
}
}
}
#available(iOS 15.0, *)
struct PinView: View {
#Binding var pin: Int
#State var pinDict: [UniqueCharacter] = []
#FocusState private var focusedField: UniqueCharacter?
var body: some View{
HStack{
ForEach($pinDict, id: \.id, content: { $char in
TextField("pin digit", text:
Binding(get: {
char.char.description
}, set: { newValue in
let newest: Character = newValue.last ?? "0"
//This check is only needed if you only want numbers
if Int(newest.description) != nil{
char.char = newest
}
//Set the new focus
DispatchQueue.main.async {
setFocus()
}
})
).textFieldStyle(.roundedBorder)
.focused($focusedField, equals: char)
})
}.onAppear(perform: {
//Set the initial value of the text fields
//By using unique characters you can keep the order
pinDict = pin.description.uniqueCharacters()
})
}
func setFocus(){
//Default to the first box when focus is not set or the user reaches the last box
if focusedField == nil || focusedField == pinDict.last{
focusedField = pinDict.first
}else{
//find the index of the current character
let idx = pinDict.firstIndex(of: focusedField!)
//Another safety check for the index
if idx == nil || pinDict.last == pinDict[idx!]{
focusedField = pinDict.first
}else{
focusedField = pinDict[idx! + 1]
}
}
//Update the Binding that came from the parent
setPinBinding()
}
///Updates the binding from the parent
func setPinBinding(){
var newPinInt = 0
for n in pinDict{
if n == pinDict.first{
newPinInt = Int(n.char.description) ?? 0
}else{
newPinInt = Int(String(newPinInt) + n.char.description) ?? 0
}
}
pin = newPinInt
}
}
//Convert String to Unique characers
extension String{
func uniqueCharacters() -> [UniqueCharacter]{
let array: [Character] = Array(self)
return array.uniqueCharacters()
}
func numberOnly() -> String {
self.trimmingCharacters(in: CharacterSet(charactersIn: "-0123456789.").inverted)
}
}
extension Array where Element == Character {
func uniqueCharacters() -> [UniqueCharacter]{
var array: [UniqueCharacter] = []
for char in self{
array.append(UniqueCharacter(char: char))
}
return array
}
}
//String/Characters can be repeating so yu have to make them a unique value
struct UniqueCharacter: Identifiable, Equatable, Hashable{
var char: Character
var id: UUID = UUID()
}
#available(iOS 15.0, *)
struct PinView_Previews: PreviewProvider {
static var previews: some View {
PinParentView()
}
}
Objective-C would not allow you to run the following code:
myShape.origin.x = 50
This made it easy to detect changes in the origin, since someone using your class was forced to write myShape.origin = newOrigin, and thus you could easily tie in to the setter of this property.
Swift now allows you to perform the original, formerly-disallowed code. Assuming the following class structure, how would you detect the change to the origin in order to execute your own code (e.g. to update the screen)?
struct Point {
var x = 0
var y = 0
}
class Shape {
var origin: Point = Point()
}
Update: Perhaps I should have been more explicit, but assume I don't want to modify the Point struct. The reason is that Shape is but one class that uses Point, there may very well be hundreds of others, not to mention that the origin is not the only way a Point may be used.
Property observers (willSet and didSet) do fire when sub-properties of that property are changed. In this case, when the x or y values of the Point structure change, that property will be set.
Here is my example playground code:
struct Point : Printable
{
var x = 0
var y = 0
var description : String {
{
return "(\(x), \(y))";
}
}
class Shape
{
var origin : Point = Point()
{
willSet(newOrigin)
{
println("Changing origin to \(newOrigin.description)!")
}
}
}
let circle = Shape()
circle.origin.x = 42
circle.origin.y = 77
And here is the console output:
Changing origin to (42, 0)!
Changing origin to (42, 77)!
Doesn't this work?
class Shape {
var origin: Point {
willSet(aNewValueForOrigin) {
// pre flight code
}
didSet(theOldValueOfOrigin) {
// post flight code
}
}
}
Edit: revisited code and added name of arguments to reflect what to expect.
You can use Property Observers also works for structs
Link to the part on the ebook
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
println("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
println("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
Use didSet, e.g.,
struct Point {
var x = 0
var y: Int = 0 {
didSet {
println("blah blah")
}
}
}
class Shape {
var origin: Point = Point()
}
let s = Shape()
s.origin.y = 2