如何用纯 Swift 3 替换我的 .xib 文件?

Posted

技术标签:

【中文标题】如何用纯 Swift 3 替换我的 .xib 文件?【英文标题】:How can I replace my .xib file with pure Swift 3? 【发布时间】:2017-03-18 13:04:35 【问题描述】:

我正在制作一个仅在菜单栏中运行的 macOS Cocoa 应用程序。当我最初创建项目时,Xcode 给了我一个名为MainMenu.xib 的文件。这个MainMenu.xib 文件似乎不包含与我的菜单栏应用程序相关的任何内容,因此我希望将其删除。

Cocoa 入口点the function NSApplicationMain,“从应用程序的主包中加载主 nib 文件”。我在其他地方读到 Cocoa 通过查询我的Info.plist 中的键NSMainNibFile 找到了“主笔尖文件”,该键设置为字符串MainMenu。我删除了密钥NSMainNibFile,我的程序仍然表现相同。由此,我假设 Cocoa 看到了这个键的缺失,所以它跳过了它的 nib/xib 加载步骤。

由于我的MainMenu.xib 文件不再从任何地方引用,我删除了它(这从我的project.pbxproj 中删除了一些看似无辜的引用)。但是,删除MainMenu.xib 后,我的应用程序不再工作。我的AppDelegate 类上的applicationDidFinishLaunching 方法没有被调用!

这意味着两件事。首先,这意味着即使NSMainNibFile 不存在,Cocoa 仍然会神奇地找到我的MainMenu.xib 文件(如果我将其重命名为Foo.xib,Cocoa 仍然会找到该文件)。更重要的是,这意味着我的MainMenu.xib is 中的某些内容仍然需要我的应用程序运行。这是完整的MainMenu.xib

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
    </dependencies>
    <objects>
        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
            <connections>
                <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
            </connections>
        </customObject>
        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
        <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Foo" customModuleProvider="target"/>
    </objects>
</document> 

我认为问题在于我的MainMenu.xib 引用了我的AppDelegate 类,而Cocoa 使用它来实例化AppDelegate 类,然后在对象上调用applicationDidFinishLaunching。这令人困惑,因为我认为 AppDelegate 类上的 @NSApplicationMain 注释就足够了。

因此,我有几个问题:

    Cocoa(即NSApplicationMain)如何找到我的MainMenu.xib 文件? Cocoa 的搜索程序是什么?如何让 Cocoa 跳过这个加载步骤? 我的MainMenu.xib 文件在做什么? 也就是说,Cocoa 是如何解释MainMenu.xib 文件的,结果它做了什么?具体来说,这个文件如何导致我的 AppDelegate 类被实例化? 如何在 Swift 3 中纯粹以编程方式重新创建此逻辑,以便删除 MainMenu.xib 文件?我需要使用哪些 API?

【问题讨论】:

其中一个问题***.com/questions/5237833/…可能重复 另请注意developer.apple.com/reference/appkit/nsapplication,它描述了NSApplicationMain 的实际作用。 @Sulthan 谢谢,但不完全是:这个问题与拆分 .xib 文件有关,而我关心的是完全删除 .xib 文件。 恕我直言,nib 只做一件事 - 它创建一个委托实例并将其连接到 NSApp 我很好奇……你为什么对摆脱 XIB 感兴趣? 【参考方案1】:

问题 #3 的答案:

创建一个 Swift 文件main.swift

main.swift中的代码替换为

import Cocoa

let appDelegate = AppDelegate()
NSApplication.shared().delegate = appDelegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
删除AppDelegate中的@NSApplicationMain。 删除Info.plist中的键/值对NSMainNibFile。 删除 .xib 文件。

【讨论】:

谢谢!这几乎有效!我在AppDelegate 初始化中调用NSStatusBar.system(),但失败并显示“断言失败:(CGAtomicGet(&is_initialized))”。这一定是因为NSApplicationMain 应该在我的AppDelegate 被实例化之前运行,但NSApplicationMain 永远不会返回,所以我认为我必须以某种方式将AppDelegate 的引用传递给NSApplicationMain 以便它可以在适当的时候实例化它...? 如果你有一个标准的 macOS 项目并完全按照我的建议(例如 main 必须以小写字母开头)它应该可以工作,因为 main 总是在 applicationDidFinishLaunching 之前执行您应该在其中创建状态栏。 class AppDelegate: NSObject, NSApplicationDelegate var statusBar: NSStatusBar! = NSStatusBar.system(); 这个例子为例 - 它适用于@NSApplicationMain,但使用上面的代码会引发断言错误。不知何故,@NSApplicationMainAppDelegate 的实例化延迟到NSApplicationMain 中发生一些初始化之后。 applicationDidFinishLaunching中初始化statusBar 确实有效,但我特别想知道@NSApplicationMain 是如何实现所描述的执行顺序的......无论如何,我会接受你的回答(非常有帮助)并移动它一个单独的问题。再次感谢!【参考方案2】:

    您的 XIB 文件的名称存储在您的 Info.plist 中。您可以通过选择您的 Xcode 项目文件并单击 Info 选项卡来直接修改它,或者您可以从“Main Interface”下的 General 选项卡进行更改。

    每个默认 MainMenu.xib 的顶层都是一个 AppDelegate 对象。该对象是接收所有应用程序委托消息的对象。它连接到占位符 Application 对象的 delegate 出口。

    要复制此行为,您需要子类化 NSApplication 并修改目标的“主体类”Info.plist 设置以反映新的子类。在您的子类中,您可以覆盖 finishLaunching() 之类的内容来创建您的应用程序委托对象并进行设置。但是请记住,在这样做时,您需要确保您的新委托在某处具有强引用,因为它不再由 nib 文件拥有。我建议为您的子类添加一个强大的属性,例如 strongDelegate

【讨论】:

嗨,鲍勃!谢谢。我编辑了 Info.plist 以删除 NSMainNibFile 键,我认为您指的是它。但是 Cocoa 仍然找到了我的 .xib 文件,这就是我询问 Cocoa 搜索程序的原因 - 它绝对不仅仅是查看我的 Info.plist 来寻找答案。

以上是关于如何用纯 Swift 3 替换我的 .xib 文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何用html doc中的超链接替换纯url

如何用纯文本内容控件替换富文本内容控件

如何用应用商店中的新版本替换 iOS 应用中预先填充的核心数据?

为啥每次都加载新的 xib 文件?为啥不替换前一个?

如何用字符串替换缩写?

Swift下使用Xib设计界面