How to raise number through while loop in SwiftUI View - swift

I'm SwiftUI Beginner! I'm trying to add Auto Numbering
But, I can't do it..
I don't know how to raise number of variable x
I want to add through while loop or for loop!
import SwiftUI
struct ContentView : View{
#State var x : Int = 1;
var body : some View{
Text(x)
}
}

As #El Tomato has said you can use ForEach. Here's an example:
struct ContentView: View {
#State var numberOfRows: Int = 5
var body: some View {
ForEach(0..<numberOfRows) {
Text("\($0)")
}
}
}

Related

How to assign a value selected from a Picker to a variable in another class?

I would like to use a picker to select a value and store that value in a variable contained in another class. How can I do that? Here is my code:
my contentview
struct ContentView: View {
#State public var selectedOperator = ""
#ObservedObject var bitwiseCalculator = BitwiseCalculator()
let operators = ["AND", "OR", "XOR"]
var body: some View {
VStack {
Picker("Operator", selection: $selectedOperator) {
ForEach(operators, id: \.self) { op in
Text(op)
}
}
}
}
}
and the variable in the class where I want to save it
class BitwiseCalculator: ObservableObject {
#Published var bitwiseOperator = ""
}
Use
$bitwiseCalculator.bitwiseOperator
& switch your
#ObservedObject to #StateObject

How can I change an #State Variable from an other View

I tried to change an #State Variable with this code (from an other struct, not the struct where I declared the Variable):
MyExampleStruct().myVariable = "New Value"
But it dont worked. I dont know how I can change the value of them, has anybody an idea?
Thanks Boothosh
You aren't going to manipulate the #State of a view directly like that. However, there are plenty of options that you can use to achieve similar results depending on your use case.
Option 1 : Binding
Use when you need to manipulate the variable from both parent and child:
struct ParentView : View {
#State var myVariable : String = "Test"
var body: some View {
VStack {
MyExampleStruct(myVariable: $myVariable)
Button("Change") {
myVariable = "\(Date())"
}
}
}
}
struct MyExampleStruct : View {
#Binding var myVariable : String
var body : some View {
VStack {
Text(myVariable)
Button("Change") {
myVariable = "\(Date())"
}
}
}
}
Option 2: ObservableObject
Use when the state needs to be manipulated in multiple places, passed as a reference via property or via envronmentObject (https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views)
class StateManager : ObservableObject {
#Published var myVariable : String = "Test"
}
struct ParentView : View {
#StateObject var state = StateManager() //use #ObservedObject if targeting iOS 13 or macOS 10.15
var body: some View {
VStack {
MyExampleStruct(state: state)
Button("Change") {
state.myVariable = "\(Date())"
}
}
}
}
struct MyExampleStruct : View {
#ObservedObject var state : StateManager
var body : some View {
Text(state.myVariable)
}
}
Option 3: No state
Use when only the parent needs to manipulate the value:
struct ParentView : View {
#State var myVariable : String = "Test"
var body: some View {
VStack {
MyExampleStruct(myVariable: myVariable)
Button("Change") {
myVariable = "\(Date())"
}
}
}
}
struct MyExampleStruct : View {
var myVariable : String
var body : some View {
Text(myVariable)
}
}
You need to pass it as a Binding to the other view.
For example
struct SubView: View
{
#Binding var myVariable: String
init(_ s: Binding<String>) {
self._myVariable = s
}
var body: some View {
Text(myVariable).onTapGuesture { myVariable = "New Value" }
}
}
struct ContentView: View
{
#State myVariable: String = "Some String"
var body {
SubView($myVariable)
}
}
Of course, this specific example is pretty lame because you hardly need a subview to change a Text string, but you can imagine if you had several views that used myVariable, one of them could change the value, and the others could be responsive to it. It's not the only way, or necessarily even the best way to achieve this in every circumstance. Sometimes you'd want an ObservableObject or an #EnvironmentObject, or with SwiftUI 2, #StateObject.

SwiftUI: ObservedObject/ObservableObject weird behavior

