Restore menubar after exiting full screen NSView - swift

I am setting an NSView in fullscreen mode and I hide the menubar without problem. My issue is when exiting fullscreen mode. How can I restore the menubar? I tried with and without options but result remains the same: menubar is missing:
func exitFullScreen() {
let presOptions: NSApplication.PresentationOptions = [.hideDock, .hideMenuBar]
let optionsDictionary = [NSView.FullScreenModeOptionKey.fullScreenModeApplicationPresentationOptions: NSNumber(value: presOptions.rawValue), NSView.FullScreenModeOptionKey.fullScreenModeAllScreens: false]
presentationWindowController.window?.contentView?.exitFullScreenMode(options: optionsDictionary)
}

Actually, I just needed to set the app presentation options again:
NSApplication.shared.presentationOptions = []

Related

SwiftUI alert and confirmation dialog preferredColorScheme

I've noticed when setting the preferredColorScheme in my SwiftUI app the .alert and .confirmationDialog modifiers don't use the colorScheme. Also the launch screen doesn't use the colorScheme either. It seems to be using system defaults, is this normal behaviour?
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.preferredColorScheme(.light)
}
}
}
I was also unable to get better results using UIKit's overrideUserInterfaceStyle. The launch screen is still using system defaults, unsure how to solve that in SwiftUI without a launch screen. Also the code becomes pretty ugly and much more extensive.
guard let scene = scenes.first as? UIWindowScene else { return }
scene.keyWindow?.overrideUserInterfaceStyle = .light // .light .dark .unspecified
I couldn't figure out a pure SwiftUI way, but I found a simple solution. Use UIKit, then handle the changes in SwiftUI. I just needed to use .overrideUserInterfaceStyle. As for the Launch Screen in SwiftUI, add a Background color key to the dictionary. Then add a Color setfrom your assets and set the Background color key value to the name of the asset created as a String.
let scenes = UIApplication.shared.connectedScenes
guard let scene = scenes.first as? UIWindowScene else { return nil }
scene.keyWindow?.overrideUserInterfaceStyle = .light //.dark, .light, .unspecified

Statusbar.image has wrong color

I am using following code for my status bar image:
let icon = NSImage(imageLiteralResourceName:"flag")
statusBarItem.image = icon
This leads to wrong color for certain background colors / modes. In the picture, what's white should be black. The image resource is white/transparent. If I change that, I get the same problem. All other status bar images will turn white on certain configurations, mine will stay black.
I was thinking that MacOS would add effects to make all statusbar icons look uniform on it's own, but apparently thats not the case...
Any ideas how to fix that?
Thanks!
MacOs can do what you want. I recommend reading Apple documentation:
https://developer.apple.com/documentation/uikit/appearance_customization/supporting_dark_mode_in_your_interface
Basically you have 2 options if you don‘t provide the code manually.
Option 1. In Xcode, navigate to your image asset in assets.xcassets. In the attributes pane, in „Render as…“ specify „Template Image“. This worked well for my menu bar app.
Option 2. Supply different versions of your icon in one image asset, macOs will then choose the appropriate version.
I found a solution. Again I realize that MacOS development is way less supported by Apple than iOS. I think the color adjustment of statusbar icons should be the task of the operating system, but Apple lets the developer do the work. Whatever.
Here is the solution:
You have to provide two versions of your icon, one in black, the other in white.
When the app launches, you have to check wether the user's MacOs is in dark or light mode. This can be done with following code:
let mode = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")
if (mode == "Dark"){
let icon = NSImage(imageLiteralResourceName:"flag")
statusBarItem.image = icon
} else {
let icon = NSImage(imageLiteralResourceName:"flagDark")
statusBarItem.image = icon
}
One problem remains here now: When the user changes the mode while your app is running, the icon color won't update. Also: If the user uses the automatic mode (i.e. it's light at day and dark in the night), the icon color won't switch as well.
You can tackle that problem by listening to a certain notification that is fired when the dark mode settings changes:
DistributedNotificationCenter.default.addObserver(self, selector: #selector(updateIcon), name: NSNotification.Name(rawValue: "AppleInterfaceThemeChangedNotification"), object: nil)
#objc func updateIcon(){
print("updateIcon ausgeführt")
let mode = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")
if (mode == "Dark"){
let icon = NSImage(imageLiteralResourceName:"flag")
statusBarItem.image = icon
} else {
let icon = NSImage(imageLiteralResourceName:"flagDark")
statusBarItem.image = icon
}
}
In my tests, this worked in all scenarios.
For me this worked:
class AppDelegate: NSObject, NSApplicationDelegate {
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
// ...
func applicationDidFinishLaunching(_ aNotification: Notification) {
// ...
if let button = statusItem.button {
let image = NSImage(named: NSImage.Name("TrayIcon"))
image?.isTemplate = true
button.image = image
}
// ...
}
// ...
}

