SwiftUI: Forcing an Update
Setting currentPage
, as it is a @State
, will reload the whole body.
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)")
}
}
SwiftUI ForEach force UI update when updating the contents of a core data relationship
I’m not sure if this is the ONLY solution as @malhal gave quite an extensive and seemingly useful response.
But I came across a much easier and immediate fix, within my original solution. The inverse relationships must be specified. Doing this resolved all issues.
How to force view updates from changes in value of published array item
without all relevent the code, I can only guess. Try something like this:
struct SecondView: View {
@ObservedObject var viewModel: MyViewModel
...
var body: some View {
...
ForEach ($viewModel.itemsArray, id: \.self) { $item in // <-- here
HStack {
Button(action: {
if item.quantity < 2 { // <-- here
item.quantity = 1
} else {
item.quantity -= 1
}
}) {
Image(systemName: "minus.circle").foregroundColor(Color.gray)
}
Text("\(item.quantity)")
Button(action: { item.quantity += 1 }) { // <-- here
Image(systemName: "plus.circle").foregroundColor(Color.black)
}
}
}
}
}
and no need for your increaseCount
and decreaseCount
code in your MyViewModel
.
EDIT-1: here is my test code that shows the UI is updated when the buttons are tapped/clicked.
struct Item: Identifiable, Hashable {
let id = UUID()
var quantity = 0
}
class MyViewModel: ObservableObject {
@Published var itemsArray = [Item(),Item(),Item()] // for testing some Items in the array
}
struct ContentView: View {
@StateObject var viewModel = MyViewModel() // <-- here, or let
var body: some View {
SecondView(viewModel: viewModel)
}
}
struct SecondView: View {
@ObservedObject var viewModel: MyViewModel
var body: some View {
VStack(spacing: 33) {
ForEach ($viewModel.itemsArray, id: \.self) { $item in // <-- here
HStack {
Button(action: {
if item.quantity < 1 { // <-- here
item.quantity = 0
} else {
item.quantity -= 1
}
}) {
Image(systemName: "minus.circle").foregroundColor(Color.gray)
}
Text("\(item.quantity)")
Button(action: { item.quantity += 1 }) { // <-- here
Image(systemName: "plus.circle").foregroundColor(Color.black)
}
}
}
}
}
}
SwiftUI view does not update for Published objects with subclasses
ObservableObject
requires that its fields are structs, not classes.
I changed your code slightly and it worked:
protocol SuperMankind {
associatedtype PlayerType
var people: [PlayerType] { get set }
}
struct Mankind: SuperMankind {
var people: [Men] = []
}
Screenshot here
Re your solution (since I can't comment):
Array<Men>
is a struct, despite the array holding class references. This is why your code works now, as before you were directly holding a reference to a class in your ObservableObject
(which therefore did not update the view).
SwiftUI is it possible to update ObservedObject in the current view without redrawing current view?
SwiftUI is designed so that the code in your views is run multiple times so that it knows what to display, and it reruns any time a piece of state – whether it's @State
, @StateObject
, @ObservedObject
, etc. – changes. That's why you're seeing multiple print statements – it's expected behaviour.
Here, your View2
and View3
are referencing the same data source, so of course if that data source changes in one, it's going to change in the other.
So when you have a one-element array and remove that element in one view, your other view is going to be re-evaluated because it's looking at exactly the same array. If you don't want that to happen, then you should not share state between views.
The key with successfully using SwiftUI is to ensure that your views' body code can be run, and rerun, as many times as the system wants to without introducing side-effects (the throwing of an error when calling let text = dataSource.listOfThings[0]
with an empty array is one such side effect). If you try and fight against that, you'll have a devil of a time creating beautiful and performant user interfaces with SwiftUI.
Force reload Image when url changes SwiftUI
This is jnpdx Answer !
I dont know the reason , if it was too close from displaying, but view wouldn t update with the firebase request onAppear, using id foreced the refresh.
struct Test4: View {
@State private var mProfilePhotoUrl: URL = URL(string:"0")!
*@State private var ID = 0*
var body: some View {
VStack(alignment: .center,spacing : 0){
KFImage(mProfilePhotoUrl)*.id(ID)*
}.onAppear(){
setUpProfileElements()
}
}
private func setUpProfileElements(){
// FirebaseRequest -> mProfilePhotoUrl = URL(string: User.profilePhoto)
// *-> self.ID += 1*
}
}
Related Topics
Swift 2 - Pattern Matching in "If"
Closure With Generic Parameters
Fatal Error: Swapping a Location With Itself Is Not Supported With Swift 2.0
Inout Parameter in Async Callback Does Not Work as Expected
Swift: Converting a String into a Variable Name
How to Return Coordinates After Forward Geocoding
Why Do I Need Underscores in Swift
Swift - Spritekit Cgpoint Alignment
What Is Geometry Reader in Swiftui
Make a Swift Dictionary Where the Key Is "Type"
Can You Execute an Applescript Script from a Swift Application
How to Remove Diacritics from a String in Swift
Constant Unassigned Optional Will Not Be Nil by Default
Swift Variable Name With ' (Backtick)
Uitableview With More Than One Custom Cells With Swift