How can I make a window take over the entire screen? MacOS SwiftUI - swift

How can I make a window that is the fullscreen of the MacOS screen that goes over the menuBar, and goes over the applications at the bottom. I saw functions like 'setMenuBarVisible'; however this does not work in SwiftUI. Any help would be appreciated

This code will make your app toggle fullscreen on launch.
WindowGroup {
ContentView()
.onAppear {
DispatchQueue.main.async {
if let window = NSApplication.shared.windows.last {
window.toggleFullScreen(nil)
}
}
}
}
I'm not sure if this is the answer you are looking for but.

Related

Is it possible to start the NavigationSplitView in an expanded state on macOS using SwiftUI?

I like the sidebar to be opened at launch.
However when I build and run the app, this is what I get.
So I need to click on the sidebar icon to show it. This is not the behavior I want. Is it possible to change this?
Somehow, without explicitly setting it in code, the app likes to change the column visibility to .detailOnly at launch. To avoid this behavior, I explicitly set it to .all at onAppear
#State private var columnVisibility =
NavigationSplitViewVisibility.all
var body: some View {
NavigationSplitView(columnVisibility: $columnVisibility) {
Text("Side bar")
} detail: {
Text("Main part")
}
.onAppear() {
columnVisibility = .all
}
}

Toggle Sidebar in Code using SwiftUI NavigationView on iPad

I'm trying to utilize the built-in sidebar from SwiftUI 2.0 by using NavigationView like this:
NavigationView {
MainView()
ListView()
DetailView()
}.navigationBarHidden(true)
But since I want to use my own Custom Back Button, I've hidden the NavigationBar and tried to toggle the sidebar with code which doesn't work.
self.presentationMode.wrappedValue.dismiss()
I've already seen a lot of solutions for macOS:
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
But I can't seem to find equivalent for iPad, thanks in advance.
I used this code to change the default sidebar settings:
extension UISplitViewController {
open override func viewDidLoad() {
super.viewDidLoad()
self.preferredDisplayMode = .secondaryOnly
self.preferredSplitBehavior = .overlay
}
}
self exposes several sidebar methods and properties that can be used. I hope it will be useful!
So this is not a good long term solution but if you are like me and 100% needed the native approach to work here's how it can be hacked. Using https://github.com/siteline/SwiftUI-Introspect you can find the right view controller in the hierarchy and set the display mode.
Text("Some View").introspectViewController { vc in
guard let splitVC = vc.parent?.parent as? UISplitViewController else {
return
}
splitVC.preferredDisplayMode = .oneBesideSecondary
}
This is BRITTLE but it works.

SwiftUI onMoveCommand actions aren't executed

I'm making a macOS app with SwiftUI, and I would like to offset a SwiftUI view using the arrow keys on the built-in keyboard.
I couldn't find many resources online, but onMoveCommand() appears to be the event handler I need. Upon trying it out, I discovered that the action I specified for onMoveCommand() does not appear to be executed. Here's some code I wrote just to test it out:
struct ContentView: View {
var body: some View {
Text("Hello")
.onAppear() {
print("Appeared!")
}
.onMoveCommand() { (direction) in
print("Moved!")
}
.onTapGesture() {
print("Tapped!")
}
}
}
onMoveCommand() does not print "Moved!" when I press the arrow keys, instead I get the error alert sound played, and nothing is printed. onAppear() successfully prints the "Appeared!" message when the view appears, and onTapGesture() prints "Tapped!" correctly whenever I click the text. This seems to tell me that the basic syntax I got for these view events is correct, but I implemented onMoveCommand() incorrectly.
For now I only want my app to print something to the Xcode console when the arrow keys are pressed, and to be able to distinguish which arrow key was pressed. Can someone please explain what I did wrong?
Keyboard events are handled only by view in focus, so fix is
var body: some View {
Text("Hello")
.focusable() // << here !!
.onAppear() {
print("Appeared!")
}
.onMoveCommand() { (direction) in
print("Moved!")
}
.onTapGesture() {
print("Tapped!")
}
}
Tested with Xcode 11.4 / macOS 10.15.4. Make sure you have turned on keyboard navigation in System Preferences.

SwiftUI - Making a View focusable and trigger an action on macOS

How is it possible to make a View on macOS focusable. I tried the it like below but the action is never triggered. I know that for NSView you have to implement acceptsFirstResponder to return true but can not find similar in SwiftUI.
Is this still a Beta related bug or a missing functionality for macOS ?
struct FocusableView: View {
var body: some View {
Group {
Text("Hello World!")
.padding(20)
.background(Color.blue.cornerRadius(8))
.focusable(true) { isFocused in
print("Focused", isFocused)
}
}
}
}
I think I found a work around for the problem to shift the focus to a SwiftUI view.
I am working on macOS Catalina, 10.15, Swift 5, Xcode 11.1
The problem is to shift the focus to a SwiftUI view. I could imagine that this has not been perfectly implemented, yet.
Only upon shifting the focus to the required SwiftUI view within the SwiftUI framework the onFocusChange closure of a focusable view will be called and the view will be focused.
My intention was: when I click into the SwiftUI view I want to execute the onFocusChange closure and I want it to be focused (for subsequent paste commands)
My SwiftUI view is built into the Window using an NSHostingView subclass - ImageHostingView.
When I add this ImageHostingView to the view hierarchy I define its previous key view:
theImageHostingView.superview.nextKeyView = theImageHostingView
I added a mouseDown handler to the ImageHostingView:
override dynamic func mouseDown(with event: NSEvent) {
if let theWindow = self.window, let theView = self.previousValidKeyView {
theWindow.selectKeyView(following:theView)
}
}
Calling the NSWindow's selectKeyView(following:) triggers within the SwiftUI framework a focus change to the desired view (the root view of ImageHostingView).
This is clearly a work-around and works only for final views which will be represented by a NSHostingView. But it highlights the problem (shifting the focus to a SwiftUI view upon a certain action) and it might be helpful in many cases.
The only way I've been able to get this to work is by checking the "Use keyboard navigation to move focus between controls" checkbox in Keyboard System Preferences

