The Xcode 13.4.1 on macOS Monterery 12.4 can use only WindowGroup
and DocumentGroup
to implement the window generation in SwiftUI, so we can’t create the single window application. You can’t implement the all of the items in the menu bar with only SwiftUI.
In the future, all of this will be resolved I think. You will be able to implement them with only SwiftUI, but this time (July 10, 2022), you need to implement them with the AppKit.
This article explains that how to add the SwiftUI view into the AppKit window.
Xcode Project Template
Since macOS Big Sur 11, You can use @main
in the SwiftUI application, and in Xcode 12, You could select the SwiftUI
from the Interface
, and SwiftUI App
or AppKit App Delegate
from the Life Cycle
.
However, The Life Cycle
option was removed from the project option dialog in Xcode 13, if you select the SwiftUI
from the Interface
option, Xcode generate the code which is using SwiftUI App
life cycle.
SwiftUI App
can’t present the enough features on the macOS 12 Monterey. For example, we can’t create the following functions with only SwiftUI App Life cycle.
- The single window style application.
- Full customize the menu bar, it can only partially.
- The document application directly access to the file at any time.
I believe that the improvements for creating the macOS Apps will be done in the future, but at this time, it is a realistic that we create the application with AppKit App Delegate
life cycle.
Create the project
Create the project. Set the Interface
option to the Storyboard
.
Put the SwiftUI
Put the SwiftUI view on ViewController
. We can use NSHostingController
class to do it. The NSHostingController
is a view controller that can contains SwiftUI view as a subview.
The code of the ViewController
is following.
import Cocoa
import SwiftUI
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.translatesAutoresizingMaskIntoConstraints = false
// Create the NSHostingController
let controller = NSHostingController(rootView:
Text("This is a SwiftUI View.")
.frame(width: 200, height: 200, alignment: .center)
)
controller.view.translatesAutoresizingMaskIntoConstraints = false
// Put the ViewController
addChild(controller)
view.addSubview(controller.view)
// Configure the auto-layout to fit the width and the height to the ViewController.
NSLayoutConstraint.activate([
controller.view.leftAnchor.constraint(equalTo: view.leftAnchor),
controller.view.topAnchor.constraint(equalTo: view.topAnchor),
controller.view.widthAnchor.constraint(equalTo: view.widthAnchor),
controller.view.heightAnchor.constraint(equalTo: view.heightAnchor)
])
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
Create the SwiftUI view
Pass the SwiftUI view to the rootView
argument of the NSHostingController
initializer. You can specify any view. In the sample code, we use Text
but you will specify your own view in normal.
let controller = NSHostingController(rootView:
Text("This is a SwiftUI View.")
.frame(width: 200, height: 200, alignment: .center)
)
Put the NSHostingController
The NSHostingController
class is an AppKit view controller which is presented by the SwiftUI framework.
As the NSHostingController
is an inheritance of the NSViewController
class, so you can put it as a sub view controller and a sub view as same as other AppKit view controllers.
addChild(controller)
view.addSubview(controller.view)
Configure the auto-layout
The NSHostingController
need to be layout in AppKit rule, so we use the AppKit Auto Layout to fit the width and the height of the NSHostingController
to of ViewController
.
First, set the translateAutoresizingMaskIntoConstraints
property to false
.
view.translatesAutoresizingMaskIntoConstraints = false
controller.view.translatesAutoresizingMaskIntoConstraints = false
Then, configure the auto-layout.
NSLayoutConstraint.activate([
controller.view.leftAnchor.constraint(equalTo: view.leftAnchor),
controller.view.topAnchor.constraint(equalTo: view.topAnchor),
controller.view.widthAnchor.constraint(equalTo: view.widthAnchor),
controller.view.heightAnchor.constraint(equalTo: view.heightAnchor)
])
Running result
Run the sample code above, the window will be show as follows.
ウインドウのサイズ
The ViewController
is set to window content
segure.
If you configure the auto-layout as like the sample code, the window size will be following SwiftUI view.
For example, the following sample code will set the height of the ViewController
to 200px
and the window height will be accordance.
let controller = NSHostingController(rootView:
Text("This is a SwiftUI View.")
.frame(width: 200, height: 200, alignment: .center)
)
In macOS Monterey, the width will be 200px
and the height will be 228px
, it is not allowed that resize it. Change the code to the following.
let controller = NSHostingController(rootView:
Text("This is a SwiftUI View.")
.frame(minWidth: 100, idealWidth: 200, maxWidth: 300,
minHeight: 100, idealHeight: 200, maxHeight: 500, alignment: .center)
)
Then, the window will be resizable, the minimum size will be 100px * 100px
and the maximum size will be 300px * 500px
.
For more information about the changing window size in SwiftUI, see following article.