How to update custom classes properties properly?
First you create a singleton class to hold all your "global" variables. Then add a didSet to the fadedTextColor property to post a notification when its value changes. Next add an observer and a selector at your custom class to change the buttons border color using the color from your singleton class:
class Shared {
private init() {}
static let instance = Shared()
var fadedTextColor: UIColor = .red {
didSet {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "fadedTextColorChanged"), object: nil)
}
}
var textHeaderColor: UIColor = .blue
}
class PopupButton: UIButton {
override func awakeFromNib() {
super.awakeFromNib()
layer.borderWidth = 1.3
layer.borderColor = Shared.instance.fadedTextColor.cgColor
layer.cornerRadius = 10
layer.backgroundColor = UIColor.white.cgColor
setTitleColor(Shared.instance.textHeaderColor, for: .normal)
NotificationCenter.default.addObserver(self, selector: #selector(colorChanged), name: NSNotification.Name(rawValue: "fadedTextColorChanged"), object: nil)
}
@objc func colorChanged(notification: Notification) {
layer.borderColor = Shared.instance.fadedTextColor.cgColor
}
}
Swift class-property update
You can use mutableCopy()
with your object but for that your custom class need to extends from NSObject
and its return type is Any
so you need to explicitly type cast its result to your CustomClass
.
targetVC.reservation = self.reservation!.mutableCopy() as! YourCustomClass
swiftui text view does not update when the property of class changes
For Objects use @StateObject property wrapper and for String, Int, etc use @State property wrapper.
@State: We use this property wrapper when we observe a property that is exists inside our ContentView.
@StateObject: We use this property wrapper when we are observing properties that exists outside of our ContentView like in our Object, we can observe that property changes by firstly conforming that class to ObservableObject Protocol and marking that property as @Published inside our Object.
struct ContentView: View {
@StateObject private var client : Client = Client()
var body: some View {
VStack{
Text(client.message)
Button( action: {
print("\(client.message)")
}){
Text("Mess")
}
}
}
}
class Client : ObservableObject{
private var mqtt: CocoaMQTT!
@Published var message : String = "Empty"
func get_message(mess : String){
self.message = mess
}
}
How to update a property in a separate class from a child view and have the parent view update
do not use Data
as it is already a standard swift name. Then try the following:
class Datax: ObservableObject {
@Published var title = "Title"
}
struct ContentView: View {
@StateObject var data = Datax()
var body: some View {
VStack {
Text(data.title).padding()
ButtonView(data: data) // <-- here
}
}
}
struct ButtonView: View {
@ObservedObject var data: Datax // <-- here
var body: some View {
VStack {
Button(action: {
data.title = "Hello"
}, label: {
Text("Button")
})
}
}
}
Update property without running combine pipeline
On further inspection, the answer came to me.
This is the case for class PassthroughSubject
.
class ObserveableTest: ObservableObject {
private var cancellables: Set<AnyCancellable> = []
/// private(set) to restrict all changes to go through class functions
@Published private(set) var items: [Int] = []
/// A subject we can pass data into, when we wanna publish changes
private let listener = PassthroughSubject<[Int], Never>()
init() {
listener
.debounce(for: 0.6, scheduler: RunLoop.main)
.sink {
print($0)
}
.store(in: &cancellables)
}
func thisFunctionIsCalledByUserAndTriggersPipeline() {
items.append((0...1000000).randomElement()!)
listener.send(items) // publish to pipeline
}
func thisFunctionIsCalledByServerAndDoesNotTriggerPipeline() {
items.append((0...1000000).randomElement()!)
}
}
How to prod a SwiftUI view to update when a model class sub-property changes?
When MyObject
is a class type the results
contains references, so when you change property of any instance inside results
the reference of that instance is not changed, so results
is not changed, so nothing published and UI is not updated.
In such case the solution is to force publish explicitly when you perform any change of internal model
class DataSource: ObservableObject
{
@Published var results = [MyObject](repeating: MyObject(label: "test"), count: 5)
func change()
{
print("I've changed")
results[3].label = "sooner"
self.objectWillChange.send() // << here !!
_ = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) {[weak self] _ in
self?.results[1].label = "Or later"
self?.objectWillChange.send() // << here !!
}
}
}
Update UI on Singleton property change without sharing dependency between Views
The ObservableObject
updates view only via ObservedObject/StateObject/EnvironmentObject
(which are actually have same nature). That wrapper (one of them) actually observers changes in published properties and refreshes view.
So your case can be solved like
struct ParentView: View {
@StateObject private var networkNanager = NetworkManager.shared // << observer !!
var body: some View {
SOME_VIEW.disabled(!networkNanager.isConnected) // injects dependency !!
}
}
Swift - update object in place
Here is the explanation for both of your queries,
1. parent child array
contains the reference
to the person1
object, i.e. person1 and parent child array
store the reference to same object.
var person1 = Person(name: "John", surname: "Doe")
parent.child.append(person1)
Now, you're assigning a new object to person1
instead of making changes to the same object, i.e.
person1 = Person(name: "Jack", surname: "Doe")
This won't reflect in the parent child array
. It will still contain the reference to the previous Person
object.
This is the reason you are getting different values of name in person1 and parent child array
print(person1.name) //Jack
print(parent.child.first!.name) //John
2. Instead of adding a separate method for updating the object - update(name:surname:age:)
, you can simply update the values individually using the . operator
on object's properties, i.e
var person2 = Person(name: "Tom", surname: "Cruise")
parent.child.append(person2)
person2.name = "Jim"
Since in the above code, instead of assigning a new Person
object to person2
, we're just updating its values.
So, person2
and parent child array's object
will still reference the same object
and hence will print the same name
values.
print(person2.name) //Tom
print(parent.child.last!.name) //Tom
Related Topics
How to Parse JSON in JSON with Swiftyjson
Swift: Skspritekit, Using Storyboards, Uiviewcontroller and Uibutton to Set in Game Parameters
How to Give Dynamic Height to Uitableview
My Button Is Centered for iPhone 6 and 6 Plus, But Not for iPhone 5
Downloading Firebase Storage Files Device Issue
Code Only Retrieving One Value from Data in Firebase
How to Increment Exponentially in Swift
How to Unpack Multiple Levels of Nested JSON in Firebase Database
App Window on Top of All Windows Including Others App Windows
iOS 13 Swiftui: App Crashes Upon Launch on Real Device
Collection View Layout Bug When Selectitem (Swift 5)
Learning Swift: Expressions Are Not Allowed at the Top Level
Undefined Symbol Objc_Class_$ When Creating iOS Framework
Color Ouput with Swift Command Line Tool