This is my project:
import Cocoa
import SwiftUI
var appName: String = "My App Name"
class ViewController: NSViewController {
override func viewWillAppear() {
let controller = NSHostingController(rootView: ContentView())
self.view = controller.view
self.view.window?.title = appName
}
}
struct ContentView: View {
var body: some View {
VStack {
Button("Change") {
appName = " updated!"
print(appName)
}
}
.frame(width: 400.0, height: 300.0)
}
}
My goal is be able to update my window title, I am be able to update the variable that holds my app name but since viewWillAppear function would not be triggered I am unable to update my window title there. I was thinking to use a notification there but in this case I am not sure if it is the right feet there, because it would need to post and receive notification, what is the approach for solving this problem?
CodePudding user response:
You can introduce a ViewModel storing title
as @Published
and from the Button action update that property rather than a global appName
. In your hosting controller, you can subscribe to changes of title
, since it is @Published
and update the window's title whenever the view model's title
property is updated.
import Cocoa
import Combine
import SwiftUI
var appName: String = "My App Name"
final class ViewModel: ObservableObject {
@Published var title: String = appName
}
class ViewController: NSViewController {
private let viewModel = ViewModel()
private var titleObservation: AnyCancellable?
override func viewDidLoad() {
super.viewDidLoad()
// Observe the title of the view model and update the window's title whenever it changes
titleObservation = viewModel.$title.sink { [weak self] newTitle in
self?.view.window?.title = newTitle
}
}
override func viewWillAppear() {
let controller = NSHostingController(rootView: ContentView(viewModel: viewModel))
self.view = controller.view
self.view.window?.title = viewModel.title
}
}
struct ContentView: View {
@ObservedObject private var viewModel: ViewModel
init(viewModel: ViewModel) {
self.viewModel = viewModel
}
var body: some View {
VStack {
Button("Change") {
viewModel.title = " updated!"
}
}
.frame(width: 400.0, height: 300.0)
}
}
Alternatively, you can simply inject a closure for the Button
action into your view from the hosting controller and update the window's title from the closure.
class ViewController: NSViewController {
override func viewWillAppear() {
let contentView = ContentView(
onButtonTap: { // this closure will be executed every time Button is tapped
appName = " updated!"
self.view.window?.title = appName
print(appName)
}
)
let controller = NSHostingController(rootView: contentView)
self.view = controller.view
self.view.window?.title = appName
}
}
struct ContentView: View {
// Closure to execute when `Button` is tapped
private let onButtonTap: () -> Void
init(onButtonTap: @escaping () -> Void) {
self.onButtonTap = onButtonTap
}
var body: some View {
VStack {
Button("Change", action: onButtonTap)
}
.frame(width: 400.0, height: 300.0)
}
}