Ответ 1
Конечно, вы можете написать только код и не использовать Interface Builder.
Вы проверили свой Info.plist? По умолчанию для MainMenu.xib есть запись, и это может быть ссылка, на которую она жалуется.
Да, я знаю, что это противоречит всему принципу MVC!
Тем не менее, я просто пытаюсь взломать довольно тривиальное приложение, и я его в значительной степени реализовал. Однако у меня есть проблема...
Я создаю пустой проект, копирую все фреймворки и устанавливаю параметры сборки - и получаю ошибки об исполняемом файле или отсутствие исполняемого файла. Настройки сборки все выглядят отлично, но он говорит мне, что нет исполняемого файла - он будет создавать + работать нормально. Однако он не работает. Ошибок тоже нет - он просто работает очень быстро и чисто! Если я не попытаюсь запустить GDB, который вежливо говорит мне, что мне нужно сначала дать ему файл.
Running…
No executable file specified.
Use the "file" or "exec-file" command.
Итак, я создал приложение Cocoa, удалил все, что мне не нужно (то есть файл MainMenu.xib
..), и теперь я могу скомпилировать свой код отлично. Однако он умирает, жалуясь, что он
"Невозможно загрузить файл nib: MainMenu, выход из
Я воспользовался символами проекта и вижу, что код действительно сильно зависит от файла NIB, даже если вы не трогаете его по коду. (MVC снова, я думаю..)
Есть ли простой способ скомпилировать только то, что вы кодируете, не добавляете файлы NIB, только код, который вы пишете, и добавляемые рамки? Я предполагаю, что это будет пустой проект, но мой опыт говорит мне иначе?!
Конечно, вы можете написать только код и не использовать Interface Builder.
Вы проверили свой Info.plist? По умолчанию для MainMenu.xib есть запись, и это может быть ссылка, на которую она жалуется.
Это метод, который я использую в своих приложениях. Извините за форматирование, надеюсь, вы сможете это сделать. Я не знаю, как отключить автоформатирование здесь.
Конечно, из этого примера не будет функционировать главное меню, для меня слишком много кода для написания на такой странице: P - Извините, сделайте некоторые исследования по этому поводу;)
Это должно помочь вам:
AppDelegate.h
@interface MyApplicationDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate> {
NSWindow * window;
}
@end
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;
}
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;
}
Хотя это вопрос на несколько лет...
Здесь минимальный фрагмент кода для загрузки приложения Cocoa в Swift.
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()
}
Кроме того, я поддерживаю кучу программных примеров для Cocoa, включая загрузку, создание окон, меню.
См. подпроекты для желаемого языка.
Проблема может заключаться в том, что вы все еще вызываете NSApplicationMain
в своей функции main
(в main.m
). Если вы не загружаете нип, например MainMenu.nib
, вам, вероятно, придется вырвать вызов на NSApplicationMain
и написать свой собственный код в main
для запуска приложения.
Вот решение Casper, обновленное для ARC в соответствии с предложением Марко:
#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;
}
7 лет слишком поздно для вечеринки, но немного более простой одиночный файл
#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;
}
версия Swift 4 с NSToolbar
и NSMenu
(с обработчиками событий вместо делегатов):
Файл 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()
}
Файл: 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 $0 {
case .quit:
self?.terminate(nil)
}
}
}
}
Файл MainWindowController.swift
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($0)
}
}
}
Файл MainViewController.swift
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
}
}
Файл MainToolbar.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 { $0.itemIdentifier == id }).first else {
return nil
}
self = event
}
static var allValues: [MainToolbar.Event] {
return [toggleSidePanel]
}
static var toolbarIDs: [NSToolbarItem.Identifier] {
return [toggleSidePanel].map { $0.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
}
}
Файл MainMenu.swift
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
}
}
Удобные расширения.
Файл NSMenu.swift
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
}
}
Файл NSMenuItem.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()
}
}
}
Файл NSToolbar.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))
}
}
}
Файл NSToolbarItem.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()
}
}
}
Файл ObjCAssociation.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)
}
}
Образец быстрого кода для фрагмента autoreleasepool приведенного выше, не работает в современном Xcode. Вместо этого вам нужно избавиться от @NSApplicationMain в исходном файле делегата приложения, если есть один (Xcode теперь добавляет их для новых проектов) и добавить файл main.swift, содержащий следующее:
Пример кода верхнего уровня выше не работает в последних версиях Xcode. Вместо этого используйте это:
import Cocoa
let delegate = ExampleApplicationController() //alloc main app delegate class
NSApplication.shared().delegate = delegate //set as app delegate
let ret = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
Не используйте NSApplication и NSApp...
Вам просто нужно указать класс, который реализует протокол UIApplicationDelegate:
UIApplicationMain(argc, argv, nil, @"Name of your class");