Swiftui - State Change in Uitextfield Causes the Framewidth to Grow Uncontrolled and Ignore Bounds/Frame

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



Leave a reply



Submit