[翻]OS X和Swift入门指南:(1/3)

译注:相对于大多数ios开发者,我是从mac开发转到ios开发的,从最初的xcode 3版本开始做开发。对于os x开发,和ios开发最直接的区别就是中文资料很少,而且资料都比较陈旧,所以看到raywenderlich这一系列os x入门教程,打算翻译给开发者参考。欢迎转载,但请尊重翻译者和原著的辛苦工作,注明来源。

本片翻译自:http://www.raywenderlich.com/87002/getting-started-with-os-x-and-swift-tutorial-part-1

成为一个iOS开发者的好时机来了。不仅是因为你可以在iTunes App Store发布应用,同时你也拥有了称成为Mac开发者记得基础技能,因为Mac开发和iOS开发有很多相似之处!(译注:我认为还是有很多区别的,特别是兼容性,需要兼容多个版本Mac操作系统。)
如果你是一个iOS开发者,并且你有兴趣学习成为Mac开发者的基础教程,因此可以将你的iOS App移植到桌面,这个教程正适合你。
在这个教程中,你将会创建你的首个Mac应用程序,特指我们How To Create A Simple iPhone App教程的Mac版本的App。
如果你曾经跟随ios教程学习过,对于大多数步骤你将会非常熟悉,同时你也能看到iOS和Mac开发的主要区别。最后,你将会对OS X和Swift开发有很好的了解。
如果你没有跟随过ios教程学习,也不用担心。这个教程不需要你提前阅读理解ios教程--我们将会一步一步知道你。
在你制作App的过程中,你将会学到如下知识点:

  • 如何利用Xcode创建Mac App
  • 学习Mac app的基础结构
  • 学习OS X和iOS开发的主要区别
  • 如何使用Table View–包括添加和删除行
  • 如何使用text field,button,image view
  • 如何从本地硬盘选择图片,或者从电脑摄像头获取图片
  • 如何在app会话存储数据
  • 这个教程是针对初学的Mac开发者,但是要求你熟悉Swift语言和Xcode。对于学习这个教程有iOS开发经验是更好的,但是不是必须的。
    在系列教程的第一部分,我们将会展示如何加载你的bugs model列表,并显示到table view中。

    开始吧

    创建Mac工程和创建iOS工程非常详细–还是使用Xcode,只是不同的模板!
    因此,先选择Xcode中的File\New Project…,在弹出的窗口中,选择”OS X”session中的”Application”。然后点击下一步。
    started-with-os-x-create-project
    在接下来的界面,你将要输出应用程序信息,在product name区域中输入ScaryBugsMac,添加organization name,选择唯一的organization identifier。苹果建议采用反向域名格式。其他文本框留白。
    最后,确保语言选择Swift,checkmarks都不需要选择,document extension也留白。当你完成后,选择下一步。
    started-with-os-x-project-info
    现在Xcode将会询问你工程保存位置,选择你电脑的文件夹然后点击“Create”。

    工程现在已经就绪,现在你有了一个空窗体Mac应用程序。让我们看看它的外观。找到”Run”按钮,在Xcode工具栏左边。点击它然后Xcode开始编译App。
    当Xcode完成编译后,你将会看到你应用程序的主窗体。

    started-with-os-x-proejct-first-view

    这个窗体显示给你三件事:一,你选择了正确的模板并且正常运行;二,他是继续操作的很好起点;三,这里有些和iOS开发大(很明显)的区别:

  • 窗体没有特殊的固定的大小,例如iphone或ipad屏幕大小--窗体可以完全自定义大小。
  • Mac App可以拥有多个窗体,你可以最小化他们,拖放他们,等等任何你想做的。
  • 让我们为窗体做一些事情,让它显示一些关于bug的信息。就像在iOS中,首先需要创建新的View Controller。在这个视图中,你将会定义定义主程序的用户界面。
    为了创建新的view controller,选择File\New\File…,在弹出的窗体中,选择OS X\Source\Cocoa Class,然后点击下一步。
    osx-tutorial1-to-new-master-view-controller
    命名类为MasterViewController,键入NSViewController作为”Subclass of”,确认选中”Also create XIB file for user interface”。点击下一步。
    osx-tutorial1-create-viewcontroller-2
    在最后的弹窗中,选择Create
    现在,你新的view controller创建了,你的Project Navigator应该如下所示:
    osx-tutorial1-project-navigator
    现在,你已经创建了view controller,是时候,为它增加一些UI元素了。在Project Navigator中,选择MasterViewController.xib。这将会在Interface Builder中家在view controller的视觉展现。
    Interface Builder让你可以可视化的构建用户界面。你只需要拖拽控件到你的view中,根据应用程序需求设置位置或者修改大小。
    对于你的app,首先你需要显示一个Bugs的列表。 那么,你将需要一个table view,在OS X中,这个控件叫做NSTableView(类似于iOS中的UITableView )。
    因此,作为一个经验法则,如果你想知道一些你知道的iOS控件是否也存在于Mac,你可以尝试看看是否有NS开头的同样类。你将会惊奇地发现很多-NSScrollView, NSLabel, NSButton等等!请注意,对于这些控件API也许同iOS版本的有点不同。然后在很多情况,Apple一直努力使OS X版本更像iOS版本。
    用户接口空间在屏幕的右下角。确认选择了第三个tab(拥有UI控件的tab)并找到NSTableView控件(你也可以滚动控件列表直到你找到它,或者你可以键入NSTableView在面板的搜索区域)。
    osx-tutorial1-add-bugs-tableview
    从面板中拖拽table view到view中,并移动到左上角。不要担心table大小,你将会稍后处理它。
    osx-tutorial1-add-bugs-tableview2
    现在你有了个带表格的view,但是你还没有为主窗体添加view controller,所以它将不会显示。打开AppDelegate.swift来开始实现它。
    现在你将要为view controller添加一个属性。在Swift中,隐式命名空间和类的作用范围是应用程序的项目。所以没有必要在Application delegate中引入MasterViewController。
    添加如下代码在window属性声明下:

    var masterViewController: MasterViewController!
    

    现在Application Delegate有了MasterViewController属性,但是视图不会显示在应用程序屏幕。为了这么做,你需要实例化变量创建新的视图,然后,你需要添加新创建的视图到应用程序的主窗体上。
    Application delegate有一个applicationDidFinishLaunching的方法,操作系统在应用程序首次加载后会调用这个方法。这个就是你添加初始化代码的地方,这意味着只在应用程序启动时运行一次。
    注意:如果你熟悉iOS编程,OS X的这个方法等价于application(_:didFinishLaunchingWithOptions:)
    在applicationDidFinishLaunching方法中插入如下代码:

    masterViewController = MasterViewController(nibName: "MasterViewController", bundle: nil)
     
    window.contentView.addSubview(masterViewController.view)
    masterViewController.view.frame = (window.contentView as! NSView).bounds
    

    首先,你实例化一个新的MasterViewController,采用interface builder中的同样文件名对象。当创建后,你添加它到主窗体中,并且设置它的大小为整个窗体的大小。
    在OS X中的窗体(NSWindow类)创建时拥有默认的的view,叫做contentView,并且被自动适配铺满窗体。如果你想要添加你自己的view到窗体中,你总是需要添加他们到contentView中,使用addSubview方法。
    最后一行知识设置了你view的大小匹配窗口的初始化大小。对比iOS变成,这有一些不同。在iOS中,你需要设置窗体的rootViewController,或者在storyboard指定App加载时的初始化view controller。但是rootViewController在OS X中不存在,因此,需要添加你需要的视图到窗体的contentView中。
    现在,如果你编译运行,你将会看到主窗体上显示你包含table view的主视图。Nice-现在是你开始的地方!(now you’re starting to get somewhere!)
    oxs-tutorial-empty-tableview

    一个Scary的数据模型:组织

    到现在为止,你有了一个拥有很好外观的table view的窗体,但是它没有显示任何东西。你希望它显示一些你的可怕的bugs的信息-但是,等等,你还没有用于显示的任何数据!
    在下一步,你将要创建应用程序的数据模型,但是在这之前,你需要学习在Project Navigator组织事务的方法。

    注意:这是一个可选的章节,用来展示给你如何分组组织文件。如果你已经熟悉Project Navigator的分组。你可以跳到下一章节。
    这个是你当前Xcode的Prject Navigator组织形式:
    osx-tutorial-projectnavigator2
    默认的模版创建了以应用程序命名的分组,并且把支持文件(plist, resources,等等)作为子分组。当你的应用程序变大时,你需要处理很多的文件,查找你需要的文件将会变的越来越困难。
    在这个章节中,我们将要展示一种组织你文件的方法。这种组织方式很主观,所以你可以任意修改来适配任何组织。
    首先,你将会创建一个分组来存储你的user interface文件,你将会命名它为“GUI”。为了创建它,control-click(或者点击鼠标右键)在ScaryBugsMac分组上。
    在弹出的窗体上,选择“New Group”。被创建的分组会自动选中,然后你可以输入新的名字“GUI”。
    现在,拖拽user interface文件到这个分组中(AppDelegate.swift, MasterViewController.swift/.xib 和 MainMenu.xib)。在拖拽完成后,你的Project Navigator将会如下所示:
    osx-tutorial-project-navigator3-GUI
    现在在ScaryBugsMac中创建第二个分组,命名为“Model”。在下一步,我们将要创建应用程序的data model文件,你将会这些文件到这个分组中。
    这个是Project Navigator在添加后的样式。
    osx-tutorial-projectnavigator4-Model
    这个是数组模型将要组织的形式:

  • ScaryBugData :包含bug名称和分数。
  • ScaryBugDoc :包含全尺寸的图片和缩略图,ScaryBugData 。
  • 你采用如上设置的原因是,这使教程后续的保存数据到磁盘更容易。

    一个Scary的数据模型:实现

    注意,如果你曾经学习过How To Create A Simple iPhone App on iOS 5 Tutorial,你将会发现这个章节(几乎)和那个相同。对于Mac/iOS编程来说,一个好处是他们共享大多数SDK,显然,除了UI类和一些操作系统特别的部分。
    因此,当你创建模型和类时,你不需要user interface,你将会发现,你的大多数代码在Mac上也能工作,或者,一些很小的的改变就能使它工作。
    例如,在这种情况下,改变ScaryBug的模型类从iOS到Mac只需要做一个修改。UIImage在OS X中不存在,说一你只需要改变为OS X的图片类,NSImage。这就是全部(随着更新语言到Swift)!

    首先需要创建model类,命名为ScaryBugData。
    在Project Navigator中,Control-Click你刚创建的Model分组,在菜单中选择“New File…”。选择OS X\Source\Cocoa Class模版。点击下一步。
    osx-tutorial-new-cocoa-class
    命名类为ScaryBugData,键入NSObject作为“Subclass of”,点击下一步。
    在最后的弹出窗体中,选择Create。如果一切正常,你的Project Navigator现在将会如下所示:
    osx-tutorial-create-scarybugdata
    打开ScaryBugData.swift,替换它的内容如下:

    import Foundation
     
    class ScaryBugData: NSObject {
      var title: String
      var rating: Double
     
      override init() {
        self.title = String()
        self.rating = 0.0
      }
     
      init(title: String, rating: Double) {
        self.title = title
        self.rating = rating
      }
    }
    

    这是很简单的东西-你定义了一个对象包含两个属性-一个bug名字的字符串,一个你投票bug多吓人的双精度浮点数。在Swift中,String和Double类型都是结构体,结构体是值类型,所以显示的内存管理(例如,strong)是不需要的。
    你也定义了类的初始化,因此,你可以在创建bug时设置标题和投票值。同样,这里非常简单的东西。你创建了包含参数的初始化来填出实例变量。注意,这里不需要deinit,因为ScaryBugData的属性是值类型,在auto-synthesis中没有不需要synthesize你的属性。

    好了,这就是ScaryBugData。现在,参照你上面做的步骤,创建另一个NSObject的子类,这次命名为ScaryBugDoc
    用如下代码替换ScaryBugDoc.swift:

    import Foundation
    import AppKit
     
    class ScaryBugDoc: NSObject {
      var data: ScaryBugData
      var thumbImage: NSImage?
      var fullImage: NSImage?
     
      override init() {
        self.data = ScaryBugData()
      }
     
      init(title: String, rating: Double, thumbImage: NSImage?, fullImage: NSImage?) {
        self.data = ScaryBugData(title: title, rating: rating)
        self.thumbImage = thumbImage
        self.fullImage = fullImage
      }
    }
    

    这里,你创建了一些属性和两个初始化。这里需要注意的是thumbImagefullImage是可选的NSImage属性,所以不同于一般的变量,它们不需要初始值,或者在默认的初始化中赋值。
    这就是全部-你的数据模型完成了!在此时,你应该变异并运行你的应用程序来检查是否一切正常。你会看到同样的空列表,应为,你还没有连接数据模型到UI。
    现在,你有了数据模型,但是你还没有任何数据。你需要创建一系列的ScaryBugDoc对象,并且你将用array存储它们。
    打开MasterViewController.swift为类添加如下属性:

    var bugs = [ScaryBugDoc]()
    

    我们将用它来保存跟踪你的bugs列表。现在用一些初始化数据填充它。

    Scary bug图片和样本数据

    在此时,MasterViewController已经准备好了接收Bugs列表。但话又说回来,你没有任何数据。
    在添加数据之前,你需要一些scary图片!你可以下载这些图片How To Create A Simple iPhone App on iOS 5 Tutorial,或者从网络查找你自己喜欢的scary bugs:]

    一旦你下载了这些文件,或者采用自己的文件,打开Images.xcassets,并且从Finder拖拽所有的bug图片到右边的图片列表中,在预存的App图标下:
    osx-tutorial-projectnavigator6-imagesxcassets
    现在让我们来创建样本数据。打开MasterViewController.swift,添加如下方法到类中:

    func setupSampleBugs() {
      let bug1 = ScaryBugDoc(title: "Potato Bug", rating: 4.0,
        thumbImage:NSImage(named: "potatoBugThumb"), fullImage: NSImage(named: "potatoBug"))
      let bug2 = ScaryBugDoc(title: "House Centipede", rating: 3.0,
        thumbImage:NSImage(named: "centipedeThumb"), fullImage: NSImage(named: "centipede"))
      let bug3 = ScaryBugDoc(title: "Wolf Spider", rating: 5.0,
        thumbImage:NSImage(named: "wolfSpiderThumb"), fullImage: NSImage(named: "wolfSpider"))
      let bug4 = ScaryBugDoc(title: "Lady Bug", rating: 1.0,
        thumbImage:NSImage(named: "ladybugThumb"), fullImage: NSImage(named: "ladybug"))
     
      bugs = [bug1, bug2, bug3, bug4]
    }
    

    这里,你使用了ScaryBugDoc初始化,来创建样本数据,对每个对象传入标题,评分,和图片。然后将他们添加到bugs数组。
    为了创建样本数据,打开AppDelegate.swift找到applicationDidFinishLaunching方法,添加如下代码在addSubview之前:

    masterViewController.setupSampleBugs()
    

    最后,你有了一些数据!编译运行你的app,确保一切正常工作没有错误。
    你仍然在用户界面上看不到任何数据,但是,在此处view controller已经包含了他需要的数据,并且你现在可以开始user interface的工作了,来显示最终的Scary Bugs列表。

    不同的Bug列表

    为了显示你的bug列表,你需要设置你的table view来从你的模型获取列表。
    在OS X中,table view空间叫做NSTableView,,它类似于UITableView,都是用来显示列表数据的,但是一个主要的区别是NSTableView一行可以包含多列。
    NSTableView每行都有cells。然后,对于如何工作,这里有些最近的修改:

  • 在OS X 10.7 Lion之前,表格的cell是NSCell类的衍生类。它们不是基于试图,所以绘制甚至鼠标事件都是是开发者的责任。 (gah!)
  • 在OS X 10.7 Lion及以后,table view有了新的类型:基于视图的table view。这种table view和UITableView工作原理很像。cell是view的特定对象,它们的使用方式和iOS非常相似–这种方式更简单。
  • 在这个教程中,你将会使用基于视图的Table View。我们将会包含基础知识,但是,如果你想要了解更多关于NSTableView,你可以阅读Table View Programming Guide,里面有优秀的指南关于如何table view工作的。
    打开MasterViewController.xib并且选择你的table view。要知道table view嵌入在一个clip view中,clip view嵌入在scoll view中,所以,第一次你点击它,你会最终选择scroll view。第二次点击会选中clip view,第三次点击才会选择实际的table view。
    另一种选择table view的方式是打开Interface Builder左侧的“Objects”面板,通过点击“Table View”对象来选择(你首先需要展开”clip view”对象)。
    一但你选择了table view,你首先需要确认table view是”View Based”,因为以前版本的Interface Builder默认创建”Cell Based”形式的table view。Xcode 6和以后的版本将会默认设置table view为View Based。
    为了检查,请确认选择了屏幕右侧的“Attributes Inspector”标签。然后,在“Content Mode”中,确保“View Based”被选中;如果没有,则选择它。同时,你的列表不需要多行,修改行属性为1行。
    为了更好地自定义列表,选中 “Alternating Rows”属性,这将会采用交替的白/灰颜色来绘制行,并且不选择 “Headers” 属性。这个将会去除表格的头,因为在这个教程中你不需要它。
    在移除额外的行以后,剩下的行也许比table view窄。为了修改它的大小,选择table column(使用左侧的Objects面板,或者对table view点击四次)然后修改大小为table的大小。
    osx-tutorial-tableview-properties

    下一步是配置table view将会使用的cell view。你的列表需要显示Bug的图片和它的名字。你需要一个image view和text field的cell来显示这些信息。Interface Builder已经有了预置的NSTableCellView里面包含了一个image view和一个text field,因此,你将会使用NSTableCellView。
    在窗口右下角的Object library面板中,定位到“Image & Text Table Cell View”,拖拽它到你的table view中。
    osx-tutorial-add-tableviewcell
    完成后,你的表格现在有了两种类型的cell。移除以前的cell类型(那个左侧没有齿轮图标的cell),通过选中它并且按键盘的Delete键。
    最后一件事是改变cell的高度,因为,现在对于现实bug图片太小了。你需要设置高度为32.通过点击选中cell,然后打开Xcode窗口工具面板(Utilities panel)右侧“Size Inspector”。你可以在Height panel中改变cell的高度为32.另一种改变它的方式,拖拽cell的底层边框直到你得到期望的高度。
    在改变了cell的高度之后,image和text fields有点不对齐。为了修正,选中他们,并且移动他们直到他们在cell中居中。同时,设置text field的宽度,因此它可以占用cell的宽度。你也可以修改image view,同时也可以根据需求设置text filed的宽度。
    现在,table view应该如下所示:
    osx-tutorial-tableview-properties
    现在你需要设置列的标识。这是你给每列设置的名字,因此,当你想要对column执行一些操作或者接收一些通知时,你可以标识这个列。
    在这个教程中,这个不是必须的,因为你只有一个列,但是,标识列是很好的习惯,如此当你想在其他工程中要创建多列table view时不会发生任何问题。
    为了这么做,选择table column(记得吧,或者选择table view四次来选择它,或者使用左侧的Objects panel),然后打开Utilities面板的“Identity Inspector”页签,
    在那里,修改标识“Automatic”为“BugColumn”。
    osx-tutorial-bugcolumn
    这些就是对table的UI配置。现在,你需要连接table view和view controller,用来使他们了解对方的存在。
    像iOS,table view有两个属性,用来使table和controller交互:数据源和代理。基础来说,数据源是告诉table view它需要现实的数据的类,代理是控制数据如何显示,同时也会接收table view通知的对象,例如,当cell被选中时。
    通常(但不总是)代理和数据源是同一个对象。在这个工程中,代理和对象会是MasterViewController。连接table view的数据源和代理可以通过编程实现,但是对于这个教程,你将会通过Interface Builder连接。
    选择你的table view,在Utilities面板中,选择 “Connections Inspector” (有一个向有箭头那个)。那里,在 “Outlets”节中,你将会看到数据源和代理。
    为了连接代理,选择代理右的圆圈,拖拽它到“File’s Owner”(就是MasterViewController),“File’s Owner”在左侧的 “PlaceHolders”面板中。
    osx-tutorial-connect-delegate
    这么做是为了告诉table view它的代理是MasterViewController。当你实例化你app的view controller时,这个连接会为你自动建立。
    现在,对于数据源(dataSource )重复同样的步骤。在完成后,你将会看到它们都连接到 File’s Owner,如下所示:
    osx-tutorial-connect-datasource
    这就是全部,现在你已经准备好了在view controller中添加一些必要的代码来显示列表了。打开MasterViewController.swift,复制如下代码在文件的结尾:

    // MARK: - NSTableViewDataSource
    extension MasterViewController: NSTableViewDataSource {
      func numberOfRowsInTableView(aTableView: NSTableView) -> Int {
        return self.bugs.count
      }
     
      func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
        // 1
        var cellView: NSTableCellView = tableView.makeViewWithIdentifier(tableColumn!.identifier, owner: self) as! NSTableCellView
     
        // 2
        if tableColumn!.identifier == "BugColumn" {
          // 3
          let bugDoc = self.bugs[row]
          cellView.imageView!.image = bugDoc.thumbImage
          cellView.textField!.stringValue = bugDoc.data.title
          return cellView
        }
     
        return cellView
      }
    }
     
    // MARK: - NSTableViewDelegate
    extension MasterViewController: NSTableViewDelegate {
    }
    

    这里,你为MasterViewController添加了两个扩展,用来定义符合NSTableViewDelegateNSTableViewDataSource的协议。为了在table view中显示数据,你只要需要实现两个数据源方法。
    第一个方法是numberOfRowsInTableView,被操作系统用来询问数据源,”你有多少行数据需要显示?“,你只需要给出在你数组中bug列表数据的数量。
    通过这个方法,table view知道了有多少行需要显示,但是仍然不知道任何关于每行cell的事情,和每行需要的信息。
    这个由tableView(_:viewForTableColumn:row:)完成。对于table view中的每行每列,操作系统都会调用这个方法,这里你已经创建了适当的cell,并且根据你的需要填充了信息。
    这里是方法的步骤分解:
    1、首先,你需要通过调用makeViewWithIdentifier得到cellView。这个方法对于列会创建(或复用)适当的cell,基于你在Interface Builder中的标识。
    2、一但你拥有了cell,是时候填充它了。你需要根据不同的列来设置它。这就是你为什么检查 “BugColumn”标识。如果标识是 “BugColumn”,然后你通过ScaryBugDoc设置image和text。在这种情况下,表格只有一种列类型,所以这个检查不是一定必须的。然后,这很好的展示了如何处理多列情况,也许你需要在你自己的app中需要。
    3、最后一步是设置cell的信息。基于当前行,你通过你的bugs数组,ScaryBugDoc的属性,然后填充缩略图的图片并且通过bug的名字(”标题“)填充文本框。
    这就是你需要在table view中显示数据的全部步骤。这仅仅通过定义属性连接Interface Builder,并且在你的view controller中仅仅实现两个方法。
    现在是时候编译运行应用程序了,如果一切正常,你将会看到在table view中显示全部的 Scary Bugs。
    osx-tutorial-part1app

    下一步到哪里去

    这里是当前的工程,包含了你在教程中的全部代码。
    这个系列的下一个节,你将会学习到,如果添加detail view,并且如何从列表中添加/编辑/删除bugs。你同样会学习如何美化界面,如何处理在窗口大小改变来使你的app在任何大小都很好显示。

    • bupo

      感谢lz,我做iOS 开发有3年了,想学一下Mac OS应用开发,发现和iOS还是区别挺大的。特别是UI交互的不同,有的时候想实现一种UI不知道用哪个控件来实现是比较好的方式,挺困惑的。