How can I disable the close button? - swift

I need some help with figuring out how to disable/hide the close, minimize, and resize buttons in OS X Cocoa and Swift 2. Here's the code I tried. I know it's for the Title Bar, but I thought I'd try it anyway:
self.window.titleVisibility = NSWindowTitleVisibility.Hidden;
Does any one know how to do that? I'm using Swift 2, OS X Cocoa, and Xcode 7.2. Thanks!

Also try;
self.window!.standardWindowButton(NSWindow.ButtonType.closeButton)!.hidden = true
self.window!.standardWindowButton(NSWindow.ButtonType.miniaturizeButton)!.hidden = true
etc.

In Xcode 9.1 you can use following in the ViewController,
override func viewWillAppear() {
self.view.window?.titleVisibility = .hidden
self.view.window?.titlebarAppearsTransparent = true
self.view.window?.styleMask.insert(.fullSizeContentView)
self.view.window?.styleMask.remove(.closable)
self.view.window?.styleMask.remove(.fullScreen)
self.view.window?.styleMask.remove(.miniaturizable)
self.view.window?.styleMask.remove(.resizable)
//self.view.window?.isMovable = false
}
override func viewWillAppear() {
self.view.window?.titleVisibility = .hidden
self.view.window?.titlebarAppearsTransparent = true
self.view.window?.styleMask.insert(.fullSizeContentView)
//self.view.window?.styleMask.remove(.closable)
self.view.window?.styleMask.remove(.fullScreen)
self.view.window?.styleMask.remove(.miniaturizable)
self.view.window?.styleMask.remove(.resizable)
//self.view.window?.isMovable = false
}

See the NSWindow.styleMask property and the Window Style Masks.
Clearing the NSClosableWindowMask, NSMiniaturizableWindowMask, and NSResizableWindowMask flags will remove all of the buttons from a window's title bar.
window.styleMask &= ~(NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)

I find that the following effectively disables without completely hiding the close button:
self.view.window?.standardWindowButton(NSWindow.ButtonType.closeButton)?.isEnabled = false

a side note: do NOT disable "close" red button: apple will reject app saying:
"The user interface of your app is not consistent with the macOS Human Interface Guidelines. Specifically:
Specifically, the red light was disabled."
:(

Expanding on the accepted answer above by #JohnElemans, this is what worked for me when presenting modally using a storyboard segue on macOS 10.15:
// On NSViewController.viewDidAppear()
if let window = self.view.window {
window.standardWindowButton(NSWindow.ButtonType.closeButton)?.isHidden = true
window.standardWindowButton(NSWindow.ButtonType.miniaturizeButton)?.isHidden = true
window.standardWindowButton(NSWindow.ButtonType.zoomButton)?.isHidden = true
}
I had to add the third line (NSWindow.ButtonType.zoomButton) to get rid of the green ('fullscreen') button.

If you are using Storyboards and you can change your window styleMask property inside an IBAction or viewDidLoad as follow:
NSApplication.sharedApplication().windows.first?.styleMask = NSTitledWindowMask // | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask
If you would like to enable them again just uncomment the rest of the style mask:
NSApplication.sharedApplication().windows.first?.styleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask

If you are using Flutter, you can do like this:
import Cocoa
import FlutterMacOS
import window_manager
class MainFlutterWindow: NSWindow {
override func awakeFromNib() {
let flutterViewController = FlutterViewController.init()
let windowFrame = self.frame
self.contentViewController = flutterViewController
self.setFrame(windowFrame, display: true)
self.standardWindowButton(.closeButton)?.isHidden = true
self.standardWindowButton(.miniaturizeButton)?.isHidden = true
self.standardWindowButton(.zoomButton)?.isHidden = true
RegisterGeneratedPlugins(registry: flutterViewController)
super.awakeFromNib()
}
override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) {
super.order(place, relativeTo: otherWin)
hiddenWindowAtLaunch()
}
}

Related

Making Cocoa NSWindow translucent hides storyboard elements

