【问题标题】:Display window on OSX using Swift without XCode or NIB在没有 XCode 或 NIB 的情况下使用 Swift 在 OSX 上显示窗口
【发布时间】:2015-06-10 17:24:17
【问题描述】:

免责声明:我正在尝试以下练习,因为我认为它具有指导意义。我对如何完成很感兴趣。所以请不要急于跳入“这是错误的做法,你永远不应该这样做!”

使用我最喜欢的文本编辑器从命令行工作,我想构建一个显示窗口的最小 Swift 程序。

如果你愿意,这是一个 GUI/Cococa hello 世界。

本着同样的精神,我想避免NIB。

所以,没有 XCode,没有 NIB。

我想:

  • 用 swift 编译器编译它
  • 创建一个#!使用 Swift 解释器运行的 swift 脚本

如果我能同时做这两件事,我会感到自己的脚在地上,并且更容易升级到 Xcode。

我尝试了以下方法:

window.swift

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    @IBOutlet weak var window: NSWindow!
    let newWindow = NSWindow(contentRect : NSScreen.mainScreen()!.frame
                             , styleMask : NSBorderlessWindowMask
                             ,   backing : NSBackingStoreType.Buffered 
                             ,     defer : false)
    func applicationDidFinishLaunching(aNotification: NSNotification) {
        // Insert code here to initialize your application
        newWindow.opaque = false
        newWindow.movableByWindowBackground = true
        newWindow.backgroundColor = NSColor.whiteColor()
        newWindow.makeKeyAndOrderFront(nil)
    }
    func applicationWillTerminate(aNotification: NSNotification) {
        // Insert code here to tear down your application
    }
}

但是,尝试从命令行运行它会失败:

pi@piBookAir.local ~ /Users/pi/dev/macdev:
 ⤐  swift window.swift 
window.swift:3:1: error: 'NSApplicationMain' attribute cannot be used in a 
                         module that contains top-level code
@NSApplicationMain
^
window.swift:1:1: note: top-level code defined in this source file
import Cocoa
^
 ✘

消除错误的正确方法是什么?

