Window buttons (close etc.) not showing at first time - swift

I have a vertical split view in the main view controller. I set the left custom view's background color. Then the three window buttons got invisible at first time after launch. But there ARE buttons on the title bar because I can tap the zoom button and make the window being fullscreen. Then I pressed esc to quit fullscreen mode. And the three buttons became visible. Please tell me why. I want them to be displayed from start up.
after launching
after quitting full screen mode
Some code:
in window controller
override func windowDidLoad() {
super.windowDidLoad()
window!.titlebarAppearsTransparent = true
window!.titleVisibility = .hidden
window!.isMovableByWindowBackground = true
window!.styleMask = [NSWindow.StyleMask.fullSizeContentView, NSWindow.StyleMask.titled, NSWindow.StyleMask.closable, NSWindow.StyleMask.miniaturizable, NSWindow.StyleMask.resizable]
}
extension for changing NSView background color
extension NSView {
#IBInspectable var backgroundColor: NSColor {
get {
let cgColor = self.layer?.backgroundColor
let result = NSColor(cgColor: (cgColor)!)
return (result)!
}
set {
self.wantsLayer = true
self.layer?.backgroundColor = newValue.cgColor
}
}
}
Settings of the window controller:

Related

Status button highlight when popover is active (Swift, macOS)

I am making a status bar macOS app. On clicking on the status bar icon, I am showing an NSPopover (not an NSMenu). However, when my NSPopover is shown, my status menu icon is not highlighted. It is only highlighted for a moment when I click it. I want it to stay highlighted, much like how it behaves with the wifi status bar icon.
I know that if I use a NSMenu instead of a NSPopover, it can probably be fixed. But the requirement is such that I need to use a NSPopover.
I have tried the following approaches, but to no avail:
1.
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
if let button = statusItem.button {
button.setButtonType(.pushOnPushOff)
}
statusItem.highlightMode = true
statusItem.button?.highlight(true)
statusItem.button?.isHighlighted = true
I am not very experienced with status bar apps. So I am not really sure about which approach to take here.
The left most icon is my status bar app icon. The popover is currently active but the icon is not highlighted. I had to crop out the popover.
This can be done, but to do it reliably requires some tight coupling. In this example, I assume you have a NSViewController named PopoverController that has a togglePopover() method and that you've set this controller as the target of the status bar button.
Step 0: Context
Here's the basic setup of the class that controls the popover:
final class PopoverController: NSViewController
{
private(set) var statusItem: NSStatusItem!
private(set) var popover: NSPopover
// You can set up your StatusItem and Popover anywhere; but here's an example with -awakeFromNib()
override func awakeFromNib()
{
super.awakeFromNib()
statusItem = NSStatusBar.system.statusItem(withLength: 20.0)
statusItem.button?.sendAction(on: [.leftMouseDown, .rightMouseDown])
statusItem.button?.image = NSImage(named: "statusBar-icon")
statusItem.button?.target = self
statusItem.button?.action = #selector(togglePopover)
popover = NSPopover()
popover.behavior = .transient
popover.delegate = self
popover.contentViewController = self
popover.setValue(true, forKeyPath: "shouldHideAnchor") // Private API
}
#objc func togglePopover()
{
if popover.isShown
{
popover.performClose(nil)
}
else if !popover.isShown,
let button: NSButton = statusItem.button,
button.window?.contentView != nil, // Exception thrown if view is nil or not part of a window.
button.superview != nil
{
popover.show(relativeTo: .zero, of: button, preferredEdge: .minY)
}
}
}
Step 1: Override the Status Button
extension NSStatusBarButton
{
public override func mouseDown(with event: NSEvent)
{
if event.modifierFlags.contains(.control)
{
self.rightMouseDown(with: event)
return
}
if let controller: PopoverController = self.target as? PopoverController
{
controller.togglePopover()
self.highlight(controller.popover.isShown)
}
}
}
Step 2: Handle Popover Closing
Make sure PopoverController conforms to NSPopoverDelegate and implement this delegate method:
func popoverDidClose(_ notification: Notification)
{
statusItem.button?.highlight(false)
}
Outcome
With all of that in place, the button highlighting now works just as it does for Apple's system status bar items like Control Center, Wifi, Battery, etc.
Note: you'll also need to add a global event monitor to listen for clicks that happen outside of your popover to ensure that it closes properly when the user clicks away from it. But that's outside the scope of this question and available elsewhere on SO.

How to make a NSTextView, that is added programmatically, active?

I am making an app where a user can click anywhere on the window and a NSTextView is added at the mouse location. I have got it working with the below code but I am not able to make it active (in focus) after adding it to the view (parent view). I have to click on the NSTextView to make it active but this is not what I want. I want it to automatically become active when its added to the parent view.
Code in my ViewController to add the NSTextView to its view:
private func addText(at point: NSPoint) {
let textView = MyTextView(frame: NSRect(origin: point, size: CGSize(width: 150.0, height: 40.0)))
view.addSubview(textView)
}
MyTextView class looks like below:
class MyTextView: NSTextView {
override var shouldDrawInsertionPoint: Bool {
true
}
override var canBecomeKeyView: Bool {
true
}
override func viewWillDraw() {
isHorizontallyResizable = true
isVerticallyResizable = true
insertionPointColor = .red
drawsBackground = false
isRichText = false
allowsUndo = true
font = NSFont.systemFont(ofSize: 40.0)
}
}
Also, I want it to lose focus (become inactive) when some other elements (view) are clicked. Right now, once a NSTextView becomes active, it stays active no matter what other elements I click except when I click on an empty space to create yet another NSTextView.
I have gone through the Apple docs multiple times but I think I am missing something. Any help would be much appreciated.
Get the NSWindow instance of the NSViewController's view and call makeFirstResponder passing the text view as parameter.
To lose focus call makeFirstResponder passing nil.