I recently encountered the following problem in SwiftUI with ObservedObject/ObservableObject:
If the ObservedObject/ObservableObject is publishing in a View, the body property is recalculated - as expected.
But if there is a sub View in the body property which also has an ObservedObject, the View, strangely enough, not only recalculates the body property of the sub View, but the entire object.
The state of the ObservedObject is of course lost from the sub View.
Of course, you can prevent this by adding the ObservableObject to the sub View through .environmentObject(), but I don't think that's the best solution, especially with more complex view hierarchies.
Here an example Code:
struct ContentView: View {
#ObservedObject var contentViewModel: ContentViewModel = ContentViewModel()
var body: some View {
VStack {
Button {
self.contentViewModel.counter += 1
} label: {
Text(String(self.contentViewModel.counter))
}
SubView()
}
}
}
class ContentViewModel: ObservableObject {
#Published var counter: Int = 0
}
And the sub View:
struct SubView: View {
#ObservedObject var subViewModel: SubViewModel = SubViewModel()
var body: some View {
Button {
self.subViewModel.counter += 1
} label: {
Text(String(self.subViewModel.counter))
}
}
}
class SubViewModel: ObservableObject {
#Published var counter: Int = 0
}
And here how the sample Code looks/works:
The last weird thing I realised, this is only the case if you use Observed Object. If I would replace in the sub View #ObservedObject var subViewModel: SubViewModel = SubViewModel() with #State var counter: Int = 0 it is working fine again and the state is preserved.
Maybe im missing out on something, but im really confused. Im very grateful for any answers and solutions. If you have further questions fell free to leave a comment, I will answer it within 24h (as long as the question is open).
Declare your ViewModel with #StateObject. #StateObject is not recreated for every view re-render more
struct SubView: View {
#StateObject var subViewModel: SubViewModel = SubViewModel() //<--- Here
var body: some View {
Button {
self.subViewModel.counter += 1
} label: {
Text(String(self.subViewModel.counter))
}
}
}
I found a perfect solution:
Either you replace the #ObservedObject with #StateObject (thx #Raja Kishan) in iOS 14 (SwiftUI v2.0) or you can create a Wrapper for the View and simulate the behaviour of #StateObject in iOS 13 (SwiftUI v1.0):
struct SubViewWrapper: View {
#State var subViewModel: SubViewModel = SubViewModel()
var body: some View {
SubView(subViewModel: self.subViewModel)
}
}
and then use SubViewWrapper() in ContentView instead of SubView()
struct ContentView: View {
#ObservedObject var contentViewModel: ContentViewModel = ContentViewModel()
var body: some View {
VStack {
Button {
self.contentViewModel.counter += 1
} label: {
Text(String(self.contentViewModel.counter))
}
SubViewWrapper()
}
}
}

Binding value from an ObservableObject