【问题讨论】:

    标签: swift command-line nswindow


    【解决方案1】:

    this 代码从 Objective-c 移植到 Swift,你会得到

    import Cocoa
    
    let nsapp = NSApplication.shared()
    NSApp.setActivationPolicy(NSApplicationActivationPolicy.regular)
    let menubar = NSMenu()
    let appMenuItem = NSMenuItem()
    menubar.addItem(appMenuItem)
    NSApp.mainMenu = menubar
    let appMenu = NSMenu()
    let appName = ProcessInfo.processInfo.processName
    let quitTitle = "Quit " + appName
    let quitMenuItem = NSMenuItem.init(title:quitTitle,
      action:#selector(NSApplication.terminate),keyEquivalent:"q")
    appMenu.addItem(quitMenuItem);
    appMenuItem.submenu = appMenu;
    let window = NSWindow.init(contentRect:NSMakeRect(0, 0, 200, 200),
        styleMask:NSTitledWindowMask,backing:NSBackingStoreType.buffered,defer:false)
    window.cascadeTopLeft(from:NSMakePoint(20,20))
    window.title = appName;
    window.makeKeyAndOrderFront(nil)
    NSApp.activate(ignoringOtherApps:true)
    NSApp.run()
    

    另存为minimal.swift,编译

    swiftc minimal.swift -o minimal
    

    然后运行

    ./minimal
    

    您将获得一个空窗口和一个菜单栏,其中包含一个名为应用程序的菜单和一个退出按钮。

    为什么它确实有效,我不知道。我是 Swift 和 Cocoa 编程的新手,但链接的网站解释了一下。

    【讨论】:

    • 我正在运行 swift 5,我不得不更改一些东西,但它可以工作。 NSApplication.shared() 变为 NSApplication.shared,NSApplicationActivationPolicy 变为 NSApplication.ActivationPolicy,NSTitledWindowMask 变为 .titled。
    • 我还发现菜单栏无法点击,正在寻找解决方案。
    【解决方案2】:

    制作一个文件TestView.swift(像这样):

        import AppKit
    
        class TestView: NSView
        {
                override init(frame: NSRect)
                {
                        super.init(frame: frame)
                }
    
                required init?(coder: NSCoder)
                {
                        fatalError("init(coder:) has not been implemented")
                }
    
                var colorgreen = NSColor.greenColor()
    
                override func drawRect(rect: NSRect)
                {
                        colorgreen.setFill()
                        NSRectFill(self.bounds)
    
                        let h = rect.height
                        let w = rect.width
                        let color:NSColor = NSColor.yellowColor()
    
                        let drect = NSRect(x: (w * 0.25),y: (h * 0.25),width: (w * 0.5),height: (h * 0.5))
                        let bpath:NSBezierPath = NSBezierPath(rect: drect)
    
                        color.set()
                        bpath.stroke()
    
                        NSLog("drawRect has updated the view")
                }
        }
    

    制作一个文件TestApplicationController.swift(像这样):

        import AppKit
    
        final class TestApplicationController: NSObject, NSApplicationDelegate
        {
    
                ///     Seems fine to create AppKit UI classes before `NSApplication` object
                ///     to be created starting OSX 10.10. (it was an error in OSX 10.9)
                let     window1 =       NSWindow()
                let     view1   =       TestView(frame: NSRect(x: 0, y: 0, width: 1000, height: 1000))
    
                func applicationDidFinishLaunching(aNotification: NSNotification)
                {
                        window1.setFrame(CGRect(x: 0, y: 0, width: 1000, height: 1000), display: true)
                        window1.contentView =   view1
                        window1.opaque      =   false
                        window1.center();
                        window1.makeKeyAndOrderFront(self)
                //      window1.backgroundColor = view1.colorgreen
                //      window1.displayIfNeeded()
                }
    
                func applicationWillTerminate(aNotification: NSNotification) {
                        // Insert code here to tear down your application
                }
        }
    

    制作一个文件 main.swift(像这样):

        //
        //  main.swift
        //  CollectionView
        //
        //  Created by Hoon H. on 2015/01/18.
        //  Copyright (c) 2015 Eonil. All rights reserved.
        //
    
        import AppKit
    
        let     app1            =       NSApplication.sharedApplication()
        let     con1            =       TestApplicationController()
    
        app1.delegate   =       con1
        app1.run()
    

    最后一个文件不能重命名,main.swift 显然是 swift 的特殊名称(否则示例将无法编译)。

    现在,输入这个(编译示例):

    swiftc -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk TestView.swift TestApplicationController.swift main.swift
    

    通过输入运行代码:

    ./main
    

    它显示了一个主窗口(居中),里面有一个漂亮的绿色和一个黄色矩形)。 您可以通过输入 Control-C 来终止该应用程序。

    请注意,这是一个快速编译而不是解释器运行,所以你有一个原生应用程序。

    注意 -sdk 和 MacOSX10.11.sdk 的路径是必不可少的(否则代码将无法编译)。

    另请注意,此编译依赖于最新的 Xcode 发行版,因此请将 MacOSX10.11.sdk 更新为 MacOSX10.10.sdk 或 SDKs 目录中的任何内容。

    花了一段时间才发现...

    【讨论】:

    • 详细信息:编译指令在 SDK 路径的末尾缺少“/MacOSX10.11.sdk”。有了它就可以了!
    【解决方案3】:

    -parse-as-library 标志传递给swiftc

    swiftc -parse-as-library window.swift
    

    也就是说,当你这样做时,你最终会遇到第二个错误:

    2015-06-10 14:17:47.093 window[11700:10854517] 应用程序包中没有 Info.plist 文件或 Info.plist 文件中没有 NSPrincipalClass,退出

    【讨论】:

    • 如果你看到他的代码范围,class AppDelegate: NSObject, NSApplicationDelegate 他根本不需要将它作为库传递:-/ 如果有任何需要解析为 -primary-file 如果这是唯一的文件,这是极不可能的。
    • @AdrianSluyters 这个标志的名字很奇怪,但它是 Xcode 在编译 Swift 应用程序时使用的标志,在我的快速测试中,它完全符合要求,即它发出了一个可执行文件让@NSApplicationMain 工作。
    • 我猜你需要一个 RADAR 文件然后苹果一个,这真的很奇怪
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-22
    • 2012-09-11
    相关资源
    最近更新 更多