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

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

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

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

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

#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
}
}

How to automatically collapse DatePicker in a form when other field is being edited?

I got a simple SwiftUI view:
import SwiftUI
struct AddItemView: View {
#State private var title = ""
#State private var date = Date()
var body: some View {
Form {
Section {
TextField("Title", text: $title)
DatePicker(
selection: $date,
in: Date()...,
displayedComponents: .date,
label: { Text("Date") }
)
}
}
}
}
struct AddItemView_Previews: PreviewProvider {
static var previews: some View {
AddItemView()
}
}
I am trying to achieve the following:
If DatePicker is expanded (user tapped date picker, picker showing wheel to select date) and then starts typing text in TextField, DatePicker should automatically switch to initial, minimized mode (just showing label and selected date). Please take a look at screenshot. This is a behaviour in a stock Calendar.app, for example, when creating events.
Any help appreciated, thank you.
Here is possible approach. The idea is to reset DatePicker component for each of events result in editing.
Tested with Xcode 11.4 / iOS 13.4
struct AddItemView: View {
#State private var title = ""
#State private var date = Date()
#State private var pickerReset = UUID()
var body: some View {
Form {
Section {
TitleTextField()
DatePicker(
selection: $date,
in: Date()...,
displayedComponents: .date,
label: { Text("Date") }
).id(self.pickerReset)
}
}
}
private func TitleTextField() -> some View {
let boundText = Binding<String>(
get: { self.title },
set: { self.title = $0; self.pickerReset = UUID() }
)
return TextField("Title", text: boundText, onEditingChanged: { editing in
if editing {
self.pickerReset = UUID()
}
})
}
}

How can we add `Button` and `TextField` by using `SwiftUI`

I am learning SwiftUI (New framework provided by Apple with iOS 13 and Xcode 11 : SwiftUI by Apple).
I want to add Button and TextField in ListView with action. I want one textfield in that user can add any one number from 1 to 10 and then hit SEND button. Anyone have any idea how to add button in it and also how can we handle touch event of Button with SwiftUI ?
Any help would be appreciate.
Here is a simple view what contains a textfield and a button in a horizontal stack.
To handle the user interaction with in your Button, just overwrite the action closure.
import SwiftUI
struct ButtonAndTextFieldView : View {
#State var text: String = ""
var body: some View {
HStack {
TextField($text,
placeholder: Text("type something here..."))
Button(action: {
// Closure will be called once user taps your button
print(self.$text)
}) {
Text("SEND")
}
}
}
}
#if DEBUG
struct ButtonWithTextFieldView_Previews : PreviewProvider {
static var previews: some View {
ButtonWithTextFieldView()
}
}
#endif
For the Login page design you can use this code section. With textFieldStyle border textfield and content type set.
struct ButtonAndTextFieldView : View {
#State var email: String = ""
#State var password: String = ""
var body: some View {
VStack {
TextField($email,
placeholder: Text("email"))
.textFieldStyle(.roundedBorder)
.textContentType(.emailAddress)
TextField($password,
placeholder: Text("password"))
.textFieldStyle(.roundedBorder)
.textContentType(.password)
Button(action: {
//Get Email and Password
print(self.$email)
print(self.$password)
}) {
Text("Send")
}
}
}
You can add button like that
Button(action: {}) {
Text("Increment Total")
}
And text field.
#State var bindingString: Binding<String> = .constant("")
TextField(bindingString,
placeholder: Text("Hello"),
onEditingChanged: { editing in
print(editing)
}).padding(.all, 40)
You can write a custom TextField which will return you the event in the closure once the user taps on the button. This Custom textfield would contain a HStack with a textfield and a button. Like this.
struct CustomTextField : View {
#Binding var text: String
var editingChanged: (Bool)->() = { _ in }
var commit: ()->() = { }
var action : () -> Void
var buttonTitle : String
var placeholder: String
var isSecuredField = false
var body : some View {
HStack {
if isSecuredField {
SecureField(placeholder, text: $text, onCommit: commit)
} else {
TextField(placeholder, text: $text, onEditingChanged: editingChanged, onCommit: commit)
}
Button(action: action) {
Text(buttonTitle)
}
}
}
}
And to you can use this custom TextField like this. I have used an example from the above-listed answers to make it more clear.
struct ListView: View {
#State var text: String = ""
var body: some View {
List {
ForEach (1..<2) {_ in
Section {
CustomTextField(
text: self.$text,
action: {
print("number is .....\(self.text)")
},
buttonTitle: "Submit",
placeholder: "enter your number")
}
}
}
}
}
ListView with textfield and button. You will need an identifier for each row in case you want to have multiple rows in the List.
struct ListView: View {
#State var text: String = ""
var body: some View {
List {
ForEach (1..<2) {_ in
Section {
HStack(alignment: .center) {
TextField(self.$text, placeholder: Text("type something here...") ).background(Color.red)
Button(action: {
print(self.$text.value)
} ) {
Text("Send")
}
}
}
}
}
}
}

Resources