Aim:
I have a model which is an ObservableObject. It has a Bool property, I would like to use this Bool property to initialise a #Binding variable.
Questions:
How to convert an #ObservableObject to a #Binding ?
Is creating a #State the only way to initialise a #Binding ?
Note:
I do understand I can make use of #ObservedObject / #EnvironmentObject, and I see it's usefulness, but I am not sure a simple button needs to have access to the entire model.
Or is my understanding incorrect ?
Code:
import SwiftUI
import Combine
import SwiftUI
import PlaygroundSupport
class Car : ObservableObject {
#Published var isReadyForSale = true
}
struct SaleButton : View {
#Binding var isOn : Bool
var body: some View {
Button(action: {
self.isOn.toggle()
}) {
Text(isOn ? "On" : "Off")
}
}
}
let car = Car()
//How to convert an ObservableObject to a Binding
//Is creating an ObservedObject or EnvironmentObject the only way to handle a Observable Object ?
let button = SaleButton(isOn: car.isReadyForSale) //Throws a compilation error and rightly so, but how to pass it as a Binding variable ?
PlaygroundPage.current.setLiveView(button)
Binding variables can be created in the following ways:
#State variable's projected value provides a Binding<Value>
#ObservedObject variable's projected value provides a wrapper from which you can get the Binding<Subject> for all of it's properties
Point 2 applies to #EnvironmentObject as well.
You can create a Binding variable by passing closures for getter and setter as shown below:
let button = SaleButton(isOn: .init(get: { car.isReadyForSale },
set: { car.isReadyForSale = $0} ))
Note:
As #nayem has pointed out you need #State / #ObservedObject / #EnvironmentObject / #StateObject (added in SwiftUI 2.0) in the view for SwiftUI to detect changes automatically.
Projected values can be accessed conveniently by using $ prefix.
You have several options to observe the ObservableObject. If you want to be in sync with the state of the object, it's inevitable to observe the state of the stateful object. From the options, the most commons are:
#State
#ObservedObject
#EnvironmentObject
It is upto you, which one suits your use case.
No. But you need to have an object which can be observed of any change made to that object in any point in time.
In reality, you will have something like this:
class Car: ObservableObject {
#Published var isReadyForSale = true
}
struct ContentView: View {
// It's upto you whether you want to have other type
// such as #State or #ObservedObject
#EnvironmentObject var car: Car
var body: some View {
SaleButton(isOn: $car.isReadyForSale)
}
}
struct SaleButton: View {
#Binding var isOn: Bool
var body: some View {
Button(action: {
self.isOn.toggle()
}) {
Text(isOn ? "Off" : "On")
}
}
}
If you are ready for the #EnvironmentObject you will initialize your view with:
let contentView = ContentView().environmentObject(Car())
struct ContentView: View {
#EnvironmentObject var car: Car
var body: some View {
SaleButton(isOn: self.$car.isReadyForSale)
}
}
class Car: ObservableObject {
#Published var isReadyForSale = true
}
struct SaleButton: View {
#Binding var isOn: Bool
var body: some View {
Button(action: {
self.isOn.toggle()
}) {
Text(isOn ? "On" : "Off")
}
}
}
Ensure you have the following in your SceneDelegate:
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView()
.environmentObject(Car())
In my case i used .constant(viewModel) to pass viewModel to ListView #Binding var viewModel
Example
struct CoursesView: View {
#StateObject var viewModel = CoursesViewModel()
var body: some View {
ZStack {
ListView(viewModel: .constant(viewModel))
ProgressView().opacity(viewModel.isShowing)
}
}
}
struct ListView: View {
#Binding var viewModel: CoursesViewModel
var body: some View {
List {
ForEach(viewModel.courses, id: \.id) { course in
Text(couse.imageUrl)
}
}
}
}

How to Add Max length for a character for Swift UI

Hi i am creating a to do application and i am facing a problem when a user entering some characters to a UIText field i remember there was a way in SWIFT 5 to put a max length but i can't find one in SWIFT UI can someone send me a link or guide me step by step HOW CAN I ADD A MAX LENTGH TO A SWIFT UI PROJECT TO THE TEXT FIELD! THANKS
I tried to find it Everywhere but i can't
struct NewTaskView: View {
var taskStore: TaskStore
#Environment(\.presentationMode) var presentationMode
#State var text = ""
#State var priority: Task.Priority = .Низкий
var body: some View {
Form {
TextField("Название задания", text: $text)
VStack {
Text("Приоритет")
.multilineTextAlignment(.center)
Picker("Priority", selection: $priority.caseIndex) {
ForEach(Task.Priority.allCases.indices) { priorityIndex in
Text(
Task.Priority.allCases[priorityIndex].rawValue
.capitalized
)
.tag(priorityIndex)
}
}
.pickerStyle( SegmentedPickerStyle() )
}
I want to put max length to a text field where is written TextField("Название задания", text: $text)
It seems like this can be achieved with Combine, by creating a wrapper around the text and opening a 2 way subscription, with the text subscribing to the TextField and the TextField subscribing to the ObservableObject. I'd say the way it works its quite logical from a Reactive point of view but would have liked to find a cleaner solution that didn't require another object to be created.
import SwiftUI
import Combine
class TextBindingManager: ObservableObject {
#Published var text = "" {
didSet {
if text.count > characterLimit && oldValue.count <= characterLimit {
text = oldValue
}
}
}
let characterLimit = 5
}
struct ContentView: View {
#ObservedObject var textBindingManager = TextBindingManager()
var body: some View {
TextField("Placeholder", text: $textBindingManager.text)
}
}
I read this article. please check here
This is my whole code. I don't use EnvironmentObject.
struct ContentView: View {
#ObservedObject private var restrictInput = RestrictInput(5)
var body: some View {
Form {
TextField("input text", text: $restrictInput.text)
}
}
class RestrictInput: ObservableObject {
#Published var text = ""
private var canc: AnyCancellable!
init (_ maxLength: Int) {
canc = $text
.debounce(for: 0.5, scheduler: DispatchQueue.main)
.map { String($0.prefix(maxLength)) }
.assign(to: \.text, on: self)
}
deinit {
canc.cancel()
}
}