In my app delegate I make my window translucent with the following code:
func applicationDidFinishLaunching(_ aNotification: Notification) {
let visualEffect = NSVisualEffectView()
visualEffect.translatesAutoresizingMaskIntoConstraints = false
visualEffect.material = .dark
visualEffect.state = .active
visualEffect.wantsLayer = true
visualEffect.layer?.cornerRadius = 16.0
NSApplication.shared.mainWindow?.titleVisibility = .hidden
NSApplication.shared.mainWindow?.styleMask.remove(.titled)
NSApplication.shared.mainWindow?.backgroundColor = .clear
NSApplication.shared.mainWindow?.isMovableByWindowBackground = true
NSApplication.shared.mainWindow?.contentView?.addSubview(visualEffect)
guard let constraints = NSApplication.shared.mainWindow?.contentView else {
return
}
visualEffect.leadingAnchor.constraint(equalTo: constraints.leadingAnchor).isActive = true
visualEffect.trailingAnchor.constraint(equalTo: constraints.trailingAnchor).isActive = true
visualEffect.topAnchor.constraint(equalTo: constraints.topAnchor).isActive = true
visualEffect.bottomAnchor.constraint(equalTo: constraints.bottomAnchor).isActive = true
}
The problem with this is that every element in the storyboard is no longer visible. How can I fix this? Thanks
You need to add all your UI to the NSVisualEffectView as subviews or move the NSVisualEffectView to the back of the view hierarchy:
NSApplication.shared.mainWindow?.contentView?.addSubview(visualEffect, positioned: .below, relativeTo: nil)
Update:
I created a new macOS project in Xcode and added a label on the view. Then I pasted your code and the only thing I changed was the line of code above. It works.

Accessing NSWindow-like properties in Catalyst macOS app

