What textfield / view to show and edit text in SwiftUI? - ios

I would like to edit an item, for example, I have an item named "Jacket", then I would like to edit with a new name "Gray Jacket"
Now I'll have the list of my items, and when I press the "Jacket" item, it will go to the DetailItem and when it appears, I want that textfield / view already filled with "Jacket" text then I able to edit it. Please note I want to use real text not placeholder when the item name first shows up.
Does anyone know what element should I use, or if textfield how? Because I couldn't find how to set textfield with name when it first appears.
UPDATE:
Here's what I've tried, this code I took it from here. It basically took the previous text and make it a placeholder, while actually I want it to be editable.
struct CustomTextField: View {
var placeholder: Text
#Binding var text: String
var editingChanged: (Bool)->() = { _ in }
var commit: ()->() = { }
var body: some View {
ZStack(alignment: .leading) {
if text.isEmpty { placeholder }
TextField("", text: $text, onEditingChanged: editingChanged, onCommit: commit)
}
}
}
struct UsageCustomTxtField: View {
#State var text = ""
var body: some View {
CustomTextField(
placeholder: Text("placeholder").foregroundColor(.black),
text: $text
)
}
}

Here is a possible solution:
import SwiftUI
struct ContentView: View {
#ObservedObject var myItems: ItemViewModel = ItemViewModel()
#State var myNewName: String = "" // Will set the new name
var body: some View {
VStack {
TextField(self.myItems.myList[0].name, // when it appears, I want that textfield / view already filled with "Jacket" text
text: self.$myNewName,
onCommit: {
self.myItems.changeNameOfListItemTo(newName: self.myNewName, index: 0) I would like to edit with a new name "Gray Jacket"
self.myNewName = "" //empty the textfield again so name from item itself will be displayed
})
}
}
}
struct Item { // I have an item named "Jacket"
var name: String
init(name: String) {
self.name = name
}
}
class ItemViewModel: ObservableObject {
#Published var myList: Array<Item> = [Item(name: "Jacket")] //your item list
func changeNameOfListItemTo(newName: String, index: Int) { //function to change the name of an item
self.myList[0].name = newName
}
}

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")
}
}
}

SwiftUI TextField onChange not triggered

New to SwiftUI and trying to figure something out.
I'm implementing a barcode scanner, which consists of a TextField, Button and CameraView.
CameraView will scan a barcode, display the serial number in the TextField, and the Button will use the serial number to pair a bluetooth device.
So now in my SwiftUI class, I have my UI setup like so:
struct BTLEConnectionView: View {
#ObservedObject var bluetoothConnectorViewModel: BluetoothConnectorViewModel
#ObservedObject var barcodeScannerViewModel = BarcodeScannerViewModel()
#Binding var deviceID: String
#State private var serialNumber: String = ""
var body: some View {
VStack {
HStack {
DeviceIDTextField(deviceID: $deviceID, serialNumber: $serialNumber, viewModel: barcodeScannerViewModel)
PairButton(bluetoothConnectorViewModel: bluetoothConnectorViewModel, barcodeScannerViewModel: barcodeScannerViewModel, serialNumber: $serialNumber)
}
.padding()
BarcodeScannerView(barscannerViewModel: barcodeScannerViewModel)
}
}
}
struct DeviceIDTextField: View {
#Binding var deviceID: String
#Binding var serialNumber: String
#ObservedObject var viewModel: BarcodeScannerViewModel
var body: some View {
let serialNumberBinding = Binding<String>(
get: { viewModel.barcodeString.isEmpty ? serialNumber : viewModel.barcodeString },
set: { serialNumber = viewModel.barcodeString.isEmpty ? $0 : viewModel.barcodeString }
)
TextField(NSLocalizedString("textfield.hint.device.id", comment: ""), text: serialNumberBinding)
.textFieldStyle(RoundedBorderTextFieldStyle())
.onChange(of: serialNumber, perform: { value in
serialNumber = value
})
}
private func textFieldChanged(_ text: String) {
print(text)
}
}
struct PairButton: View {
#ObservedObject var bluetoothConnectorViewModel: BluetoothConnectorViewModel
#ObservedObject var barcodeScannerViewModel: BarcodeScannerViewModel
#Binding var serialNumber: String
var body: some View {
Button(action: {
withAnimation {
bluetoothConnectorViewModel.connectBluetoothLowEnergyDevice(deviceID: serialNumber)
}
}) {
ButtonText(text: NSLocalizedString("button.text.pair", comment: ""))
}
}
}
So basically I want to make it so:
If user scans barcode, the serial is added to the textfield, press pair button to use that serial and call the method and pass in the serial number.
If user deletes the prepopulated serial, enters their own, it should update the textfield, and pressing the button should use the new serial number.
Right now when I scan a barcode, it populates the text field, however the onChange callback isn't picked up until I actually type in the TextField, so the result is never set for the method call.
Any help with this would be great, hope it makes sense.
You can create a binding with a custom closure, like this:
struct ContentView: View {
#State var location: String = ""
var body: some View {
let binding = Binding<String>(get: {
self.location
}, set: {
self.location = $0
// do whatever you want here
})
return VStack {
Text("Current location: \(location)")
TextField("Search Location", text: binding)
}
}
}

