0
点赞
收藏
分享

微信扫一扫

macOS警告/提示视图 — NSAlert、自定义WindowController

系统的NSAlert——系统自带提示视图

在'ViewController.swift'文件中,先创建一个NSAlert实例来看看~

import Cocoa

class ViewController: NSViewController {

    @objc func clickItem(btn: NSButton) {
        print("clickItem:\(btn.title)")
        
    }
    @objc func showNotice() {
        let alert = NSAlert()
        //添加的前3个项(按钮),会响应‘点击选项时系统的回调’方法——且枚举中有对应项进行确认
        alert .addButton(withTitle: "确定")
        alert .addButton(withTitle: "取消")
        let item3 = alert .addButton(withTitle: "More") //获取对应的按钮
        //item3.target = self; item3.action = #selector(clickItem)//重写方法—覆盖‘点击选项时系统的回调’方法
        //第4项(按钮)开始,会响应‘点击选项时系统的回调’方法——但枚举中没有对应项进行确认
        let item4 = alert .addButton(withTitle: "第4项")  //获取对应的按钮
        //item4.target = self; item4.action = #selector(clickItem)//重写方法—覆盖‘点击选项时系统的回调’方法
        let item5 = alert .addButton(withTitle: "第5项")  //获取对应的按钮
        //item5.target = self; item5.action = #selector(clickItem)//重写方法—覆盖‘点击选项时系统的回调’方法
        alert.messageText = "blabla操作的警告"//标题
        alert.informativeText = "blabla操作会导致XX后果,是否继续?"//详细描述
        //设置NSAlert的展示风格
        alert.alertStyle = NSAlert.Style.critical//warning\informational\critical
        alert .beginSheetModal(for: self.view .window!) { (returnCode: NSApplication.ModalResponse) in
            //点击选项时系统的回调
            switch returnCode {
            case .alertFirstButtonReturn:
                print("NSApplication.ModalResponse.alertFirstButtonReturn")
            case .alertSecondButtonReturn:
                print("NSApplication.ModalResponse.alertSecondButtonReturn")
            case .alertThirdButtonReturn:
                print("NSApplication.ModalResponse.alertThirdButtonReturn")
            //以下条件均不会到达
            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() {//为该window,随便添加一个按钮
        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 any additional setup after loading the view.
        self .addOneBtn()//添加按钮
        
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }


}

效果:
1.点击'Button'按钮后弹出NSAlert视图!弹出该NSAlert视图(除了该NSAlert视图 进行选择)其他视图 不能进行操作~
2.前3项(按钮)——点击第1项('确定')/第2项('取消')/第3项('More')后NSAlert视图会消失并打印NSApplication.ModalResponse.alertFirstButtonReturn/NSApplication.ModalResponse.alertSecondButtonReturn/NSApplication.ModalResponse.alertThirdButtonReturn
3.从第4项(按钮)开始,点击第4项('第4项')/第5项('第5项')后NSAlert视图会消失并都打印Other situation——default

结论:添加的前3个项(按钮),会响应‘点击选项时系统的回调’方法——且枚举中有对应项进行确认!第4项(按钮)开始,会响应‘点击选项时系统的回调’方法——但枚举中没有对应项进行确认!


由上面代码可以发现NSAlert实例对象是基于NSWindow对象创建的!
所以代码如下:为NSWindow类添加扩展(extension)~

 extension NSWindow {
    //创建macOS警告/提示视图——返回NSApplication.ModalResponse
    public func showAlertNotice(firstItemStr: String, secondItemStr: String, title: String, information: String, alertStyle: NSAlert.Style, completionHalder handler: @escaping ((NSApplication.ModalResponse) -> Void)) {//@escaping——去除“scaping closure captures non-escaping parameter 'handler'”警告
        let alert = NSAlert()
        alert .addButton(withTitle: firstItemStr)
        alert .addButton(withTitle: secondItemStr)
        alert.messageText = title//标题
        alert.informativeText = information//详细描述
        //设置NSAlert的展示风格
        alert.alertStyle = alertStyle//warning\informational\critical
        alert .beginSheetModal(for: self) { (returnCode: NSApplication.ModalResponse) in
            //点击选项时系统的回调
            handler(returnCode)
        }
    }
    //创建macOS警告/提示视图——返回bool值
    public func showAlertNoticeWithBool(firstItemStr: String, secondItemStr: String, title: String, information: String, alertStyle: NSAlert.Style, completionHalder handler: @escaping ((Bool) -> Void)) {//@escaping——去除“scaping closure captures non-escaping parameter 'handler'”警告
        let alert = NSAlert()
        alert .addButton(withTitle: firstItemStr)
        alert .addButton(withTitle: secondItemStr)
        alert.messageText = title//标题
        alert.informativeText = information//详细描述
        //设置NSAlert的展示风格
        alert.alertStyle = alertStyle//warning\informational\critical
        alert .beginSheetModal(for: self) { (returnCode: NSApplication.ModalResponse) in
            //点击选项时系统的回调
            if returnCode == .alertFirstButtonReturn {//点击"确定"
                handler(true)
            } else if returnCode == .alertSecondButtonReturn {//点击"取消"
                handler(false)
            }
        }
        
    }
}

方法的调用:在'ViewController.swift'文件中

//返回NSApplication.ModalResponse
self.view.window! .showAlertNotice(firstItemStr: "OK", secondItemStr: "取消", title: "标题", information: "警告的提示内容和信息", alertStyle: .critical) { (returnCode : NSApplication.ModalResponse) in
    if returnCode == .alertFirstButtonReturn {//点击"确定"
        print("returnCode == .alertFirstButtonReturn")
        //"确定"相应的操作
    } else if returnCode == .alertSecondButtonReturn {//点击"取消"
        print("returnCode == .alertSecondButtonReturn")
        //"取消"相应的操作
    }
}

//返回bool值
self.view.window! .showAlertNoticeWithBool(firstItemStr: "OK", secondItemStr: "取消", title: "标题", information: "警告的提示内容和信息", alertStyle: .critical) { (isOK: Bool) in
    if isOK == true {//点击"确定"
        print("isOK == true")
        //"确定"相应的操作
    } else {//点击"取消"
        print("isOK == false")
        //"取消"相应的操作
    }
}



自定义WindowController

新建一个自定义的GYHNoticeAlertWindowController类(需勾选'Also create XIB file for user interface'

添加的图片素材:

在'GYHNoticeAlertWindowController.swift'文件中,进行控件UI布局

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!//标题
    }
    
}

在'ViewController.swift'文件中,进行使用:

import Cocoa

class ViewController: NSViewController, NSWindowDelegate {
    
    var noticeWC: GYHNoticeAlertWindowController?//自定义‘提示’窗口
    //事件的响应——自定义‘提示’窗口
    @objc func clickWindowButton(btn: NSButton) {
        btn.state = NSControl.StateValue.off//改为‘关闭’状态——非选中
        
        if btn == noticeWC?.sureBtn {//点击"确定"
            print("btn == noticeWC?.sureBtn")
            //"确定"相应的操作
            
            //关闭模态、关闭窗口
            NSApplication .shared .stopModal()
            self .dismissNotice()
        } else if btn == noticeWC?.cancelBtn {//点击"取消"
            print("btn == noticeWC?.cancelBtn")
            //"取消"相应的操作
            
            //关闭模态、关闭窗口
            NSApplication .shared .stopModal()
            self .dismissNotice()
        }
    }
    //创建并展示‘提示’窗口
    public func createShowNotice(with delegate: NSWindowDelegate?) {
        //判空,避免重复创建
        noticeWC = noticeWC != nil ? noticeWC : GYHNoticeAlertWindowController(title: nil, message: nil, backColor:nil, okStr: nil, cancelStr: nil)//自定义初始化方法
        noticeWC? .showWindow(nil)
        //if delegate != nil {
        //    noticeWC?.window?.delegate = delegate;
        //} //无需delegate
        noticeWC?.sureBtn?.target = self;   noticeWC?.sureBtn?.action = #selector(clickWindowButton)
        noticeWC?.cancelBtn?.target = self; noticeWC?.cancelBtn?.action = #selector(clickWindowButton)
        
        //开启模态
        NSApplication .shared .runModal(for: (noticeWC?.window)!)
    }
    //隐藏‘提示’窗口
    public func dismissNotice() {
        noticeWC? .close()//关闭窗口
        //noticeWC?.window?.delegate = nil  //无需delegate
        noticeWC = nil//置为nil
    }
    
    @objc func showNotice() {
        self .createShowNotice(with: nil)
    }

    func addOneBtn() {//为该window,随便添加一个按钮
        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 any additional setup after loading the view.
        self .addOneBtn()//添加按钮
        
    }
        
    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }


}

效果:
1.点击'Button'按钮后弹出自定义‘提示’窗口GYHNoticeAlertWindowController窗口)!弹出GYHNoticeAlertWindowController窗口(除了该GYHNoticeAlertWindowController窗口 进行选择)其他视图 不能进行操作~
2.点击'确定'/'取消'后该GYHNoticeAlertWindowController窗口会消失并进行其相应的打印!之后其他视图 可以进行操作~


思路如此,封装看个人~(不想去封装了?)











goyohol's essay

举报

相关推荐

0 条评论