Swift iOS -How to Add Subview to Window's Center?

I have an activity indicator that gets presented on an iPhone and iPad. In the iPad in split screen mode it gets presented to whichever side of the view that called it. I would instead like it to get presented in the middle/center the window's screen. If I do it this way wether on the iPhone in portrait or iPad in split screen mode it will always be in the center of the screen.
How do I do this?
MyView: UIViewController{
let actInd = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
#IBAction fileprivate func buttonPressed(_ sender: UIButton) {
guard let window = UIApplication.shared.keyWindow else { return }
//how to add actInd as subview to the window' screen?
actInd.startAnimating()
}
}
It's pretty simple. Turn off the auto-resizing mask. Add the add actInd to window, then set the center anchors.
actInd.translatesAutoresizingMaskIntoConstraints = false
window.addSubview(actInd)
actInd.centerXAnchor.constraint(equalTo: window.centerXAnchor).isActive = true
actInd.centerYAnchor.constraint(equalTo: window.centerYAnchor).isActive = true
Window is subclass of UIView. Just add it as it's subview like you're adding a view to another view. But remember that window is shared throughout your app, so adding it every-time will consume memory, remove it after your job is done.
If you want to center it in the window, you can use autoResizingMask or add constraints to it.

How to add buttons when NSVisualEffectView is used

I have created a window using NSVisualEffectView to get blur and rounded corners. Like here
The problem is I don't see my button in the window when I have NSVisualEffectView code. If I remove the code, the button is displayed. What is going wrong?
NSVisualEffectView code in AppDelegate.swift:
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
guard let window = NSApplication.shared().windows.first else { return }
let effect = NSVisualEffectView(frame: NSRect(x: 0, y: 0, width: 0, height: 0))
effect.blendingMode = .behindWindow
effect.state = .active
effect.material = .dark
effect.wantsLayer = true
effect.layer?.cornerRadius = 15.0
effect.layer?.masksToBounds = true
window.isOpaque = false
window.backgroundColor = .clear
window.contentView = effect
window.titlebarAppearsTransparent = true
window.titleVisibility = .hidden
}
I have added some buttons in storyboard. When I run the project I don't see any buttons.
When I remove everything from applicationDidFinishLaunching(_ aNotification: Notification) i.e., NSVisualEffectView code, I can see the buttons.
Can anyone tell me what is happening?
I think I should have corrected you in your previous question only but I didn't.
You are using Storyboard so why are you creating NSVisualViewEffect variable in your code?
Search for nsvisualeffectview in the right panel(Utilities panel) where you search for buttons etc. (object library).
Drag it and resize it according to your main view controller.
To add the blur effect and mode, go to "Attribites Inspector" in the "Utilities panel"
and set window.backgroundColor = .clear and window.isOpaque = false
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
guard let window = NSApplication.shared.windows.first else { return }
window.isOpaque = false
window.backgroundColor = .clear
}
Now you can add your buttons, text fields and run the project. You can see all your added elements.
I hope it helps!
window is above the view you are adding buttons to, so the buttons are below the blurred-out window, and are therefore impossible to see. Why not add the visualEffectView to the same view as the buttons? You'd need to insert it below the buttons to make the buttons visible.

How can I use NSVisualEffectView to blend window with background

There seem to be a bunch of questions on this for old versions of Swift/Xcode, but for some reason it hasn't been working with the latest update. I created a NSVisualEffectView, blurryView, and added the subview to my main view:
class ViewController: NSViewController {
#IBOutlet weak var blurryView: NSVisualEffectView!
override func viewDidLoad() {
super.viewDidLoad()
//background styling
blurryView.wantsLayer = true
blurryView.blendingMode = NSVisualEffectBlendingMode.behindWindow
blurryView.material = NSVisualEffectMaterial.dark
blurryView.state = NSVisualEffectState.active
self.view.addSubview(blurryView, positioned: NSWindowOrderingMode.above, relativeTo: nil)
// Do any additional setup after loading the view.
}
...
}
But when I run it, there is no effect on the window. (when I set it to within window, and layer it on top of my other view, the blur works correctly, but I only want the window to blur.) I also tried doing the same thing in my App Delegate class, but I can't connect my window as an outlet, and therefore can't add the blurry view to the window. Here's what the code would look like:
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
blurryView.wantsLayer = true
blurryView.blendingMode = NSVisualEffectBlendingMode.withinWindow
blurryView.material = NSVisualEffectMaterial.dark
blurryView.state = NSVisualEffectState.active
self.window.contentView?.addSubview(blurryView)
}
...
}
To get an idea if what I'm looking for: NSVisualEffectView Vibrancy
It works quite easy:
In Interface Builder drag a NSVisualEffectView directly as a subview of the main view of your scene.
In the Properties Inspector set Blending Mode to Behind Window
Add the rest of the views you need as subviews of the NSVisualEffectView
That's it, you're done
Here's an example:
Panel 1 View Controller is my blurred view, Background View is the first (non-blurred) view in my "real"view hierarchy.
Swift 5:
Simply add this to your viewWillAppear and it should work:
override func viewWillAppear() {
super.viewWillAppear()
//Adds transparency to the app
view.window?.isOpaque = false
view.window?.alphaValue = 0.98 //you can remove this line but it adds a nice effect to it
let blurView = NSVisualEffectView(frame: view.bounds)
blurView.blendingMode = .behindWindow
blurView.material = .fullScreenUI
blurView.state = .active
view.window?.contentView?.addSubview(blurView)
}