I am thinking about porting my macOS app to Catalyst.
My app shows a transparent window (no title bar, clear background) on top of all other apps windows (dock included).
To do that, in the non-catalyst code I use:
window.isOpaque = false
window.hasShadow = false
window.backgroundColor = .clear
window.styleMask = .borderless
window.isMovableByWindowBackground = true
window.level = .statusBar
Using UIKit, I was only able to remove the toolbar so far:
window.titleBar.titleVisibility
...But no clue about the other settings.
I plan to make the app available on the App Store in the future, but if the only way to do so is and hack with a private API, that's fine.
Any ideas?
Thanks in advance
There is no official API for doing that, but you can easily access the NSWindow instance and modify it directly. You can do that manually or using some library like Dynamic (Full disclosure: I'm the author):
let window = Dynamic.NSApplication.sharedApplication.delegate.hostWindowForUIWindow(uiWindow)
window.isOpaque = false
window.hasShadow = false
window.backgroundColor = Dynamic.NSColor.clearColor
window.styleMask = 0 /*borderless*/
window.isMovableByWindowBackground = true
window.level = 25 /*statusBar*/
I have some success removing the close button on Catalyst by calling a function from the viewDidAppear(). I called it AppDelegate().disableTitleBarButtons(). Has to be from view did appear.
AppDelegate().disableTitleBarButtons() is as follows
func disableTitleBarButtons() {
func bitSet(_ bits: [Int]) -> UInt {
return bits.reduce(0) { $0 | (1 << $1) }
}
func property(_ property: String, object: NSObject, set: [Int], clear: [Int]) {
if let value = object.value(forKey: property) as? UInt {
object.setValue((value & ~bitSet(clear)) | bitSet(set), forKey: property)
}
}
// disable full-screen button
if let NSApplication = NSClassFromString("NSApplication") as? NSObject.Type,
let sharedApplication = NSApplication.value(forKeyPath: "sharedApplication") as? NSObject,
let windows = sharedApplication.value(forKeyPath: "windows") as? [NSObject]
{
for window in windows {
let resizable = 4
property("styleMask", object: window, set: [], clear: [resizable])
let fullScreenPrimary = 7
let fullScreenAuxiliary = 8
let fullScreenNone = 9
property("collectionBehavior", object: window, set: [fullScreenNone], clear: [fullScreenPrimary, fullScreenAuxiliary])
}
}
}
Where is says let resizable = 4,
Change to 3 for no Maximise,
Change to 2 for No minimise,
Change to 1 of No Close button.
Play with the other numbers or stylemask settings also. Good luck

How to create block screen with circle loader

I am doing an app that does background job that can take some time
I want to show a loader in that time
I want a black screen with a simple loader in the front of it
and show it \ hide it,
when I do actions in the background
I want to do a simple half black square with loader circle
that also blocks presses to the screen
Like in this picture:
How can I achieve that and that ?
First create one UIView which you will put in front of your LogIn view. Then add UIActivityIndicatorView to the created UIView.
let loadingIndicatorView = UIView()
let activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
Now the loadingIndicatorView should have same frame size as your LogIN view. For color you can set your own color with alpha as you want to show LogIn content too. Initially keep it hidden and whenever you want to show it unhide it.
loadingIndicatorView.frame = view.frame
loadingIndicatorView.backgroundColor = .gray
loadingIndicatorView.isHidden = true
Now setup activityIndicatorView, it should be shown at centre,
activityIndicatorView.center = CGPoint(
x: UIScreen.main.bounds.size.width / 2,
y: UIScreen.main.bounds.size.height / 2
)
You can set some color to the indicator,
activityIndicatorView.color = .white
activityIndicatorView.hidesWhenStopped = true
Now add this activityIndicatorView to loadingIndicatorView and loadingIndicatorView to LogIn View.
loadingIndicatorView.addSubview(activityIndicatorView)
view.addSubview(loadingIndicatorView)
Lastly for showing do,
loadingIndicator.startAnimating()
loadingIndicatorView.isHidden = false
And for hiding,
loadingIndicator.stopAnimating()
loadingIndicatorView.isHidden = true
Updated Answer
Since the OP wanted an example code. Hence the updated answer. Hope everyone gets to learn something or the other out of it.
To start with, I created a subclass of UIView and named it PSOverlaySpinner and it looks something like below:
import UIKit
class PSOverlaySpinner: UIView {
//MARK: - Variables
private var isSpinning: Bool = false
private lazy var spinner : UIActivityIndicatorView = {
var spinner = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.white)
spinner.translatesAutoresizingMaskIntoConstraints = false
spinner.hidesWhenStopped = true
return spinner
}()
// MARK: - View Lifecycle Functions
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init() {
super.init(frame: CGRect.zero)
self.translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = UIColor.init(white: 0.0, alpha: 0.8)
self.isSpinning = false
self.isHidden = true
createSubviews()
}
deinit {
self.removeFromSuperview()
}
func createSubviews() -> Void {
self.addSubview(spinner)
setupAutoLayout()
}
// MARK: - Private Methods
private func setupAutoLayout() {
if #available(iOS 11.0, *) {
spinner.safeAreaLayoutGuide.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor).isActive = true
spinner.safeAreaLayoutGuide.centerYAnchor.constraint(equalTo: safeAreaLayoutGuide.centerYAnchor).isActive = true
} else {
// Fallback on earlier versions
spinner.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
spinner.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
}
// MARK: - Public Methods
public func show() -> Void {
DispatchQueue.main.async {
if !self.spinner.isAnimating {
self.spinner.startAnimating()
}
self.isHidden = false
}
isSpinning = true
}
public func hide() -> Void {
DispatchQueue.main.async {
if self.spinner.isAnimating {
self.spinner.stopAnimating()
}
self.isHidden = true
}
isSpinning = false
}
}
Now move onto the ViewController that you want to add this overlay view to. Since I create my views programmatically, I will show how to do it the same way, but you can easily do it via storyboard or xibs.
Step 1 : Initialize
public lazy var spinnerView : PSOverlaySpinner = {
let loadingView : PSOverlaySpinner = PSOverlaySpinner()
return loadingView
}()
Step 2 : Add as a subview
self.view.addSubview(spinnerView)
Step 3 : Set constraints
spinnerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
spinnerView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
spinnerView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
spinnerView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
Step 4 : To show PSOverlaySpinner
spinnerView.show()
Step 5 : To hide PSOverlaySpinner
spinnerView.hide()
That is it!!
If you want you can go ahead and modify the PSOverlaySpinner as per your needs. For example, you might want to add a UILabel below the spinner indicating him of the type of action taking place and so on.
Before
After
Old Answer
If you wish to do it manually then create a UIView with the its frame matching self.view.bounds, with 0.5-0.7 alpha and black background color. Add UIActivityIndicator as its subview constrained to its center. For a spinner specific to the image you will have to use the open sourced spinners made available. A couple of them can be found here. Once done add this view as the topmost subview in self.view.
You need to import this library SVProgressHUD and then set few properties like as follows:
SVProgressHUD.setDefaultStyle(SVProgressHUDStyle.dark)
SVProgressHUD.setBackgroundColor(.clear)
SVProgressHUD.setForegroundColor(.white)
SVProgressHUD.setDefaultMaskType(.black)
SVProgressHUD.show()
//SVProgressHUD.show(withStatus: "Loading something, Loading something,Loading something ...")
This will produce same UI output as needed by you in OP. You can find a running sample at my repository (TestPreLoader)

