Home > OS >  How can I update my Window title for macOS Storyboard Cocoa project via an action?
How can I update my Window title for macOS Storyboard Cocoa project via an action?

Time:02-01

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)
  }
}
  • Related