Create Spotlight-like window in Swift 4? - swift

I want to create a window as part of my app which is displaying a Spotlight-like window after hitting a certain keystroke.
But all I got is to hide the title:
override func viewWillAppear() {
self.view.window?.titleVisibility = .hidden
self.view.window?.titlebarAppearsTransparent = true
self.view.window?.styleMask.insert(.fullSizeContentView)
}
Which results in:
How can I create such a view with Xcode 10 and Swift?

You can create a custom window using NSPanel
final class Panel: NSPanel {
init(contentRect: NSRect, backing: NSWindow.BackingStoreType, defer flag: Bool) {
super.init(contentRect: contentRect, styleMask: [.titled, .resizable, .closable, .fullSizeContentView], backing: backing, defer: flag)
self.isFloatingPanel = true
self.level = .floating
self.collectionBehavior.insert(.fullScreenAuxiliary)
self.titleVisibility = .hidden
self.titlebarAppearsTransparent = true
self.isMovableByWindowBackground = true
self.isReleasedWhenClosed = false
self.standardWindowButton(.closeButton)?.isHidden = true
self.standardWindowButton(.miniaturizeButton)?.isHidden = true
self.standardWindowButton(.zoomButton)?.isHidden = true
}
// `canBecomeKey` and `canBecomeMain` are required so that text inputs inside the panel can receive focus
override var canBecomeKey: Bool {
return true
}
override var canBecomeMain: Bool {
return true
}
}
and use it in AppDelegate like this:
final class AppDelegate: NSObject, NSApplicationDelegate {
lazy var panel: NSPanel = FloatingPanel(
contentRect: NSRect(x: 0, y: 0, width: 700, height: 320),
backing: .buffered,
defer: false
)
func applicationDidFinishLaunching(_ aNotification: Notification) {
// panel.contentView = ...
panel.makeKeyAndOrderFront(nil)
panel.center()
}
}

Related

How can I override windowDidLoad method in a custom class in order get notified my window did load?

All I am trying in this question is about get notified when window itself did load, in order to try solve the issue i make a sub class of NSWindowController and named it as MyNSWindowController and put my needed code for solving the issue, but Xcode make an error of:
Cannot convert value of type 'NSWindowController' to specified type 'MyNSWindowController'
Here is my codes:
import Cocoa
import SwiftUI
class AppDelegate: NSObject, NSApplicationDelegate {
private var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
buildMainMenu()
buildWindow()
}
func buildWindow() {
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 100.0, height: 100.0),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.setFrameAutosaveName("Main Window")
window.title = "No Storyboard Window"
window.makeKeyAndOrderFront(window)
window.center()
}
func buildMainMenu() {
let appMainMenu: NSMenu = NSMenu()
let mainMenu: NSMenuItem = NSMenuItem()
mainMenu.submenu = NSMenu(title: "MainMenu")
let mainMenuItem0 = NSMenuItem(title: "About", action: #selector(NSApplication.about), keyEquivalent: "a")
mainMenuItem0.keyEquivalentModifierMask = .command
let mainMenuItem1 = NSMenuItem(title: "Close", action: #selector(NSWindow.performClose(_:)), keyEquivalent: "w")
mainMenuItem1.keyEquivalentModifierMask = .command
let mainMenuItem2 = NSMenuItem(title: "Quit", action: #selector(NSApplication.shared.terminate(_:)), keyEquivalent: "q")
mainMenuItem2.keyEquivalentModifierMask = .command
mainMenu.submenu?.items = [mainMenuItem0, mainMenuItem1, mainMenuItem2]
appMainMenu.items = [mainMenu]
NSApp.mainMenu = appMainMenu
}
}
extension NSApplication {
#objc func about() {
AboutView().openInWindow(title: "About My App", sender: self)
}
}
struct AboutView: View {
var body: some View {
Color.green
.frame(width: 500, height: 200)
}
}
extension View {
#discardableResult
func openInWindow(title: String, sender: Any?) -> NSWindow {
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 500.0, height: 500.0),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
// let myNSWindowController: NSWindowController = NSWindowController(window: window)
let myNSWindowController: MyNSWindowController = NSWindowController(window: window)
window.makeKeyAndOrderFront(window)
window.center()
window.isReleasedWhenClosed = false
window.setFrameAutosaveName("Main Window")
window.title = "No Storyboard Window"
window.contentView = NSHostingView(rootView: self)
return window
}
}
class MyNSWindowController: NSWindowController {
override func windowDidLoad() {
print("Window did load!")
}
}
If you think my plan is not good about get notified when window get load, you can come with better answer for solving the issue and goal, I have to say I am trying to find a native way in Cocoa to work as .onAppear modifier which we have in SwiftUI. In SwiftUI .onAppear modifier is for content of view, here in this question i just want to know when the window did load or appear, just the window itself not what carry this window.

