How can I bind static string? - binding

For example, I have this interface:
import SwiftUI
struct ContentView: View {
#Binding var statictext : String
var body: some View {
Text("My New Text: \(statictext)")
}
}
and this class:
class Strings
{
public static var mytext = "MyText"
}
How can I initialize ContentView? I need, than text in interface become "My New Text: new text" when I change the value of Strings.mytext = "new text"
P.S.: sorry for my English)))))

I dont know if this is the right answer for this question but you could do this, even thou it feels kinda dirty to me.
import SwiftUI
class Strings
{
public static var mytext = "MyText"
}
struct ContentView: View {
#State var statictext : String {
willSet {
Strings.mytext = newValue
}
}
init() {
self._statictext = State(initialValue: Strings.mytext)
}
var body: some View {
VStack {
Text("My New Text: \(statictext)")
Button(action: {
self.statictext = "Test"
}) {
Text("Button")
}
}
}
}

Related

Supply any string value to swiftUI form in different for textfield

I have two view file. I have textfield. I want to supply string value to the textfield from another view
File 1 :- Place where form is created
struct ContentView: View {
#State var subjectLine: String = ""
var body: some View {
form {
Section(header: Text(NSLocalizedString("subjectLine", comment: ""))) {
TextField("SubjectLine", text: $subjectLine
}
}
}
}
File 2 :- Place where I want to provide value to the string and that will show in the textfield UI
struct CalenderView: View {
#Binding var subjectLine: String
var body : some View {
Button(action: {
subjectLine = "Assigned default value"
}, label: {
Text("Fill textfield")
}
}
})
}
}
This is not working. Any other way we can supply value to the textfield in other view file.
As i can understand you have binding in CalenderView
that means you want to navigate there when you navigate update there.
struct ContentView: View {
#State private var subjectLine: String = ""
#State private var showingSheet: Bool = false
var body: some View {
NavigationView {
Form {
Section(header: Text(NSLocalizedString("subjectLine", comment: ""))) {
TextField("SubjectLine", text: $subjectLine)
}
}
.navigationBarItems(trailing: nextButton)
.sheet(isPresented: $showingSheet) {
CalenderView(subjectLine: $subjectLine)
}
}
}
var nextButton: some View {
Button("Next") {
showingSheet.toggle()
}
}
}
CalendarView
struct CalenderView: View {
#Binding var subjectLine: String
#Environment(\.dismiss) private var dismiss
var body: some View {
Button {
subjectLine = "Assigned default value"
dismiss()
} label: {
Text("Fill textfield")
}
}
}

Array of binding variables for multiple toggles in MVVM pattern in SwiftUI

I have a simple use case of having a VStack of a dynamic number of Text with Toggle buttons coming from an array.
import SwiftUI
public struct Test: View {
#ObservedObject public var viewModel = TestViewModel()
public init() {
}
public var body: some View {
VStack {
ForEach(viewModel.models) { model in
ToggleView(title: <#T##Binding<String>#>, switchState: <#T##Binding<Bool>#>)
//how to add the above
}
}.padding(50)
}
}
struct ToggleView: View {
#Binding var title: String
#Binding var switchState: Bool
var body: some View {
VStack {
Toggle(isOn: $switchState) {
Text(title)
}
}
}
}
public class TestModel: Identifiable {
#Published var state: Bool {
didSet {
//do something
//publish to the view that the state has changed
}
}
#Published var title: String
init(state: Bool, title: String) {
self.state = state
self.title = title
}
}
public class TestViewModel: ObservableObject {
#Published var models: [TestModel] = [TestModel(state: false, title: "Title 1"), TestModel(state: true, title: "Title 2")]
}
The following questions arise:
In MVVM pattern, is it ok to have the binding variables in model class or should it be inside the view model?
How to send the message of state change from model class to view/scene when the toggle state is changed?
If using an array of binding variables in view model for each of the toggle states, how to know which particular element of array has changed? (see the following code snippet)
class ViewModel {
#Published var dataModel: [TestModel]
#Published var toggleStates = [Bool]() {
didSet {
//do something based on which element of the toggle states array has changed
}
}
}
Please help with the above questions.
here is one way you could achieve what you desire.
Has you will notice you have to use the binding power of #ObservedObject.
The trick is to use indexes to reach the array elements for you binding.
If you loop on the array elements model directly you loose the underlying binding properties.
struct Test: View {
#ObservedObject public var viewModel = TestViewModel()
var body: some View {
VStack {
ForEach(viewModel.models.indices) { index in
ToggleView(title: self.viewModel.models[index].title, switchState: self.$viewModel.models[index].state)
}
}.padding(50)
}
}
class TestViewModel: ObservableObject {
#Published var models: [TestModel] = [
TestModel(state: false, title: "Title 1"),
TestModel(state: true, title: "Title 2")]
}
struct ToggleView: View {
var title: String
#Binding var switchState: Bool
var body: some View {
VStack {
Toggle(isOn: $switchState) {
Text(title)
}
}
}
}
class TestModel: Identifiable {
var state: Bool
var title: String
init(state: Bool, title: String) {
self.title = title
self.state = state
}
}
Hope this does the trick for you.
Best

