Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 0 additions & 26 deletions Example/HostingExample/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,30 +63,4 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
}
}

final class WindowController: NSWindowController {
init() {
super.init(window: nil)
}

@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override var windowNibName: NSNib.Name? { "" }

lazy var viewController = ViewController()

override func loadWindow() {
window = NSWindow(contentRect: .init(x: 0, y: 0, width: 500, height: 300), styleMask: [.titled, .closable, .miniaturizable], backing: .buffered, defer: false)
window?.center()
}

override func windowDidLoad() {
super.windowDidLoad()

contentViewController = viewController
}
}

#endif
24 changes: 11 additions & 13 deletions Example/HostingExample/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import UIKit
import AppKit
#endif

import OpenSwiftUI

#if os(iOS) || os(visionOS)
class ViewController: UINavigationController {
override func viewDidAppear(_ animated: Bool) {
Expand Down Expand Up @@ -51,21 +49,21 @@ final class EntryViewController: UIViewController {
}
}
#elseif os(macOS)
class ViewController: NSViewController {
override func loadView() {
view = NSHostingView(rootView: ContentView())
final class WindowController: NSWindowController {
init() {
super.init(window: nil)
}

override func viewDidLoad() {
super.viewDidLoad()

view.frame = .init(x: 0, y: 0, width: 500, height: 300)
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
override var windowNibName: NSNib.Name? { "" }

override func loadWindow() {
window = NSWindow(contentViewController: NSHostingController(rootView: ContentView().frame(width: 500, height: 300)))
window?.center()
}
}
#endif
13 changes: 5 additions & 8 deletions Sources/OpenSwiftUI/App/App/AppKit/AppKitAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,13 @@ class AppDelegate: NSResponder, NSApplicationDelegate {
// FIXME
let items = AppGraph.shared?.rootSceneList?.items ?? []
let view = items[0].value.view
let hostingVC = NSHostingController(rootView: view)
let hostingVC = NSHostingController(rootView: view.frame(width: 500, height: 300))
let windowVC = WindowController(hostingVC)
windowVC.showWindow(nil)
self.windowVC = windowVC
}

var windowVC: NSWindowController?
}

// FIXME: frame is zero
Expand All @@ -82,15 +85,9 @@ final class WindowController<Content>: NSWindowController where Content: View {
let hostingVC: NSHostingController<Content>

override func loadWindow() {
window = NSWindow(contentRect: .init(x: 0, y: 0, width: 500, height: 300), styleMask: [.titled, .closable, .miniaturizable], backing: .buffered, defer: false)
window = NSWindow(contentViewController: hostingVC)
window?.center()
}

override func windowDidLoad() {
super.windowDidLoad()
contentViewController = hostingVC
hostingVC.host.frame = window!.frame
}
}

// MARK: - App Utils
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@

public import AppKit

/// An AppKit view controller that hosts SwiftUI view hierarchy.
/// An AppKit view controller that hosts OpenSwiftUI view hierarchy.
///
/// Create an `NSHostingController` object when you want to integrate SwiftUI
/// views into an AppKit view hierarchy. At creation time, specify the SwiftUI
/// Create an `NSHostingController` object when you want to integrate OpenSwiftUI
/// views into an AppKit view hierarchy. At creation time, specify the OpenSwiftUI
/// view you want to use as the root view for this view controller; you can
/// change that view later using the ``NSHostingController/rootView`` property.
/// Use the hosting controller like you would any other view controller, by
Expand All @@ -26,10 +26,10 @@ public import AppKit
open class NSHostingController<Content>: NSViewController where Content: View {
var host: NSHostingView<Content>

/// Creates a hosting controller object that wraps the specified SwiftUI
/// Creates a hosting controller object that wraps the specified OpenSwiftUI
/// view.
///
/// - Parameter rootView: The root view of the SwiftUI view hierarchy that
/// - Parameter rootView: The root view of the OpenSwiftUI view hierarchy that
/// you want to manage using the hosting view controller.
public init(rootView: Content) {
// TODO
Expand All @@ -39,11 +39,11 @@ open class NSHostingController<Content>: NSViewController where Content: View {
}

/// Creates a hosting controller object from an archive and the specified
/// SwiftUI view.
/// OpenSwiftUI view.
///
/// - Parameters:
/// - coder: The decoder to use during initialization.
/// - rootView: The root view of the SwiftUI view hierarchy that you want
/// - rootView: The root view of the OpenSwiftUI view hierarchy that you want
/// to manage using this view controller.
public init?(coder: NSCoder, rootView: Content) {
// TODO
Expand All @@ -54,6 +54,7 @@ open class NSHostingController<Content>: NSViewController where Content: View {

func _commonInit() {
host.viewController = self
self.view = host
}

/// Creates a hosting controller object from the contents of the specified
Expand All @@ -68,11 +69,9 @@ open class NSHostingController<Content>: NSViewController where Content: View {
public required init?(coder: NSCoder) {
preconditionFailure("init(coder:) must be implemented in a subclass and call super.init(coder:, rootView:)")
}

open override func loadView() {
view = host
}


/// The root view of the OpenSwiftUI view hierarchy managed by this view
/// controller.
public var rootView: Content {
get { host.rootView }
set { host.rootView = newValue }
Expand All @@ -88,23 +87,23 @@ open class NSHostingController<Content>: NSViewController where Content: View {
}

/// The options for how the hosting controller's view creates and updates
/// constraints based on the size of its SwiftUI content.
/// constraints based on the size of its OpenSwiftUI content.
///
/// NSHostingController can create minimum, maximum, and ideal (content
/// size) constraints that are derived from its SwiftUI view content. These
/// size) constraints that are derived from its OpenSwiftUI view content. These
/// constraints are only created when Auto Layout constraints are otherwise
/// being used in the containing window.
///
/// If the NSHostingController is set as the `contentViewController` of an
/// `NSWindow`, it will also update the window's `contentMinSize` and
/// `contentMaxSize` based on the minimum and maximum size of its SwiftUI
/// `contentMaxSize` based on the minimum and maximum size of its OpenSwiftUI
/// content.
///
/// `sizingOptions` defaults to `.standardBounds` (which includes
/// `minSize`, `intrinsicContentSize`, and `maxSize`), but can be set to an
/// explicit value to control this behavior. For instance, setting a value
/// of `.minSize` will only create the constraints necessary to maintain the
/// minimum size of the SwiftUI content, or setting a value of `[]` will
/// minimum size of the OpenSwiftUI content, or setting a value of `[]` will
/// create no constraints at all.
///
/// If a use case can make assumptions about the size of the
Expand All @@ -113,7 +112,7 @@ open class NSHostingController<Content>: NSViewController where Content: View {
/// fewer options can improve performance as it reduces the amount of layout
/// measurements that need to be performed. If an `NSHostingController` has
/// a `frame` that is smaller or larger than that required to display its
/// SwiftUI content, the content will be centered within that frame.
/// OpenSwiftUI content, the content will be centered within that frame.
public var sizingOptions: NSHostingSizingOptions {
get { host.sizingOptions }
set { host.sizingOptions = newValue }
Expand All @@ -127,6 +126,55 @@ open class NSHostingController<Content>: NSViewController where Content: View {
set { host.safeAreaRegions = newValue }
}

/// The options for which aspects of the window will be managed by this
/// controller's hosting view.
///
/// `NSHostingController` will populate certain aspects of its associated
/// window, depending on which options are specified.
///
/// For example, a hosting controller can manage its window's toolbar by
/// including the `.toolbars` option:
///
/// struct RootView: View {
/// var body: some View {
/// ContentView()
/// .toolbar {
/// MyToolbarContent()
/// }
/// }
/// }
///
/// let controller = NSHostingController(rootView: RootView())
/// controller.sceneBridgingOptions = [.toolbars]
///
/// When this hosting controller is set as the `contentViewController` for a
/// window, the default value for this property will be `.all`, which
/// includes the options for `.toolbars` and `.title`. Otherwise, the
/// default value is `[]`.
public var sceneBridgingOptions: NSHostingSceneBridgingOptions {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs for sceneBridgingOptions say the default becomes .all when used as a window’s contentViewController, but the current implementation only forwards to host.sceneBridgingOptions (which defaults to []). This looks like a docs/behavior mismatch that could confuse callers relying on the documented default.

Severity: low

Other Locations
  • Sources/OpenSwiftUI/Integration/Hosting/AppKit/View/NSHostingView.swift:102

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

get { host.sceneBridgingOptions }
set { host.sceneBridgingOptions = newValue }
}

open override var preferredContentSize: NSSize {
get {
if sizingOptions.contains(.preferredContentSize) {
return host.idealSize()
} else {
return super.preferredContentSize
}
}
set {
super.preferredContentSize = newValue
}
}

open override var identifier: NSUserInterfaceItemIdentifier? {
didSet {
host.identifier = identifier
}
}

/// Calculates and returns the most appropriate size for the current view.
///
/// - Parameter size: The proposed new size for the view.
Expand All @@ -140,6 +188,10 @@ open class NSHostingController<Content>: NSViewController where Content: View {
return result
}

public func _render(seconds: Double) {
host.render(interval: seconds, targetTimestamp: nil)
}

public func _forEachIdentifiedView(body: (_IdentifiedViewProxy) -> Void) {
host.forEachIdentifiedView(body: body)
}
Expand Down
Loading
Loading