#State property keeping initial value instead of updating

From a settings page, I want to :
Navigate to a child view
Let the user input update some value in a textfield
Save this value in the user defaults
Navigate back to the settings
If the user opens the child view again, pre-fill the textfield with the previously saved value
Given the following (simple) code :
// User defaults wrapper
class SettingsProvider: ObservableObject {
static let shared = SettingsProvider()
var savedValue: String {
get { UserDefaults.standard.string(forKey: "userdefaultskey") ?? "Default value" }
set {
UserDefaults.standard.setValue(newValue, forKey: "userdefaultskey")
objectWillChange.send()
}
}
}
struct SettingsView: View {
var body: some View {
NavigationView {
NavigationLink("Open child", destination: ChildView())
}
}
}
struct ChildView: View {
#ObservedObject var settingsProvider = SettingsProvider.shared
#State var text: String = SettingsProvider.shared.savedValue
var body: some View {
Text("Value is \(settingsProvider.savedValue)")
TextField("Enter value", text: $text).background(Color.gray)
Button("Save value") {
settingsProvider.savedValue = text
}
}
}
I'm having the following behaviour : video
Can somebody explain to me why the TextField contains Default value the second time I open it ?
Is it a bug in SwiftUI that I should report, or am I missing something ?
If I kill & re-open the app, the textfield will contain (as expected) Other value.
You can just add an onAppear { text = SettingsProvider.shared.savedValue } under the Button like this:
var body: some View {
Text("Value is \(settingsProvider.savedValue)")
TextField("Enter value", text: $text).background(Color.gray)
Button("Save value") {
settingsProvider.savedValue = text
}
.onAppear {
text = SettingsProvider.shared.savedValue // <= add this
}
}

SwiftUI: how to update a variable transiting from a parent view only when wanted?

I would like to pass a variable from a parent to a child view, but in this child view, use it in a text field but update this value only when a SAVE button is pressed.
I tried this:
ParentView
struct ParentView: View {
#State private var name: String = ""
var body: some View {
HStack {
Text("User name: \(name)")
Spacer()
NavigationLink(
destination: ChildView(name: name),
label: {
Text("Update name")
}
)
}
}
}
ChildView
struct ChildView: View {
#State private var name: String = ""
#Binding var passedName: String
var body: some View {
VStack {
Form {
TextField("Update name", text: $name)
Button(action: {
passedName = name
}, label: {
Text("SAVE")
})
}
}
}
init(name: String) {
self._passedName = .constant(name)
self.name = name
}
}
Since I don't want to update the variable directly, I tried to use a name value and then only set the value of the binded passedName when the OK button is tapped. But it doesn't work.
I don't know how to do what I want to.
Thank you for your help
Your general approach is correct - have a state variable to represent a temporarily typed name, but a binding to represent the name "returned" to the parent.
Because the child modifies the data owned by the parent, it needs to accept a binding - not a plain String:
struct ChildView: View {
#State private var name: String
#Binding var passedName: String
var body: some View {
VStack {
Form {
TextField("Update name", text: $name)
Button(action: {
passedName = name
}, label: {
Text("SAVE")
})
}
}
}
init(name: Binding<String>) {
self._passedName = name
self._name = State(initialValue: name.wrappedValue)
}
}
Then, pass in a binding from the parent:
ChildView(name: $name)

SwiftUI - TextField is disabled (not editable) when placed in List on macOS

TextField is disabled (not editable) when placed in List on macOS. When the same code is build for iOS and ran in Simulator, it works as expected.
Is this a bug, or am I missing something?
The code:
struct ContentView : View {
#State private var text: String = ""
var body: some View {
VStack {
List {
// TextField is not editable when this code is ran on macOS
TextField($text, placeholder: Text("Entry text"))
Text("Entered text: \(text)")
}
// TextField is editable on both macOS as well as iOS
TextField($text, placeholder: Text("Entry text"))
}
}
}
That's because the list is taking clicks to drive selection, which you're not using here. TextField becomes editable in the list on macOS only when the row in which it is housed has selection.
If you change your code to something like this
struct ContentView : View {
#State private var text: String = "Hello"
#State private var selection: Int? = nil
var body: some View {
VStack {
List(selection: $selection) {
ForEach(0..<5) { _ in
TextField(self.$text)
}
}
TextField($text)
}
}
}
then run the code, the first click on the cell will cause it to get selected and the second click will cause the text field to receive focus.
Create the TextField using the following code
struct ContentView : View {
#State private var helloText: String = "Hello"
#State private var selection: Int? = nil
var body: some View {
VStack {
List(selection: $selection) {
ForEach(0..<5) { _ in
TextField(.constant(self.helloText), placeholder: Text("Entry text")).textFieldStyle(.roundedBorder)
}
}
}
}
}

Resources