xcode 创建没有 NIB 文件的 Cocoa 应用程序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2997333/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating a Cocoa application without NIB files
提问by Moddy
Yes, I know this goes against the whole MVC principle!
是的,我知道这违背了整个 MVC 原则!
However, I'm just trying to whip up a pretty trivial application - and I've pretty much implemented it. However, I have a problem...
然而,我只是想创建一个非常简单的应用程序 - 我已经实现了它。但是,我有一个问题......
I create an empty project, copy all the frameworks over and set the build settings - and I get errors about the executable, or lack of executable. The build settings all appear fine, but it tells me there is no executable - it will build + run fine. However it doesn't run. There is no error either - it just appears to run very fast and cleanly! Unless I try and run GDB which politely tells me I need to give it a file first..
我创建了一个空项目,复制了所有框架并设置了构建设置 - 我收到关于可执行文件的错误,或者缺少可执行文件。构建设置看起来都很好,但它告诉我没有可执行文件 - 它将构建 + 运行良好。但是它不运行。也没有错误 - 它似乎运行得非常快速和干净!除非我尝试运行 GDB,它礼貌地告诉我我需要先给它一个文件..
Running…
No executable file specified.
Use the "file" or "exec-file" command.
So I created a Cocoa application, removed all the stuff I didn't need (that is, the MainMenu.xib
file..), and now I can compile my code perfectly. However it dies complaining that it's
所以我创建了一个 Cocoa 应用程序,删除了所有我不需要的东西(即MainMenu.xib
文件..),现在我可以完美地编译我的代码。然而它死了,抱怨它是
"Unable to load nib file: MainMenu, exiting"
“无法加载 nib 文件:MainMenu,正在退出”
I have gone through the Project Symbols and see that the code actually relies upon the NIB file heavily, even if you don't touch it code-wise. (MVC again I guess..)
我已经浏览了项目符号并看到代码实际上严重依赖于 NIB 文件,即使您没有在代码方面接触它。(我猜又是MVC ..)
Is there a simple way to compile just what you code, no added NIB files, just the code you write and the frameworks you add? I assume it would be a blank project, but my experience tells me otherwise?!
有没有一种简单的方法来编译你编写的代码,不添加 NIB 文件,只编译你编写的代码和添加的框架?我认为这将是一个空白项目,但我的经验告诉我并非如此?!
采纳答案by slf
Of course you can write just code and not use Interface Builder.
当然,您可以只编写代码而不使用 Interface Builder。
Have you checked your Info.plist? By default there is an entry there for MainMenu.xib and it may be that reference it's complaining about.
你检查过你的 Info.plist 吗?默认情况下,MainMenu.xib 有一个条目,它可能是它抱怨的引用。
回答by Casper B. Hansen
This is the method I use in my applications. Sorry for the formatting, I hope you can make it out. I don't know how to turn off the auto-formatting here.
这是我在我的应用程序中使用的方法。抱歉格式化,我希望你能弄明白。我不知道如何在这里关闭自动格式化。
Of course there will be no functioning main menu out of this example, that's far too much code for me to write on a post like this :P - Sorry, out do some research on that ;)
当然,在这个例子中不会有任何功能的主菜单,对于我来说,在这样的帖子上写的代码太多了 :P - 抱歉,请对此进行一些研究;)
This should get you started:
这应该让你开始:
AppDelegate.h
AppDelegate.h
@interface MyApplicationDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate> {
NSWindow * window;
}
@end
AppDelegate.m
AppDelegate.m
@implementation MyApplicationDelegate : NSObject
- (id)init {
if (self = [super init]) {
// allocate and initialize window and stuff here ..
}
return self;
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
[window makeKeyAndOrderFront:self];
}
- (void)dealloc {
[window release];
[super dealloc];
}
@end
main.m
主文件
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSApplication * application = [NSApplication sharedApplication];
MyApplicationDelegate * appDelegate = [[[[MyApplicationDelegate]alloc] init] autorelease];
[application setDelegate:appDelegate];
[application run];
[pool drain];
return EXIT_SUCCESS;
}
回答by Bosko Popovic
int main() {
[NSAutoreleasePool new];
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
id menubar = [[NSMenu new] autorelease];
id appMenuItem = [[NSMenuItem new] autorelease];
[menubar addItem:appMenuItem];
[NSApp setMainMenu:menubar];
id appMenu = [[NSMenu new] autorelease];
id appName = [[NSProcessInfo processInfo] processName];
id quitTitle = [@"Quit " stringByAppendingString:appName];
id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle
action:@selector(terminate:) keyEquivalent:@"q"] autorelease];
[appMenu addItem:quitMenuItem];
[appMenuItem setSubmenu:appMenu];
id window = [[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 200, 200)
styleMask:NSTitledWindowMask backing:NSBackingStoreBuffered defer:NO]
autorelease];
[window cascadeTopLeftFromPoint:NSMakePoint(20,20)];
[window setTitle:appName];
[window makeKeyAndOrderFront:nil];
[NSApp activateIgnoringOtherApps:YES];
[NSApp run];
return 0;
}
回答by Eonil
Though this is a few years old question...
虽然这是几年前的问题......
Here's minimal code snippet to bootstrap a Cocoa application in Swift.
这是在 Swift 中引导 Cocoa 应用程序的最小代码片段。
import AppKit
final class ExampleApplicationController: NSObject, NSApplicationDelegate {
let window1 = NSWindow()
func applicationDidFinishLaunching(aNotification: NSNotification) {
window1.setFrame(CGRect(x: 0, y: 0, width: 800, height: 500), display: true)
window1.makeKeyAndOrderFront(self)
}
func applicationWillTerminate(aNotification: NSNotification) {
}
}
autoreleasepool { () -> () in
let app1 = NSApplication.sharedApplication()
let con1 = ExampleApplicationController()
app1.delegate = con1
app1.run()
}
Also, I am maintaining a bunch of programmatic examples for Cocoa including bootstrapping, window, menu creations.
此外,我正在维护一系列 Cocoa 的程序化示例,包括引导、窗口、菜单创建。
See subprojects for desired language.
有关所需语言,请参阅子项目。
回答by mipadi
The problem might be that you're still calling NSApplicationMain
in your main
function (in main.m
). If you're not loading a nib such as MainMenu.nib
, you'll probably have to rip out the call to NSApplicationMain
and write your own code in main
for starting the application.
问题可能是您仍在调用NSApplicationMain
您的main
函数 (in main.m
)。如果您没有加载诸如 之类的笔尖MainMenu.nib
,您可能必须删除对 的调用NSApplicationMain
并编写您自己的代码main
来启动应用程序。
回答by Vlad
Swift 4 version with NSToolbar
and NSMenu
(with event handlers instead of delegates):
带有NSToolbar
和的Swift 4 版本NSMenu
(使用事件处理程序而不是委托):
File main.swift:
文件main.swift:
autoreleasepool {
// Even if we loading application manually we need to setup `Info.plist` key:
// <key>NSPrincipalClass</key>
// <string>NSApplication</string>
// Otherwise Application will be loaded in `low resolution` mode.
let app = Application.shared
app.setActivationPolicy(.regular)
app.run()
}
File: Application.swift
文件:Application.swift
class Application: NSApplication {
private lazy var mainWindowController = MainWindowController()
private lazy var mainAppMenu = MainMenu()
override init() {
super.init()
setupUI()
setupHandlers()
}
required init?(coder: NSCoder) {
super.init(coder: coder) // This will never called.
}
}
extension Application: NSApplicationDelegate {
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
func applicationDidFinishLaunching(_ aNotification: Notification) {
mainWindowController.showWindow(nil)
}
}
extension Application {
private func setupUI() {
mainMenu = mainAppMenu
}
private func setupHandlers() {
delegate = self
mainAppMenu.eventHandler = { [weak self] in
switch class MainWindowController: NSWindowController {
private (set) lazy var viewController = MainViewController()
private (set) lazy var mainToolbar = MainToolbar(identifier: NSToolbar.Identifier("ua.com.wavelabs.Decoder:mainToolbar"))
init() {
let window = NSWindow(contentRect: CGRect(x: 400, y: 200, width: 800, height: 600),
styleMask: [.titled, .closable, .resizable, .miniaturizable],
backing: .buffered,
defer: true)
super.init(window: window)
let frameSize = window.contentRect(forFrameRect: window.frame).size
viewController.view.setFrameSize(frameSize)
window.contentViewController = viewController
window.titleVisibility = .hidden
window.toolbar = mainToolbar
setupHandlers()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
extension MainWindowController {
private func setupHandlers() {
mainToolbar.eventHandler = {
print(class MainViewController: NSViewController {
init() {
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
view = NSView()
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.magenta.cgColor
}
}
)
}
}
}
{
case .quit:
self?.terminate(nil)
}
}
}
}
File MainWindowController.swift
文件MainWindowController.swift
class MainToolbar: NSToolbar {
enum Event: Int {
case toggleSidePanel
}
let toolbarDelegate = GenericDelegate()
var eventHandler: ((MainToolbar.Event) -> Void)?
override init(identifier: NSToolbar.Identifier) {
super.init(identifier: identifier)
setupUI()
setupHandlers()
}
}
extension MainToolbar {
private func setupUI() {
allowsUserCustomization = true
autosavesConfiguration = true
displayMode = .iconOnly
toolbarDelegate.allowedItemIdentifiers = [.space, .flexibleSpace]
toolbarDelegate.selectableItemIdentifiers = [.space, .flexibleSpace]
toolbarDelegate.defaultItemIdentifiers = Event.toolbarIDs + [.flexibleSpace]
}
private func setupHandlers() {
delegate = toolbarDelegate
toolbarDelegate.makeItemCallback = { [unowned self] id, _ in
guard let event = Event(id: id) else {
return nil
}
return self.makeToolbarItem(event: event)
}
}
private func makeToolbarItem(event: Event) -> NSToolbarItem {
let item = NSToolbarItem(itemIdentifier: event.itemIdentifier)
item.setHandler { [weak self] in
guard let event = Event(id: event.itemIdentifier) else {
return
}
self?.eventHandler?(event)
}
item.label = event.label
item.paletteLabel = event.paletteLabel
if event.image != nil {
item.image = event.image
} else if event.view != nil {
item.view = event.view
}
return item
}
}
extension MainToolbar.Event {
init?(id: NSToolbarItem.Identifier) {
guard let event = (MainToolbar.Event.allValues.filter { class MainMenu: NSMenu {
enum Event {
case quit
}
var eventHandler: ((Event) -> Void)?
private lazy var applicationName = ProcessInfo.processInfo.processName
init() {
super.init(title: "")
setupUI()
}
required init(coder decoder: NSCoder) {
super.init(coder: decoder)
}
}
extension MainMenu {
private func setupUI() {
let appMenuItem = NSMenuItem()
appMenuItem.submenu = appMenu
addItem(appMenuItem)
}
private var appMenu: NSMenu {
let menu = NSMenu(title: "")
menu.addItem(title: "Quit \(applicationName)", keyEquivalent: "q") { [unowned self] in
self.eventHandler?(.quit)
}
return menu
}
}
.itemIdentifier == id }).first else {
return nil
}
self = event
}
static var allValues: [MainToolbar.Event] {
return [toggleSidePanel]
}
static var toolbarIDs: [NSToolbarItem.Identifier] {
return [toggleSidePanel].map { extension NSMenu {
@discardableResult
public func addItem(title: String, keyEquivalent: String, handler: NSMenuItem.Handler?) -> NSMenuItem {
let item = addItem(withTitle: title, action: nil, keyEquivalent: keyEquivalent)
item.setHandler(handler)
return item
}
}
.itemIdentifier }
}
var itemIdentifier: NSToolbarItem.Identifier {
switch self {
case .toggleSidePanel: return NSToolbarItem.Identifier("ua.com.wavalabs.toolbar.toggleSidePanel")
}
}
var label: String {
switch self {
case .toggleSidePanel: return "Toggle Side Panel"
}
}
var view: NSView? {
return nil
}
var image: NSImage? {
switch self {
case .toggleSidePanel: return NSImage(named: NSImage.Name.folder)
}
}
var paletteLabel: String {
return label
}
}
File MainViewController.swift
文件MainViewController.swift
extension NSMenuItem {
public typealias Handler = (() -> Void)
convenience init(title: String, keyEquivalent: String, handler: Handler?) {
self.init(title: title, action: nil, keyEquivalent: keyEquivalent)
setHandler(handler)
}
public func setHandler(_ handler: Handler?) {
target = self
action = #selector(wavelabsActionHandler(_:))
if let handler = handler {
ObjCAssociation.setCopyNonAtomic(value: handler, to: self, forKey: &OBJCAssociationKeys.actionHandler)
}
}
}
extension NSMenuItem {
private struct OBJCAssociationKeys {
static var actionHandler = "com.wavelabs.actionHandler"
}
@objc private func wavelabsActionHandler(_ sender: NSControl) {
guard sender == self else {
return
}
if let handler: Handler = ObjCAssociation.value(from: self, forKey: &OBJCAssociationKeys.actionHandler) {
handler()
}
}
}
File MainToolbar.swift
文件MainToolbar.swift
extension NSToolbar {
class GenericDelegate: NSObject, NSToolbarDelegate {
var selectableItemIdentifiers: [NSToolbarItem.Identifier] = []
var defaultItemIdentifiers: [NSToolbarItem.Identifier] = []
var allowedItemIdentifiers: [NSToolbarItem.Identifier] = []
var eventHandler: ((Event) -> Void)?
var makeItemCallback: ((_ itemIdentifier: NSToolbarItem.Identifier, _ willBeInserted: Bool) -> NSToolbarItem?)?
}
}
extension NSToolbar.GenericDelegate {
enum Event {
case willAddItem(item: NSToolbarItem, index: Int)
case didRemoveItem(item: NSToolbarItem)
}
}
extension NSToolbar.GenericDelegate {
func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
return makeItemCallback?(itemIdentifier, flag)
}
func toolbarDefaultItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
return defaultItemIdentifiers
}
func toolbarAllowedItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
return allowedItemIdentifiers
}
func toolbarSelectableItemIdentifiers(_: NSToolbar) -> [NSToolbarItem.Identifier] {
return selectableItemIdentifiers
}
// MARK: Notifications
func toolbarWillAddItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem,
let index = notification.userInfo?["newIndex"] as? Int {
eventHandler?(.willAddItem(item: toolbarItem, index: index))
}
}
func toolbarDidRemoveItem(_ notification: Notification) {
if let toolbarItem = notification.userInfo?["item"] as? NSToolbarItem {
eventHandler?(.didRemoveItem(item: toolbarItem))
}
}
}
File MainMenu.swift
文件MainMenu.swift
extension NSToolbarItem {
public typealias Handler = (() -> Void)
public func setHandler(_ handler: Handler?) {
target = self
action = #selector(wavelabsActionHandler(_:))
if let handler = handler {
ObjCAssociation.setCopyNonAtomic(value: handler, to: self, forKey: &OBJCAssociationKeys.actionHandler)
}
}
}
extension NSToolbarItem {
private struct OBJCAssociationKeys {
static var actionHandler = "com.wavelabs.actionHandler"
}
@objc private func wavelabsActionHandler(_ sender: NSControl) {
guard sender == self else {
return
}
if let handler: Handler = ObjCAssociation.value(from: self, forKey: &OBJCAssociationKeys.actionHandler) {
handler()
}
}
}
Convenience extensions.
便利扩展。
File NSMenu.swift
文件NSMenu.swift
public struct ObjCAssociation {
public static func value<T>(from object: AnyObject, forKey key: UnsafeRawPointer) -> T? {
return objc_getAssociatedObject(object, key) as? T
}
public static func setAssign<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_ASSIGN)
}
public static func setRetainNonAtomic<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
public static func setCopyNonAtomic<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
public static func setRetain<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_RETAIN)
}
public static func setCopy<T>(value: T?, to object: Any, forKey key: UnsafeRawPointer) {
objc_setAssociatedObject(object, key, value, .OBJC_ASSOCIATION_COPY)
}
}
File NSMenuItem.swift
文件NSMenuItem.swift
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate> {
NSWindow* window;
}
@end
@implementation AppDelegate : NSObject
- (id)init {
if (self = [super init]) {
window = [NSWindow.alloc initWithContentRect: NSMakeRect(0, 0, 200, 200)
styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskClosable
backing: NSBackingStoreBuffered
defer: NO];
}
return self;
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
window.title = NSProcessInfo.processInfo.processName;
[window cascadeTopLeftFromPoint: NSMakePoint(20,20)];
[window makeKeyAndOrderFront: self];
}
@end
int main(int argc, const char * argv[]) {
NSApplication* app = NSApplication.sharedApplication;
app.ActivationPolicy = NSApplicationActivationPolicyRegular;
NSMenuItem* item = NSMenuItem.new;
NSApp.mainMenu = NSMenu.new;
item.submenu = NSMenu.new;
[app.mainMenu addItem: item];
[item.submenu addItem: [[NSMenuItem alloc] initWithTitle: [@"Quit " stringByAppendingString: NSProcessInfo.processInfo.processName] action:@selector(terminate:) keyEquivalent:@"q"]];
AppDelegate* appDelegate = AppDelegate.new; // cannot collapse this and next line because .dlegate is weak
app.delegate = appDelegate;
(void)app.run;
return 0;
}
File NSToolbar.swift
文件NSToolbar.swift
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
NSApplication *application = [NSApplication sharedApplication];
AppDelegate *appDelegate = [[AppDelegate alloc] init];
[application setDelegate:appDelegate];
[application run];
}
return EXIT_SUCCESS;
}
File NSToolbarItem.swift
文件NSToolbarItem.swift
import Cocoa
let delegate = ExampleApplicationController() //alloc main app's delegate class
NSApplication.shared().delegate = delegate //set as app's delegate
let ret = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
File ObjCAssociation.swift
文件ObjCAssociation.swift
##代码##回答by Leo
7 years too late to the party, but a bit simpler single file code
7年来晚了,不过单文件代码简单一点
##代码##回答by jasongrlicky
Here is Casper's solution, updated for ARC as per Marco's suggestion:
这是Casper 的解决方案,根据Marco 的建议针对 ARC 进行了更新:
##代码##回答by iHarshil
Of course, it's too late to answer on this but for anyone who is thinking on creating iOS App without Xib(Nib) files should keep this thing in mind.
当然,现在回答这个问题为时已晚,但对于正在考虑创建没有 Xib(Nib) 文件的 iOS 应用程序的任何人来说,都应该记住这一点。
Note: Although you can create an Objective-C application without using nib files, doing so is very rare and not recommended. Depending on your application, avoiding nib files might require you to replace large amounts of framework behavior to achieve the same results you would get using a nib file.
注意:虽然您可以在不使用 nib 文件的情况下创建 Objective-C 应用程序,但这种做法非常少见,不推荐使用。根据您的应用程序,避免使用 nib 文件可能需要您替换大量的框架行为,以获得与使用 nib 文件相同的结果。
See this Documentation to know more what apple has to say on this approach
I hope this could help someone in future. Thanks!
我希望这可以帮助将来的某个人。谢谢!
回答by Ted Lemon
The sample swift code for the autoreleasepool snippet provided abovedoes not work in modern Xcode. Instead, you need to get rid of the @NSApplicationMain in your App Delegate source file, if there is one (Xcode now adds these for new projects), and add a main.swift file that contains the following:
上面提供的 autoreleasepool 片段的示例 swift 代码在现代 Xcode 中不起作用。相反,您需要删除 App Delegate 源文件中的 @NSApplicationMain,如果有的话(Xcode 现在为新项目添加了这些),并添加一个包含以下内容的 main.swift 文件:
The top level code sample above no longer works in recent versions of Xcode. Instead use this:
上面的顶级代码示例在最新版本的 Xcode 中不再有效。而是使用这个:
##代码##