SwiftUI - State change in UITextField causes the frameWidth to grow uncontrolled and ignore bounds/frame
You need to decrease priority of content resistance in makeUIView
(so content would not push external layout set in SwiftUI) like below
func makeUIView(context: UIViewRepresentableContext<MyField>) -> UITextField {
let field = UITextField(frame: .zero)
field.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
...
backup
SwiftUI textfield proxy binding code to ignore spaces not working?
No, we should not return, there should be assignment, because it generates feedback.
It can be like
TextField(placeholder, text: Binding(
get: { self.input },
set: {
var newValue = $0
newValue.removeAll { $0 == " " } // << here !!
self.input = newValue
}))
Problem enabling and focusing a text field at the same time
Yes you need to separate isEditing
from isFocusOn
, then make isFocusOn
depends on isEditing
like this
Updated by @sak request: change editing on focus
struct MyView: View {
@Binding var text: String = ""
@State private var isEditing: Bool = false
@FocusState var isFocusOn: Bool
var body: some View {
HStack {
if isEditing {
TextField("Text", text: $text)
.focused($isFocusOn)
.onAppear {
DispatchQueue.main.async {
isFocusOn = true
}
}
} else {
TextField("Text", text: $text)
.disabled(true)
}
Spacer()
Button(isEditing ? "Done" : "Edit") {
isEditing.toggle()
}
}
.onChange(of: isFocusOn) { newValue in
if !newValue { isEditing = false }
}
}
}
Note that we don't set edit = true
when it's on focus, otherwise the disabled
will be useless
Picker choice changing the TextField in SwiftUI
Format is not tracked to update TextField, you can do it forcefully using id
depending on format parameter, like
TextField("Amount", value: $amount, format: .currency(code: currency))
.keyboardType(.decimalPad)
.id(currency) // << here !!
Tested with Xcode 13.2 / iOS 15.2
SwiftUI: View does not update when state variable changes
The cause of this is using @State
for your CNMutableContact
.
@State
works best with value types -- whenever a new value is assigned to the property, it tells the View
to re-render. In your case, though, CNMutableContact
is a reference type. So, you're not setting a truly new value, you're modifying an already existing value. In this case, the View
only updates when name
changes, which then triggers your onChange
, so there's no update after the contact
changes and you're always left one step behind.
But, you need something like @State
because otherwise you can't mutate the contact.
There are a couple of solutions to this. I think the simplest one is to wrap your CNMutableContact
in an ObservableObject
and call objectWillChange.send()
explicitly when you change a property on it. That way, the View
will be re-rendered (even though there aren't any @Published
properties on it).
class ContactViewModel : ObservableObject {
var contact = CNMutableContact()
func changeGivenName(_ newValue : String) {
contact.givenName = newValue
self.objectWillChange.send()
}
}
struct ContentView: View {
@State var name: String = ""
@StateObject private var contactVM = ContactViewModel()
var body: some View {
TextField("name", text: $name)
.onChange(of: name) { newValue in
contactVM.changeGivenName(newValue)
print("contact.givenName = \(contactVM.contact.givenName)")
}
Text("contact.givenName = \(contactVM.contact.givenName)")
}
}
Another option is moving name
to the view model and using Combine to observe the changes. This works without objectWillChange
because the sink
updates contact
on the same run loop as name
gets changed, so the @Published
property wrapper signals the View
to update after the change to contact
has been made.
import Combine
import SwiftUI
import Contacts
class ContactViewModel : ObservableObject {
@Published var name: String = ""
var contact = CNMutableContact()
private var cancellable : AnyCancellable?
init() {
cancellable = $name.sink {
self.contact.givenName = $0
}
}
}
struct ContentView: View {
@StateObject private var contactVM = ContactViewModel()
var body: some View {
TextField("name", text: $contactVM.name)
Text("contact.givenName = \(contactVM.contact.givenName)")
}
}
Related Topics
Calling a Swift Class Factory Method With Leading Dot Notation
Passing Lists from One Function to Another in Swift
Add Local Notification in iOS 10 - Swift 3
Ios 11 Custom Navbar Goes Under Status Bar
How to Get Text to Wrap in a Uilabel (Via Uiviewrepresentable) Without Having a Fixed Width
Number of Words in a Swift String For Word Count Calculation
Why Is 'Nil' Not Compatible With 'Unsafepointer≪Cgaffinetransform≫' in Swift 3
Get Associated Value from Enumeration Without Switch/Case
Xcode 8 Beta 3: Expected ',' Joining Parts of a Multi-Clause Condition
Storing Values in Completionhandlers - Swift
Difference Between Computed Property and Property Set With Closure
How to Append Elements into a Dictionary in Swift
How to Load Url in Uiwebview in Swift
Swiftui View and @Fetchrequest Predicate With Variable That Can Change
Sharing Userdefaults Between Extensions