How to extract String value from Observed Object in Swift

I want to extract String value from Observed Object
This is example code
import SwiftUI
import Combine
class SetViewModel : ObservableObject {
private static let userDefaultTextKey = "textKey"
#Published var text: String = UserDefaults.standard.string(forKey: SetViewModel.userDefaultTextKey) ?? ""
private var canc: AnyCancellable!
init() {
canc = $text.debounce(for: 0.2, scheduler: DispatchQueue.main).sink { newText in
UserDefaults.standard.set(newText, forKey: SetViewModel.userDefaultTextKey)
}
}
deinit {
canc.cancel()
}
}
struct SettingView: View {
#ObservedObject var viewModel = SettingViewModel()
var body: some View {
ZStack {
Rectangle().foregroundColor(Color.white).edgesIgnoringSafeArea(.all).background(Color.white)
VStack {
TextField("test", text: $viewModel.text).textFieldStyle(BottomLineTextFieldStyle()).foregroundColor(.red)
Text($viewModel.text) //I want to get String Value from $viewModel.text
}
}
}
}
I want to use "$viewModel.text"'s String value. How can I do this?
Here is fix
Text(viewModel.text) // << use directly, no $ needed, it is for binding
try this:
struct SettingView: View {
#ObservedObject var viewModel = SetViewModel()
var body: some View {
ZStack {
Rectangle().foregroundColor(Color.white).edgesIgnoringSafeArea(.all).background(Color.white)
VStack {
TextField("test", text: self.$viewModel.text)
.textFieldStyle(PlainTextFieldStyle())
.foregroundColor(.red)
Text(viewModel.text) //I want to get String Value from $viewModel.text
}
}
}
}

Type of expression is ambiguous without more context on SwiftUI when Text is used multiple times, how to fix it?