how can enable the zoom in uiwebview using swift without using the scalesPageToFit=true property because it cause Sequese webview

I want to enable the zoom in zoom out functionality on UIWebView using swift. I've applied the following and all help available on stack over flow and others. I dont want to use the scalesPageToFit functionality provided by apple when I use scalesPageToFit = true the zoom in zoom out functionality start working other wise it will disabled and I dont want to set my webview property as scalesPageToFit= true because when I use scalesPageToFit= true my web view Sequese.
when i set scalesPageToFit= true [1]: https://i.stack.imgur.com/4fIgY.jpg
when the scalesPageToFit=false but it will not zoom in zoom out the web view when i set this property as false [2]: https://i.stack.imgur.com/RUU9B.jpg
use the following code
override func viewDidLoad() {
self.web_Description.delegate = self
self.web_Description.scrollView.delegate = self
self.web_Description.scrollView.minimumZoomScale = 1.0
self.web_Description.scrollView.maximumZoomScale = 5.0
}
func webViewDidFinishLoad(_ webView: UIWebView) {
self.web_Description.scrollView.minimumZoomScale = 1.0
self.web_Description.scrollView.maximumZoomScale = 5.0
self.web_Descriptio.stringByEvaluatingJavaScript(from: "document.querySelector('meta[name=viewport]').setAttribute('content', 'user-scalable = 1;', false); ")
}
func scrollViewDidEndZooming(_ scrollView: UIScrollView,with view: UIView?,atScale scale: CGFloat) {
self.web_Descriptio.stringByEvaluatingJavaScript(from: "document.querySelector('meta[name=viewport]').setAttribute('content', 'user-scalable = 1;', false); ")
}

How can I make an editable text field in a swift shell application

I'm trying to make a editable text region in a NSWindow. So far I can make
a window and add a text field - but when I select it and type characters the characters are echoed in the shell and NOT the text area.
NOTE: this is NOT an Xcode project - I am trying to do this in a single
file in the shell - my goal is to do this in code only
To replicate the error put the following code into a file (experiment.swift) and give the shell command
> swift experiment.swift
Here's the code
import Cocoa
class MyAppDelegate: NSObject, NSApplicationDelegate {
let window = NSWindow()
let ed = NSTextField(frame: NSMakeRect(20, 10, 180, 160))
func applicationDidFinishLaunching(aNotification: NSNotification) {
window.setContentSize(NSSize(width:600, height:200))
window.styleMask = NSTitledWindowMask | NSClosableWindowMask |
NSMiniaturizableWindowMask |
NSResizableWindowMask
window.opaque = false
window.center();
window.title = "My window"
ed.font = NSFont(name:"Helvetica Bold", size:20)
ed.stringValue = "edit me"
ed.editable = true
ed.selectable = true
window.contentView!.addSubview(ed)
window.makeKeyAndOrderFront(window)
window.level = 1
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
}
let app = NSApplication.sharedApplication()
let obj = MyAppDelegate()
app.delegate = obj
app.run()
Before app.run(), add
app.setActivationPolicy(.Regular)
According to the docs, the default activationPolicy is Prohibited:
Prohibited
The application does not appear in the Dock and may not create windows or be activated. [...] This is also the default for unbundled executables that do not have Info.plists.