Changing AVPlayerView background color

Is it possible to change the background color of a AVPlayerView when used in a macOS application. I want to do this to remove the black bars when playing a video.
I've tried the following:
videoView.contentOverlayView?.wantsLayer = true
videoView.contentOverlayView?.layer?.backgroundColor = NSColor.blue.cgColor
also tried adding these:
view.wantsLayer = true
videoView.wantsLayer = true
but the background is still black.
AVPlayerView does not have layer after the initialization or setting wantsLayer property, but creates it later at some point. I was able to change the background with the next code in my AVPlayerView subclass:
override var layer: CALayer? {
get { super.layer }
set {
newValue?.backgroundColor = CGColor.clear
super.layer = newValue
}
}
videoView.contentOverlayView?.layer?.setNeedsDisplay()
try this maybe you just need to update the view. However if you would post more of your code I could try to help more.

Is it possible to (programmatically) set wallpapers for each separate "space"/desktop in macOS?

I'm making a small app for myself to change the desktop background image periodically.
My program contains this block of code:
let screen = NSScreen.main()!
let newWallpaperURL = URL(/* ... */)
// ...
try! NSWorkspace.shared().setDesktopImageURL(newWallpaperURL, for: screen, options: [:])
This works, but only for the current "space" the keyboard is focused on.
e.g. if I'm in a fullscreen app, only the background of the Space occupied fullscreen app will be changed (not the background of my normal desktop).
If I have two Spaces/desktops, it only changes the background image of one of them.
Is it possible to individually set wallpapers for each space programmatically?
You can get all the screens and set all of them.
let screens = NSScreen.screens
let newWallpaperURL = URL(/* ... */)
for i in screens {
try! NSWorkspace.shared().setDesktopImageURL(newWallpaperURL, for: i, options: [:])
}
Use this in Xcode 8.x:
if let screens = NSScreen.screens() {
let newWallpaperURL = URL(/* ... */))
for screen in screens {
try? NSWorkspace.shared().setDesktopImageURL(newWallpaperURL, for: screen, options: [:])
}
}
Unlike other solutions posted here this one will work in the current Xcode 8. NSScreen.screens is a class var in Xcode 9 (currently beta) but a class func in Xcode 8 which is why you need to put .screens() instead of .screens. Also, screens returns an optional so you need to safely unwrap it before passing it to the for loop.

Xcode 7 UI Testing - can't type text into search bar

The following UI Test code will successfully tap the UISearchBar element. The software keyboard appears and the search bar looks like it has focus. (ie. it animates as if someone tapped it)
let searchBar = XCUIApplication().otherElements["accessibility_label"]
searchBar.tap()
searchBar.typeText("search text")
However, typeText fails with:
UI Testing Failure - Neither element nor any descendant has keyboard
focus. Element:
Note: Hardware->Keyboard->Connect Hardware Keyboard is toggled off. This solved the same issue for text fields but the search bar is still failing.
I found something:
let app = XCUIApplication()
app.tables.searchFields["Search"].tap()
app.searchFields["Search"].typeText("something")
It looks strange, but it works for me.
it worked for me to set
searchBar.accessibilityTraits = UIAccessibilityTraitSearchField
in the view controller and then access it in the UI test by
let searchfield = app.searchFields.element(boundBy: 0)
searchfield.typeText("foobar")
I used the following workaround successfully:
let firstCell = app.cells.elementBoundByIndex(0)
let start = firstCell.coordinateWithNormalizedOffset(CGVectorMake(0, 0))
let finish = firstCell.coordinateWithNormalizedOffset(CGVectorMake(0, 3))
start.pressForDuration(0, thenDragToCoordinate: finish)
app.searchFields.element.tap()
app.searchFields.element.typeText("search text")
This effectively scrolls the search bar into view and taps it to focus, after that typeText() can be used.
I use the following (Swift 4, Xcode 9.4.1):
app.searchFields["Placeholder"].clearAndEnterText("String to search")
app.buttons["Search"].tap()
... where "Placeholder" is the string that appears in the textfield before typing the text.
Note: clearAndEnterText is a function created in a XCUIElement extension:
func clearAndEnterText(_ text: String)
{
guard let stringValue = self.value as? String else {
XCTFail("Tried to clear and enter text into a non string value")
return
}
self.tap()
let deleteString = String(repeating: XCUIKeyboardKey.delete.rawValue, count: stringValue.count)
self.typeText(deleteString)
self.typeText(text)
}
Hope this can help you.
Just put a wait for few seconds and it will work!
let searchBar = XCUIApplication().otherElements["accessibility_label"]
searchBar.tap()
sleep(5)
searchBar.typeText("search text")