EXC_BAD_ACCESS Issue with closing a Window in macOS Cocoa

This codes works well until I want close about window with mouse or using keyboard with command + w, when I close my about window an Error happen:
Error:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x20)
Not sure why this error happen and how to solve the issue! By the way I am using a main file for loading my app like this:
import Cocoa
// **** Main **** //
let nsApplication = NSApplication.shared
let appDelegate = AppDelegate()
nsApplication.delegate = appDelegate
nsApplication.run()
And this is my all codes:
import Cocoa
import SwiftUI
class AppDelegate: NSObject, NSApplicationDelegate {
private var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
buildMainMenu()
buildWindow()
}
func buildWindow() {
window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 100.0, height: 100.0),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.setFrameAutosaveName("Main Window")
window.title = "No Storyboard Window"
window.makeKeyAndOrderFront(window)
}
func buildMainMenu() {
let appMainMenu: NSMenu = NSMenu()
let mainMenu: NSMenuItem = NSMenuItem()
mainMenu.submenu = NSMenu(title: "MainMenu")
let mainMenuItem0 = NSMenuItem(title: "About", action: #selector(NSApplication.about), keyEquivalent: "a")
mainMenuItem0.keyEquivalentModifierMask = .command
let mainMenuItem1 = NSMenuItem(title: "Close", action: #selector(NSWindow.performClose(_:)), keyEquivalent: "w")
mainMenuItem1.keyEquivalentModifierMask = .command
let mainMenuItem2 = NSMenuItem(title: "Quit", action: #selector(NSApplication.shared.terminate(_:)), keyEquivalent: "q")
mainMenuItem2.keyEquivalentModifierMask = .command
mainMenu.submenu?.items = [mainMenuItem0, mainMenuItem1, mainMenuItem2]
appMainMenu.items = [mainMenu]
NSApp.mainMenu = appMainMenu
}
}
extension NSApplication {
#objc func about() {
AboutView().openInWindow(title: "About My App", sender: self)
}
}
struct AboutView: View {
var body: some View {
Color.green
.frame(width: 500, height: 200)
}
}
extension View {
#discardableResult
func openInWindow(title: String, sender: Any?) -> NSWindow {
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.contentView = NSHostingView(rootView: self)
window.title = title
window.makeKeyAndOrderFront(sender)
return window
}
}
One line of code fixes it: window.isReleasedWhenClosed = false added to the function openInWindow(). Reference is here:https://developer.apple.com/forums/thread/651592 . Your original code would destroy the about window object each time it was opened and closed. By not allowing it to do that your code runs without error. Make this substitution.
extension View {
#discardableResult
func openInWindow(title: String, sender: Any?) -> NSWindow {
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.isReleasedWhenClosed = false
window.contentView = NSHostingView(rootView: self)
window.title = title
window.makeKeyAndOrderFront(sender)
return window
}
}

How can I assign my custom NSanimation to an Object in cocoa swift?

I want define and create a value about my custom animation and then assign it to an object. Here in this code I defined my custom animation called myAnimator now I want assign this custom animation to window size change which use a default animation. Here is situation the window size change is 500 or 250, I cannot use this hard coded values because this values could be change, the only thing that i can use is my created animation called myAnimator, the goal of this question is finding a way for animation assigning via created a custom NSAnimnation-constant like myAnimator.
Here the code for macOS Cocoa:
import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate {
private let window: NSWindow = NSWindow()
var myAnimator = NSAnimation(duration: 2.0, animationCurve: .linear)
var size: CGFloat = 500.0
var blurView: NSVisualEffectView = NSVisualEffectView()
func applicationDidFinishLaunching(_ aNotification: Notification) {
window.styleMask = [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView]
window.backingType = .buffered
window.title = "No Storyboard Window"
window.isReleasedWhenClosed = false
window.isMovableByWindowBackground = true
window.center()
window.setFrameAutosaveName("Main Window")
let nsView: NSView = NSView()
nsView.wantsLayer = true
nsView.layer?.backgroundColor = NSColor.red.cgColor
let nsButton: NSButton = NSButton()
nsButton.title = "update"
nsButton.action = #selector(self.nsButton_Action)
window.contentView = nsView
window.contentView?.addSubview(nsButton)
nsButton.translatesAutoresizingMaskIntoConstraints = false
nsButton.centerXAnchor.constraint(equalTo: window.contentView!.centerXAnchor, constant: 0).isActive = true
nsButton.centerYAnchor.constraint(equalTo: window.contentView!.centerYAnchor, constant: 0).isActive = true
window.makeKeyAndOrderFront(window)
}
#IBAction func nsButton_Action(_ sender: NSButton) {
if size == 500.0 { size = 250.0 }
else { size = 500.0 }
updateFunc()
}
func updateFunc() {
window.setFrame(NSRect(origin: window.frame.origin, size: CGSize(width: size, height: size)), display: true, animate: true)
}
}

