NIB 的显示窗口防止多个实例

问题描述 投票:0回答:3

我正在尝试从 nib 加载和显示窗口,但不明白如何正确地做到这一点。 现在我使用以下代码:

import Cocoa

class AppDelegate: NSObject, NSApplicationDelegate {
    
    
    var statusItem: NSStatusItem!
    var statusButton: NSStatusBarButton!
    
    var menu: NSMenu!
    var menuItemTitle: NSMenuItem!
    var menuItemInfo: NSMenuItem!
    var menuItemQuit: NSMenuItem!
    
    var menuImage: NSImage!
    
    var titleView: TitleView!
        
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        menuImage = NSImage(systemSymbolName: "menubar.rectangle", accessibilityDescription: "Image")
        
        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
        statusButton = statusItem.button
        statusButton.image = menuImage
        
        menuItemTitle = NSMenuItem(title: "First", action: nil, keyEquivalent: "")
        menuItemInfo = NSMenuItem(title: "Info", action: #selector(actionInfo), keyEquivalent: "i")
        menuItemQuit = NSMenuItem(title: "Quit", action: #selector(actionQuit), keyEquivalent: "q")
        
        menu = NSMenu()
        menu.addItem(menuItemTitle)
        menu.addItem(menuItemInfo)
        menu.addItem(NSMenuItem.separator())
        menu.addItem(menuItemQuit)
        
        statusItem.menu = menu
        
        titleView = TitleView(frame: NSRect(x: 0, y: 0, width: 250, height: 70))
        menuItemTitle.view = titleView
    }
    
    func applicationWillTerminate(_ aNotification: Notification) {
    }
    
    func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
        return true
    }
    
    @objc func actionQuit() {
        NSApplication.shared.terminate(self)
    }
    
    var infoWindowController: InfoWindowController!
    @objc func actionInfo() {
        infoWindowController = InfoWindowController()
        infoWindowController.showWindow(self)
//        infoWindowController.showWin(self)
    }
    
}

import Foundation
import Cocoa

class InfoWindowController: NSWindowController {
    
    @IBOutlet var infoWindow: NSWindow!
    @IBOutlet weak var infoIcon: NSImageView!
    
    override var windowNibName: String! {
        return "InfoWindow"
    }
    
    override func windowDidLoad() {
        print("windowDidLoad")
    }
        
    func showWin(_ sender: Any) {
        _ = window
        showWindow(sender)
        /// This does not work
//        window?.center()
//        window?.makeKeyAndOrderFront(self)
        
        //This works with the first NSViewController.window access
//        infoWindow?.center()
//        infoWindow?.makeKeyAndOrderFront(self)
    }
}

信息窗口.xib

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
    <dependencies>
        <deployment identifier="macosx"/>
        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21701"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <customObject id="-2" userLabel="File's Owner" customClass="InfoWindowController" customModule="GUI" customModuleProvider="target">
            <connections>
                <outlet property="infoWindow" destination="QvC-M9-y7g" id="jBK-DN-ufD"/>
            </connections>
        </customObject>
        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
        <window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g">
            <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
            <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
            <rect key="contentRect" x="196" y="240" width="480" height="270"/>
            <rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
            <view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
                <rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
                <autoresizingMask key="autoresizingMask"/>
                <subviews>
                    <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2Ad-gr-Tqg">
                        <rect key="frame" x="185" y="166" width="56" height="16"/>
                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
                        <textFieldCell key="cell" lineBreakMode="clipping" title="Hi there!" id="lZn-he-q8e">
                            <font key="font" usesAppearanceFont="YES"/>
                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
                        </textFieldCell>
                    </textField>
                </subviews>
            </view>
            <point key="canvasLocation" x="105" y="144"/>
        </window>
    </objects>
</document>

我没有故事板,所以我使用 main.swift:

import Foundation
import Cocoa

let app = NSApplication.shared
let delegate = AppDelegate()
app.delegate = delegate

_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

根据here 的文档,我需要“访问 window 属性以便调用 windowDidLoad() 和 windowWillLoad() 方法”。在执行

_ = window
行后,我观察 windowDidLoad 。但即便如此,我的控制器的 ``window` 属性仍然为零。但是 IBOutlet 开始工作了,所以我的工作代码是这样的:

    func show() {
        _ = window
        infoWindow?.center()
        infoWindow?.makeKeyAndOrderFront(self)
    }

我的问题:是否可以在没有 IBOutlet 的情况下从 xib 加载和显示窗口?或者只是使用 IBOutlet,而不访问 NSWindowController 的窗口属性?如何以“正确”的方式做到这一点?而且,我怎样才能阻止用户多次激活此窗口? 谢谢!

更新:亲爱的同事们,感谢你们的帮助!我非常感激。我已经 制作了一个 github repo 以便更容易跟踪您建议我尝试的更改。

swift macos appkit
3个回答
1
投票

你在问多个不同的问题,这让事情变得非常混乱。从你的问题的标题来看,你似乎看到了多个信息窗口(每次你选择信息菜单项时可能会多一个)。我认为这是因为您的

actionInfo
处理程序每次调用时都会创建一个新实例。原始代码将用新实例覆盖
infoWindowController
属性。

您可以创建一次信息窗口并使用

lazy
延迟创建直到需要它:

lazy private var infoWindowController: InfoWindowController = {
        InfoWindowController()
}()
@objc func actionInfo() {
    infoWindowController.showWindow(self)
}

请注意,即使关闭窗口,这也会使信息窗口控制器的实例保持活动状态。


0
投票

将以下代码添加到您的窗口控制器。

override init(window: NSWindow!)
{
    super.init(window: window)
}

required init(coder: NSCoder)
{
    super.init(coder: coder)!
}

convenience init()
{
    self.init(windowNibName: "InfoWindowController", owner:(Any).self)
    
}

然后创建窗口;

   var myWindow = InfoWindowController(windowNibName: "InfoWindowController")
   myWindow.showWindow(self)

0
投票

是否可以在没有 infoWindow IBOutlet 的情况下从 xib 加载和显示窗口?

是的,将xib中的File's Owner(

InfoWindowController
)的window outlet连接到window上

你不必打电话给

_ = window
。方法
windowDidLoad()
windowWillLoad()
将在加载窗口时自动调用,例如
showWindow
.

© www.soinside.com 2019 - 2024. All rights reserved.