I am encountering the following error message:
Type of expression is ambiguous without more context
on the this line:
Picker(selection: $contact.type, label: Text("Select Testing Type"))
Error was popping up the moment I've added more lines with Text. did any one have the same problem? how can it be solved?
I am working with Catalina and iOS11.
here the whole code snippet:
import Combine
import SwiftUI
class Contact: ObservableObject {
var objectWillChange = PassthroughSubject <Void, Never>()
static let types = ["Functional", "Automation", "Manual", "Mobile App", "Performance", "Security", "E2E", "Integration"]
static let services = ["HandsOn Testing", "Test Management", "Test Process Setup", "Training", "Test Leadership"]
var type = 0 {didSet { update()}}
var service = 0 {didSet { update()}}
var name = "" {didSet { update()}}
var company = "" {didSet { update()}}
var city = "" {didSet { update()}}
var email = "" {didSet { update()}}
var isValid: Bool{
if name.isEmpty || company.isEmpty || city.isEmpty || email.isEmpty {
return false
}
}
func update(){
//add log
objectWillChange.send()
}
}
struct ContentView: View {
#ObservedObject var contact = Contact()
var body: some View {
NavigationView {
Form {
Section {
Picker(selection: $contact.type, label: Text("Select Testing Type"))
{ ForEach(0..<Contact.types.count)
{Text(Contact.types[$0]).tag($0)
}}
Picker(selection: $contact.service, label: Text("Select Testing Service"))
{ForEach(0..<Contact.services.count)
{Text(Contact.services[$0]).tag($0)
}}
}
Section {
TextField($contact.name, placeholder:Text("Name"))
TextField($contact.company, placeholder:Text("Company"))
TextField($contact.city, placeholder:Text("City"))
TextField($contact.email, placeholder:Text("Email"))
}
Section {
Button(action: {
self.sendForm()
}) {
Text("Send Form")
}
}
.navigationBarTitle(Text("Contact us"))
}
}}
func sendForm(){
}}
struct SwiftUIView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
If you copy/pasted code as it is in your module, then I assume the issue is everywhere in
ForEach(0..<Contact.types.count)
you use type name instead of variable name contact

How to observe a TextField value with SwiftUI and Combine?

I'm trying to execute an action every time a textField's value is changed.
#Published var value: String = ""
var body: some View {
$value.sink { (val) in
print(val)
}
return TextField($value)
}
But I get below error.
Cannot convert value of type 'Published' to expected argument type 'Binding'
This should be a non-fragile way of doing it:
class MyData: ObservableObject {
var value: String = "" {
willSet(newValue) {
print(newValue)
}
}
}
struct ContentView: View {
#ObservedObject var data = MyData()
var body: some View {
TextField("Input:", text: $data.value)
}
}
In your code, $value is a publisher, while TextField requires a binding. While you can change from #Published to #State or even #Binding, that can't observe the event when the value is changed.
It seems like there is no way to observe a binding.
An alternative is to use ObservableObject to wrap your value type, then observe the publisher ($value).
class MyValue: ObservableObject {
#Published var value: String = ""
init() {
$value.sink { ... }
}
}
Then in your view, you have have the binding $viewModel.value.
struct ContentView: View {
#ObservedObject var viewModel = MyValue()
var body: some View {
TextField($viewModel.value)
}
}
I don't use combine for this. This it's working for me:
TextField("write your answer here...",
text: Binding(
get: {
return self.query
},
set: { (newValue) in
self.fetch(query: newValue) // any action you need
return self.query = newValue
}
)
)
I have to say it's not my idea, I read it in this blog: SwiftUI binding: A very simple trick
If you want to observe value then it should be a State
#State var value: String = ""
You can observe TextField value by using ways,
import SwiftUI
import Combine
struct ContentView: View {
#State private var Text1 = ""
#State private var Text2 = ""
#ObservedObject var viewModel = ObserveTextFieldValue()
var body: some View {
//MARK: TextField with Closures
TextField("Enter text1", text: $Text1){
editing in
print(editing)
}onCommit: {
print("Committed")
}
//MARK: .onChange Modifier
TextField("Enter text2", text: $Text2).onChange(of: Text2){
text in
print(text)
}
//MARK: ViewModel & Publisher(Combine)
TextField("Enter text3", text: $viewModel.value)
}
}
class ObserveTextFieldValue: ObservableObject {
#Published var value: String = ""
private var cancellables = Set<AnyCancellable>()
init() {
$value.sink(receiveValue: {
val in
print(val)
}).store(in: &cancellables)
}
}
#Published is one of the most useful property wrappers in SwiftUI, allowing us to create observable objects that automatically announce when changes occur that means whenever an object with a property marked #Published is changed, all views using that object will be reloaded to reflect those changes.
import SwiftUI
struct ContentView: View {
#ObservedObject var textfieldData = TextfieldData()
var body: some View {
TextField("Input:", text: $textfieldData.data)
}
}
class TextfieldData: ObservableObject{
#Published var data: String = ""
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

Resources