Where is the correct place to build the needed Window for app in Cocoa macOS

I am confused to find the concert way for building the needed Window for app, so one of source say that I have to use this down code:
import Cocoa
import SwiftUI
class ViewController: NSViewController {
override func viewWillAppear() {
self.view = NSHostingView(rootView: ContentView())
self.view.window?.makeKeyAndOrderFront(nil)
self.view.window?.standardWindowButton(NSWindow.ButtonType.zoomButton)?.isHidden = true
self.view.window?.standardWindowButton(NSWindow.ButtonType.miniaturizeButton)?.isHidden = true
self.view.window?.standardWindowButton(NSWindow.ButtonType.closeButton)?.isHidden = true
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
And some other source says I have use this code:
#main
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 300, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.center()
window.setFrameAutosaveName("Main Window")
window.contentView = NSHostingView(rootView: ContentView())
window.makeKeyAndOrderFront(nil)
window.standardWindowButton(NSWindow.ButtonType.zoomButton)?.isHidden = true
window.standardWindowButton(NSWindow.ButtonType.miniaturizeButton)?.isHidden = true
window.standardWindowButton(NSWindow.ButtonType.closeButton)?.isHidden = true
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
}
Which I am really confused which one is needed and why? can some one explain it to me?

Fullscreen button disabled

I'm newbe in Cocoa Apps development. My fullscreen button is disabled for some unknown reason. Probably the problem is in styleMask. Here is my code:
class AppDelegate: NSObject, NSApplicationDelegate {
lazy var window = NSWindow(contentRect:
NSRect(center: NSScreen.main?.frame.center ?? .zero, size: CGSize(width: 800, height: 450)),
styleMask: [.titled, .miniaturizable, .closable, .fullSizeContentView],
backing: NSWindow.BackingStoreType.buffered,
defer: false)
func applicationDidFinishLaunching(_ aNotification: Notification) {
let vc = Router.getPhotoBrowserNSViewController()
window.contentViewController = vc
window.delegate = NSWindowHandler()
window.titlebarAppearsTransparent = true
window.isMovableByWindowBackground = true
window.makeKeyAndOrderFront(nil)
window.maxFullScreenContentSize = NSScreen.main?.frame.size ?? .zero
window.minFullScreenContentSize = CGSize(width: 800, height: 450)
}
}
And here is the result:
https://imgur.com/NE4fgfG
Any Help would be appreciated.
You need to set the window's collectionBehavior to an option set that includes .fullScreenPrimary.