OSX Application: how to make window maximized?

I am working on a mac application, and I like to make initial window be in maximized state, like when you are pressing green button with plus sign.
I don't want it to be full screen.
An app in its zoomed state is not the same thing as "maximized." The green plus icon indicates zoom, which means "the appropriate size for this content." In some applications that's the visible frame (as Eric D. discusses), but it can be almost anything. Try zooming a Safari window for instance.
Assuming you really want "maximized" and not "zoom", then Eric is on the right track, but it can be done better. First, you should use the window's screen if it has one. Also, you should not animate the window resize during launch (since that can look awkward on launch).
func applicationDidFinishLaunching(aNotification: NSNotification) {
if let screen = window.screen ?? NSScreen.mainScreen() {
window.setFrame(screen.visibleFrame, display: true)
}
}
You may want to consider using a NSWindowController to manage this rather than putting it in the application delegate. In that case, you can put this in windowDidLoad. Window controllers are a pretty common tool in AppKit (as opposed to view controllers, which are not historically as common).
If you actually want zoom behavior, familiarize yourself with the the NSWindowDelegate method windowWillUseStandardFrame(_:defaultFrame:). You shouldn't generally call zoom(_:) directly on launch because that will animate, but whatever logic you do in the delegate should be used to compute your frame. Again, make sure to adjust your frame to live on the window's screen if it has one, rather than the main screen.
Ideally, you really should be honoring the last frame that the user used rather than forcing it to the visible frame. That's called frameAutosave in Cocoa if you want to research that more. A window controller will help you manage that somewhat automatically if you just set a autosave name in Interface Builder. (Though it's slightly complicated by needing to compute the frame on first launch to get the visible frame, so it won't be completely automatic.)
Do give some careful thought before making your default frame be the visible frame in any case. That can be really enormous on large monitors (there are still a lot of 30" Cinema displays out there, but even on a 27" it can be pretty overwhelming). Sometimes that's fine depending on your app, but I often find that it's worth defining a maximum initial size (while allowing the user to make it larger).
You can "zoom" a window to the max available space by using NSScreen's visibleFrame as the target frame. Let's say window is your NSWindow IBOutlet:
if let screen = NSScreen.mainScreen() {
window.setFrame(screen.visibleFrame, display: true, animate: true)
}
For example, in the AppDelegate.swift:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(aNotification: NSNotification) {
if let screen = NSScreen.mainScreen() {
window.setFrame(screen.visibleFrame, display: true, animate: true)
}
}
in Swift 4.2:
class ViewController: NSViewController {
override func viewDidAppear() {
super.viewDidAppear()
view.window?.zoom(self) //bespread the screen
//view.window?.toggleFullScreen(self) //fullscreen
}
2020 | SWIFT 5.1:
use extension:
extension NSWindowController {
func maximize() { self.window?.zoom(self) }
}
just call maximize() of NSWindowController instance :)
Swift 5
If anyone's still having issues, trying calling the zoom function the main thread. Worked for me.
DispatchQueue.main.async {
self.view.window?.zoom(self)
}
Hi Guys I really appreciate your help.
I am working on a document based mac application. I put the code you provided in the makeWindowControllers() of Document class and it works like a charm.
Thank you very much. Here is the code I use.
override func makeWindowControllers() {
// Returns the Storyboard that contains your Document window.
let storyboard = NSStoryboard(name: "Main", bundle: nil)
let windowController = storyboard.instantiateControllerWithIdentifier("Document Window Controller") as! NSWindowController
self.addWindowController(windowController)
if let screen = NSScreen.mainScreen() {
windowController.window?.setFrame(screen.visibleFrame, display: true, animate: true)
}
}
this code works well only on single-windowed application, but it's really easy to edit to work with multy-windowed application
usage to maximize and unmaximize window:
TheApp.maximized.toggle()
Source code
public class TheApp {
static var maximized: Bool {
get {
guard let visibleFrame = NSScreen.main?.visibleFrame,
let window = NSApp.mainWindow
else { return false }
return window.frame == visibleFrame
}
set { NSApp.mainWindow?.zoom(newValue) }
}
static var fullscreen: Bool {
get {
guard let screenFrame = NSScreen.main?.frame,
let window = NSApp.mainWindow
else { return false }
return window.frame == screenFrame
} set {
NSApp.mainWindow?.toggleFullScreen(newValue)
}
}
static var mimimized: Bool {
get { NSApp.mainWindow?.isMiniaturized ?? false }
set { NSApp?.mainWindow?.miniaturize(newValue) }
}
}