NSAlert for the system – The system comes with a hint view

In the ‘viewController.swift’ file, create an instance of NSAlert to see ~

import Cocoa class ViewController: NSViewController { @objc func clickItem(btn: NSButton) {print("clickItem:\(btn.title)")} @objc func showNotice() {let alert = NSAlert(), AddButton (withTitle: "Ok ") alert. AddButton (withTitle:" Ok ") Item3 = alert. AddButton (withTitle: "More") Item3. action = #selector(clickItem) Let item4 = alert. addButton(withTitle: "item4 ") // get the corresponding button //item4.target = self; Item4. Action = #selector(clickItem)// Overriding method -- let item5 = alert. AddButton (withTitle: Item5. target = self; Item5. action = #selector(clickItem)// Overriding method -- override 'system callback when an option is clicked' method alert.messageText = "warning of blabla operation "// title alert.informativeText = "Operation BLabLA will result in XX, Whether to continue? "detailed description / / / / set the alert NSAlert display Style. AlertStyle = NSAlert. Style. The critical / / warning, informational, critical alert .beginSheetModal(for: self.view .window!) {(returnCode: NSApplication. ModalResponse) in / / click option system callback switch returnCode {case. AlertFirstButtonReturn: print("NSApplication.ModalResponse.alertFirstButtonReturn") case .alertSecondButtonReturn: print("NSApplication.ModalResponse.alertSecondButtonReturn") case .alertThirdButtonReturn: Print (" NSApplication. ModalResponse. AlertThirdButtonReturn ") / / the following conditions are not reach the case. Stop: print("NSApplication.ModalResponse.stop") case .abort: print("NSApplication.ModalResponse.abort") case .`continue`: print("NSApplication.ModalResponse.`continue`") case .OK: print("NSApplication.ModalResponse.OK") case .cancel: print("NSApplication.ModalResponse.cancel") case .continue: print("NSApplication.ModalResponse.continue") default: Print ("Other Situation -- default") break}}} func addOneBtn() {let BTN = NSButton(frame: NSMakeRect(100, 100, 100, 100)) btn.target = self; btn.action = #selector(showNotice) self.view .addSubview(btn) } override func viewDidLoad() { super.viewDidLoad() // Do Override var representedObject: any additional setup after loading the view.self.addonebtn ()} Override var representedObject: any? { didSet { // Update the view, if already loaded. } } }Copy the code

Effect:

1. Click ‘Button’ to pop up NSAlert view! After the NSAlert view is displayed (except the NSAlert view), other views cannot be operated ~

2. The first 3 items (button) – click on the item 1 (a) confirmation/item 2 (‘ cancelled ‘) / 3 (the ‘More’) after NSAlert view will disappear and print the NSApplication. ModalResponse. AlertFirstButtonReturn/NSApplica Tion. ModalResponse. AlertSecondButtonReturn/NSApplication. ModalResponse. AlertThirdButtonReturn!

3. Starting with item 4 (button), clicking item 4 (‘ item 4 ‘)/ item 5 (‘ item 5 ‘) will make the NSAlert view disappear and print Other Situation — default!

Conclusion: The first three items added (buttons) respond to the ‘system callback when an option is clicked’ method — and there are items in the enumeration to confirm! Starting with item 4 (button), it responds to the ‘system callback when option is clicked’ method — but there is no corresponding item in the enumeration to confirm!

Open func addButton(withTitle title: String) -> NSButton Override its button override method – the NSAlert view will not disappear after clicking on the item (instead of going to the default ‘system callback when clicking on the option’ method and executing @objc func clickItem(BTN: NSButton) method)!

Public commented @objc func showNotice() method:

@objc func showNotice() {let alert = NSAlert() AddButton (withTitle: "Ok ") alert. AddButton (withTitle:" Ok ") Item3 = alert. AddButton (withTitle: "More"); Item3. action = #selector(clickItem) Let item4 = alert. addButton(withTitle: "item4 ") // get the corresponding button item4.target = self; Item4. Action = #selector(clickItem)// Overriding method -- let item5 = alert. AddButton (withTitle: Item5. target = self; Item5. action = #selector(clickItem)// Overriding method -- override 'system callback when an option is clicked' method alert.messageText = "warning of blabla operation "// title alert.informativeText = "Operation BLabLA will result in XX, Whether to continue? "detailed description / / / / set the alert NSAlert display Style. AlertStyle = NSAlert. Style. The critical / / warning, informational, critical alert .beginSheetModal(for: self.view .window!) {(returnCode: NSApplication. ModalResponse) in / / click option system callback switch returnCode {case. AlertFirstButtonReturn: print("NSApplication.ModalResponse.alertFirstButtonReturn") case .alertSecondButtonReturn: print("NSApplication.ModalResponse.alertSecondButtonReturn") case .alertThirdButtonReturn: Print (" NSApplication. ModalResponse. AlertThirdButtonReturn ") / / the following conditions are not reach the case. Stop: print("NSApplication.ModalResponse.stop") case .abort: print("NSApplication.ModalResponse.abort") case .`continue`: print("NSApplication.ModalResponse.`continue`") case .OK: print("NSApplication.ModalResponse.OK") case .cancel: print("NSApplication.ModalResponse.cancel") case .continue: print("NSApplication.ModalResponse.continue") default: Print ("Other Situation -- default") break}}}Copy the code

Click callback method of the corresponding button:

@objc func clickItem(btn: NSButton) {
    print("clickItem:\(btn.title)")
    
}
Copy the code

Effect: 1. Click ‘Button’ to pop up NSAlert view! After the NSAlert view is displayed (except the NSAlert view), other views cannot be operated ~

2. The first two items (button) – click on the item 1 (‘ sure ‘) after 2 (‘ cancelled ‘) / / NSAlert view will disappear and print the NSApplication. ModalResponse. AlertFirstButtonReturn/NSApplication. ModalR Esponse. AlertSecondButtonReturn/NSApplication. ModalResponse alertThirdButtonReturn!

3. Start with item 3 (button) – add action response to the corresponding button, ClickItem 3 (‘More’)/ item 4 (‘ 4 ‘)/ item 5 (‘ 5 ‘) and the NSAlert view will not disappear (will not go to the default ‘system callback when clicking option’ method) and execute @objc func clickItem(BTN: NSButton) method after printing clickItem: More/clickItem: item 4 / clickItem: item 5!

NSAlert * * alertStyle attributes corresponding effect * * — NSAlert. Style. Warning, NSAlert. Style. The informational, NSAlert. Style. Critical

NSAlert.Style.warning

NSAlert.Style.informational

NSAlert.Style.critical

You can see from the above code that the NSAlert instance object is created based on the NSWindow object! So the code looks like this: add extension **(extension)~ for the **NSWindow class

The extension NSWindow {/ / create macOS warning/hint view - returns NSApplication. ModalResponse public func showAlertNotice (firstItemStr: String, secondItemStr: String, title: String, information: String, alertStyle: NSAlert.Style, completionHalder handler: @ escaping ((NSApplication ModalResponse) - > Void)) {/ / @ escaping - remove "scaping closure captures non - escaping parameter 'Handler' "alert let alert = NSAlert() alert.addButton (withTitle: firstItemStr) alert.addButton (withTitle: firstItemStr) SecondItemStr) alert.messagetext = title// title alert.informativeText = information// detailed description // Set NSAlert display style alert.alertstyle  = alertStyle//warning\informational\critical alert .beginSheetModal(for: self) { (returnCode: NSApplication. ModalResponse) in / / click option system callback handler (returnCode)}} / / create macOS warning/hint view - return a bool value public func showAlertNoticeWithBool(firstItemStr: String, secondItemStr: String, title: String, information: String, alertStyle: NSAlert.Style, completionHalder handler: @escaping ((Bool) -> Void)) {//@escaping -- Remove "scaping closure captures non-escaping parameter 'handler'" warning let alert = NSAlert() alert .addButton(withTitle: firstItemStr) alert .addButton(withTitle: SecondItemStr) alert.messagetext = title// title alert.informativeText = information// detailed description // Set NSAlert display style alert.alertstyle  = alertStyle//warning\informational\critical alert .beginSheetModal(for: self) { (returnCode: NSApplication. ModalResponse) in / / click option system callback if returnCode = =. AlertFirstButtonReturn {/ / click "sure" handler (true)} else if ReturnCode ==.alertSecondButtonReturn {// Click "cancel" handler(false)}}}}Copy the code

Method call: in the ‘viewController.swift’ file

/ / return NSApplication. ModalResponse self. View. The window! .showalertnotice (firstItemStr: "OK", secondItemStr: "cancel ", title:" info ", alertStyle: .critical) { (returnCode : NSApplication. ModalResponse) in the if returnCode = =. AlertFirstButtonReturn {/ / click "sure" print (" returnCode = = .alertFirstButtonReturn") //" Confirm "the corresponding action} else if returnCode ==. AlertSecondButtonReturn {// Click" cancel "print("returnCode == .alertSecondButtonReturn") //" cancel "corresponding operation}} // Return bool sell.view.window! ShowAlertNoticeWithBool (firstItemStr: "OK", secondItemStr: "cancel ", title:" title ", Information: "Notification content and message ", alertStyle: .critical) { (isOK: Bool) in if isOK == true {// click "ok" print("isOK == true")} else {// Click "cancel" print("isOK == false") //" cancel "corresponding operation }}Copy the code


Custom WindowController

Create a new custom GYHNoticeAlertWindowController classes (need to check the ‘of the create XIB file for user interface’)

Select ‘Also create XIB file for user interface’

Added picture material:

In ‘GYHNoticeAlertWindowController. Swift’ file, to control the UI layout:

import Cocoa

class GYHNoticeAlertWindowController: NSWindowController {
    var sureBtn: NSButton!
    var cancelBtn: NSButton!
    
    var _title: String?     //标题
    var _message: String?   //提示信息
    var _okStr: String?     //“确定”文字
    var _cancelStr: String? //“取消”文字
    var _backColor: NSColor?//背景色
    convenience init(title: String?, message: String?, backColor: NSColor?, okStr: String?, cancelStr: String?) {
        self.init(windowNibName: "GYHNoticeAlertWindowController")
        
        _title = title != nil ? title : "警告"
        _message = message != nil ? message : "提示信息的内容:blabla~"
        _okStr = okStr != nil ? okStr : "确定"
        _cancelStr = cancelStr != nil ? cancelStr : "取消"
        _backColor = backColor != nil ? backColor : NSColor(red: 190/255.0, green: 150/255.0, blue: 125/255.0, alpha: 1.0)
    }
    override func windowDidLoad() {
        super.windowDidLoad()
        
        // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
        //对窗口(self.window)进行配置
        self.window?.isRestorable = false//窗口不可拉伸
        self.window?.titlebarAppearsTransparent = true
        self.window?.title = _title!//标题
        self.window?.titleVisibility = NSWindow.TitleVisibility.hidden//隐藏窗口标题
        self.window?.styleMask = NSWindow.StyleMask.fullSizeContentView//窗口风格:顶部视图+窗口(融合)
        self.window?.isMovableByWindowBackground = true//点击背景,可移动
        //设置背景色
        self.window?.contentView?.wantsLayer = true
        self.window?.contentView?.layer?.backgroundColor = _backColor?.cgColor
        //self.window?.contentView?.layer?.cornerRadius = 5.0//无效:切圆角—边框未隐藏
        
        self .setUpAllViews()
        self .setUpTitleBarV()
        
    }
    func setUpAllViews() {  //视图布局——"确定"按钮、"取消"按钮、提示信息Label
        var window_W = self.window?.frame.size.width;
        window_W = window_W != nil ? window_W : 0.0;
        
        let margin_Of_TwoBtns = (200.0/3600.0)*window_W!;
        //"确定"按钮
        let sureBtn_W = (1400.0/3600.0)*window_W!;   let sureBtn_X = window_W!/2.0 - margin_Of_TwoBtns/2.0 - sureBtn_W;
        let sureBtn_H = (360.0/3600.0)*window_W!;    let sureBtn_Y = (150.0/3600.0)*window_W!;
        sureBtn = NSButton(frame: NSMakeRect(sureBtn_X, sureBtn_Y, sureBtn_W, sureBtn_H))
        self.window?.contentView?.addSubview(sureBtn)
        sureBtn.title = _okStr!
        sureBtn.alignment = NSTextAlignment.center
        sureBtn.bezelStyle = .circular
        sureBtn.setButtonType(NSButton.ButtonType.switch)//按钮类型(可多选)
        sureBtn.imagePosition = .imageOverlaps//图片放在最下面
        sureBtn.imageScaling = .scaleAxesIndependently//图片自动调整尺寸
        sureBtn.image = NSImage(named: "Item_Button_nor")//图片——普通状态
        sureBtn.alternateImage = NSImage(named: "Item_Button_sel")//图片——高亮状态
        //"取消"按钮
        let cancelBtn_W = (1400.0/3600.0)*window_W!;   let cancelBtn_X = window_W!/2.0 + margin_Of_TwoBtns/2.0;
        let cancelBtn_H = (360.0/3600.0)*window_W!;    let cancelBtn_Y = (150.0/3600.0)*window_W!;
        cancelBtn = NSButton(frame: NSMakeRect(cancelBtn_X, cancelBtn_Y, cancelBtn_W, cancelBtn_H))
        self.window?.contentView?.addSubview(cancelBtn)
        cancelBtn.title = _cancelStr!
        cancelBtn.alignment = NSTextAlignment.center
        cancelBtn.bezelStyle = .circular
        cancelBtn.setButtonType(NSButton.ButtonType.switch)//按钮类型(可多选)
        cancelBtn.imagePosition = .imageOverlaps//图片放在最下面
        cancelBtn.imageScaling = .scaleAxesIndependently//图片自动调整尺寸
        cancelBtn.image = NSImage(named: "Item_Button_nor")//图片——普通状态
        cancelBtn.alternateImage = NSImage(named: "Item_Button_sel")//图片——高亮状态
        
        //提示信息Label
        let tipLabel_X = 0.0;       let tipLabel_Y = self.sureBtn.frame.maxY + (110.0/2800.0)*window_W!
        let tipLabel_W = window_W!;  let tipLabel_H = (300.0/2800.0)*window_W!
        let tipContentLabel = NSTextField(frame: NSMakeRect(CGFloat(tipLabel_X), tipLabel_Y, tipLabel_W, tipLabel_H))
        self.window?.contentView?.addSubview(tipContentLabel)
        tipContentLabel.isEditable = false//不可编辑
        tipContentLabel.isBordered = false//不显示边框
        tipContentLabel.backgroundColor = .clear
        tipContentLabel.alignment = .center
        tipContentLabel.font = NSFont .boldSystemFont(ofSize: 15.0)
        tipContentLabel.textColor = .lightGray
        tipContentLabel.stringValue = _message!
    }
    func setUpTitleBarV() { //设置窗口的标题栏
        let titleBar_Height: CGFloat = 27.0
        let titleBarImgV = NSImageView()
        self.window?.contentView?.addSubview(titleBarImgV)
        //添加约束
        titleBarImgV.translatesAutoresizingMaskIntoConstraints = false;
        self.window?.contentView?.addConstraint(NSLayoutConstraint(item: titleBarImgV, attribute: NSLayoutConstraint.Attribute.top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.window?.contentView, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1.0, constant: 0.0))
        self.window?.contentView?.addConstraint(NSLayoutConstraint(item: titleBarImgV, attribute: NSLayoutConstraint.Attribute.bottom, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.window?.contentView, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1.0, constant: titleBar_Height))
        self.window?.contentView?.addConstraint(NSLayoutConstraint(item: titleBarImgV, attribute: NSLayoutConstraint.Attribute.left, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.window?.contentView, attribute: NSLayoutConstraint.Attribute.left, multiplier: 1.0, constant: 0.0))
        self.window?.contentView?.addConstraint(NSLayoutConstraint(item: titleBarImgV, attribute: NSLayoutConstraint.Attribute.right, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.window?.contentView, attribute: NSLayoutConstraint.Attribute.right, multiplier: 1.0, constant: 0.0))
        titleBarImgV.image = NSImage(named: "titleBar_Img")
        titleBarImgV.imageScaling = NSImageScaling.scaleAxesIndependently//图片自动调整尺寸
        
        let nameLB_leftMargin: CGFloat = 3.0
        let nameLB_Height: CGFloat = 20.0
        let nameLabel = NSTextField()
        self.window?.contentView?.addSubview(nameLabel)
        //添加约束-简写
        nameLabel.translatesAutoresizingMaskIntoConstraints = false
        self.window?.contentView?.addConstraint(NSLayoutConstraint(item: nameLabel, attribute: .top, relatedBy: .equal, toItem: self.window?.contentView, attribute: .top, multiplier: 1.0, constant: (titleBar_Height - nameLB_Height)))
        self.window?.contentView?.addConstraint(NSLayoutConstraint(item: nameLabel, attribute: .bottom, relatedBy: .equal, toItem: self.window?.contentView, attribute: .top, multiplier: 1.0, constant: titleBar_Height))
        self.window?.contentView?.addConstraint(NSLayoutConstraint(item: nameLabel, attribute: .left, relatedBy: .equal, toItem: self.window?.contentView, attribute: .left, multiplier: 1.0, constant: nameLB_leftMargin))
        self.window?.contentView?.addConstraint(NSLayoutConstraint(item: nameLabel, attribute: .right, relatedBy: .equal, toItem: self.window?.contentView, attribute: .right, multiplier: 1.0, constant: 0.0))
        nameLabel.isBordered = false
        nameLabel.isEditable = false
        nameLabel.backgroundColor = NSColor.clear
        nameLabel.textColor = NSColor.gridColor
        nameLabel.font = NSFont .boldSystemFont(ofSize: 13.0)
        nameLabel.stringValue = _title!//标题
    }
    
}
Copy the code

In the ‘viewController.swift’ file, use:

import Cocoa class ViewController: NSViewController, NSWindowDelegate { var noticeWC: GYHNoticeAlertWindowController? @objc func clickWindowButton(BTN: NSButton) {BTN. State = NSControl. StateValue. Off / / changed to "closed" state - not selected if BTN = = noticeWC? .surebtn {// Click "OK" print(" BTN == noticeWC? .surebtn ") //" determine "the appropriate operation // Close the mode, close the window nsApplication.shared.stopmodal () self.dismiss notice ()} else if BTN == noticeWC? CancelBtn {// Click "cancel" print(" BTN == noticeWC? CancelBtn ") //" dismiss "the corresponding operation // close the modal, close the window nsApplication.shared.stopmodal () self.dismiss notice ()}} // create and display the 'hint' window public func createShowNotice(with delegate: NSWindowDelegate?) {// Call noticeWC, avoid repeating the creation of noticeWC = noticeWC! = nil ? noticeWC : GYHNoticeAlertWindowController(title: nil, message: nil, backColor:nil, okStr: nil, cancelStr: Nil)// Custom initialization method noticeWC? .showWindow(nil) //if delegate ! = nil { // noticeWC? .window? .delegate = delegate; // no need to delegate WC? .sureBtn? .target = self; noticeWC? .sureBtn? .action = #selector(clickWindowButton) noticeWC? .cancelBtn? .target = self; noticeWC? .cancelBtn? .action = #selector(clickWindowButton) // Enable mode nsApplication.shared. runModal(for: (noticeWC?.window)!) } // Hide the 'hint' window public func dismissNotice() {noticeWC? .close()// Close the window //noticeWC? .window? .delegate = nil// No delegate wc = nil// set to nil} @objc func showNotice() {self.createshownotice (with: Nil)} func addOneBtn() {// For the window, add a button let BTN = NSButton(frame: NSMakeRect(100, 100, 100, 100)) btn.target = self; btn.action = #selector(showNotice) self.view .addSubview(btn) } override func viewDidLoad() { super.viewDidLoad() // Do Override var representedObject: any additional setup after loading the view.self.addonebtn ()} Override var representedObject: any? { didSet { // Update the view, if already loaded. } } }Copy the code

Effect:

1. Click on the ‘Button’ Button after playing from the definition “tips” window (GYHNoticeAlertWindowController window)! After the GYHNoticeAlertWindowController popup Windows (with the exception of the GYHNoticeAlertWindowController window can choose) other views can’t operate ~

2. Click on the ‘sure’/’ cancelled ‘after the GYHNoticeAlertWindowController window will disappear and its corresponding print! Other views can then operate ~

View View hierarchy in “Debug View Hierarchy “:

1. Default Windows (‘NSWindowController’) :

2. Custom “tips” window (‘ GYHNoticeAlertWindowController ‘) :

The idea is like this, the package looks at the individual ~ (do not want to go to the package 😒)

Tip1: Regarding the font center, color and size of the button, please refer to the section of “Setting the title color and font of the button” in “Use of NSButton (Mac Terminal Button)” : -(void)setTitleColorToColor:(NSColor *)color andFontNum:(CGFloat)FontNum isBold:(BOOL)isBold; Methods through the set up button * * attributedTitle properties used in * * * * (rich text) [paraStyle setAlignment: NSTextAlignmentCenter]; ** implements font center ~


Tip2: About the prompt message Label text that may be outside the scope of the control:

  • 1. You can set its **toolTip* * attributes –Mouse upIt will show! (The effect is as follows)

  • 2. See “iOS\Mac OS according to font size, control width, calculation of string display size!” – (NSSize)sizeForLblContent:(NSString *)strContent fixMaxWidth:(CGFloat)w andFondSize:(int)fontSize; Methods!

Also used in the article “Using Mac OS Controls, Encapsulating loading Views ProgressView” — Recursive font size determination!


Don’t want to write a Swift code ~😂

Tip3: Solve the “top title bar mouse can not drag, the border is not smooth” problem

will

self.window? . StyleMask = NSWindow. StyleMask. FullSizeContentView / / window style: top view + window (convergence)Copy the code

Replace it with the following code:

self.window? . StyleMask = [self. Window!. StyleMask, > NSWindow. StyleMask. FullSizeContentView] / / window style: // Get the 'Close', 'minimize', 'maximize' buttons on the left (system) let closeBtn = self.window? .standardWindowButton(NSWindow.ButtonType.closeButton) let zoomBtn = self.window? .standardWindowButton(NSWindow.ButtonType.zoomButton) let miniaturizeBtn = self.window? .standardWindowButton(NSWindow.ButtonType.miniaturizeButton) closeBtn? .ishidden = true // Hide the 'close' button // Initial position X:7.0 Y:3.0 zoomBtn? .ishidden = true // Hide the 'maximize' button // Initial position X: 47.0y :3.0 miniaturizeBtn? .ishidden = true // Hide the minimize button // Initial position X:27.0 Y:3.0Copy the code

Optimized effect: top title bar mouse can be dragged, border round


goyohol’s essay