原文:http://www.raywenderlich.com/87002/getting-started-with-os-x-and-swift-tutorial-part-1
翻译原文:http://blog.csdn.net/kmyhy/article/details/45150649
打开Xcode,使用File\NewProject…菜单,在弹出窗口中选择 “OS X/Application”,然后Next。
在接下来的窗口中,配置App信息。在product name栏中输入ScaryBugsMac,输入你的机构名以及机构ID。剩余字段保留为空白。
选择Swift作为开发语言,保持所有选项框反选,document extension栏保留为空白。然后点Next。
然后Xcode会要求你选择项目保存路径。选择一个物理路径,然后点击Create。
项目就创建完了,这是一个单窗口App。点击工具栏左上角的Run按钮,运行这个程序,效果如下图所示。
首先我们来总结一下。我们使用Xcode模板创建了一个Mac App项目,然后编译运行了这个空白项目。与iOS开发的最大不同在于:
·窗口不需要特别指明大小,比如iPhone或iPad屏幕大小——MacApp的窗口是可以通过拖动来改变大小的。
·Map App可以拥有多个窗口,窗口支持最小化,重排等操作。
然后我们来新建一个View Controller,并在它上面放入App的主界面。使用
File\New\File…菜单,在弹出窗口中,选择OS X\Source\Cocoa Class,然后点Next。
类名填入 MasterViewController,“Subclass of”填入NSViewController。确保“Also create XIB file for user interface” 为勾选,然后点Next。
在最后一个弹出窗口中,点击Create。新的View Controller将显示在项目导航窗口中:
打开MasterViewController.xib。需要注意的是,在Mac App中,有大量的类和iOS中都类似,只不过是以NS前缀命名。例如NSScrollView、NSLabel、NSButton等。
在右下角的UI Controls面板(位于第三个Tab)中,选中NSTableView将它拖到MasterViewController.xib的画布中。
不要担心Table View的大小,我们待会会来处理它。
AppDelegate.swif。在window属性下面插入如下语句:
varmasterViewController:MasterViewController! |
找到applicationDidFinishLaunching方法,这个方法在App启动时调用。
注意:这个方法等同于iOS中的application(_:didFinishLaunchingWithOptions:)方法。
在applicationDidFinishLaunching方法内,加入以下语句:
masterViewController=MasterViewController(nibName:"MasterViewController",bundle:nil) window.contentView.addSubview(masterViewController.view) masterViewController.view.frame=(window.contentViewasNSView).bounds |
在 OS X中,窗口(NSWindow对象)总是有一个默认的View,即contentView。它自动占据整个窗口的大小。当我们想在窗口中使用自己的视图时,需要用addSubview方法将它添加到contentView的subviews中。
在iOS开发中,我们可以设置将一个View Controller直接设置为窗口的rootViewController属性,但在OS X中你只能将视图添加到contentView的subviews,因为OS X中没有rootViewController的概念。
运行App,你将看到如下画面:
数据模型
接下来创建数据模型。
首先我们来熟悉一下Xcode项目文件的组织结构:
默认模板会创建一个以项目名称为名的文件夹。在这个文件夹下有一个supporting files的子文件夹,其中存放plist和资源文件。当项目很大时,会创建大量的文件,查找文件就会变得很困难。因此我们需要有一个良好的项目文件组织形式。
首先,我们新建一个文件夹(group),命名为GUI。在ScaryBugsMac文件夹上点击右键,将弹出一个快捷菜单,选择NewGroup,然后输入GUI。
然后将所有跟UI有关的文件拖到这个文件夹(AppDelegate.swift,MasterViewController.swift/.xibandMainMenu.xib),如下图所示:
然后新建另一个文件夹Model。
在Model文件夹中将包含如下内容
·ScaryBugData: 包含两个属性:昆虫的名称及昆虫的估价。
ScaryBugDoc: 包含3个属性:昆虫图片、昆虫缩略图及一个ScaryBugData属性。
实现模型对象
:如果你阅读过How ToCreate A Simple iPhone App on iOS 5 Tutorial,你会发现接下来的内容和那篇教程中的相应内容几乎一模一样。这是因为Mac和iOS编程大部分SDK都是系统的,除了UI和操作系统相关的API。而模型对象不涉及UI,因此模型对象的代码基本是一致的。
对于ScaryBug的模型类,将Mac版本与iOS版本只有一个地方不同,即将UIImage类修改为NSImage即可。当然,你也需要将它从O-C实现修改为Swift实现。
在Model文件夹上点击右键,点击 “New File…”,然后选择OS X\Source\Cocoa Class 模板,然后点击Next。
类名输入ScaryBugData,Subclass of 输入NSObject,点击 Next。
在最后一个弹出界面中,点击Create。项目导航窗口将显示如下:
ScaryBugData.swift替换为如下内容:
然后创建另一个模型对象ScaryBugDoc。
ScaryBugDoc.swift编辑为如下内容:
这个数组属性用于存储昆虫列表,接下来我们将会用一些数据填充这个数组。
填充数据及图片
MasterViewController需要用一系列昆虫来填充。你可以从此处下载所需的
昆虫图片。
下载完图片之后,,将所有图片从Finder中拖到Images.xcassets中如下图右边AppIcon之下的位置:
MasterViewController.swift添加如下方法:
masterViewController.setupSampleBugs() |
编译运行程序,确保编译通过。
显示昆虫列表
在 OS X中,Table View使用NSTableView类,它等同于iOS的UITableView类,但有一个最大的不同是:NSTableView的每一行有多个列或多个单元格。
在OS X 10.7Lion之前,table view cell继承于NSCell类。而后者并非NSView类,因此开发者需要自己处理绘图和鼠标事件。
从OS X 10.7开始,table view从 NSView继承。这样就和UITableView差不多了。cell也有相应的View类型,因此也和iOS中的类似——这样我们就轻松得多了!
在本教程中,使用的是基于View的TableView。如果你想了解NSTableView的用法,你可以阅读这里,它对 table views 的用法进行了详细的说明。
MasterViewController.xib,选中table view。注意Table View位于Scroll View中的Clip View中,因此第一个点击你选中的会是ScrollView,第二次点击你选中的才是ClipView,第三次点击才会选中Table View。
当然,你也可以直接从IB的Objects面板中选择Table View对象(展开 Clip View对象)。
选中Table View之后,在属性面板中,确认Content Mode一项是设置为View Based而不是Cell Based。同时,因为我们的列表仅显示单列,所以将Columns属性修改为1。
勾选 “Alternating Rows”属性,让表格以“明暗颜色交替”的方式绘制单元格。
反选 “Headers” 属性,因为我们不需要在表格上方显示一个标题。
接下来我们修改单元格的大小。选择Table View上的列,拖动它的大小使其占据整个表格宽度。
然后是单元格的配置。我们需要在单元格中显示昆虫的图片和名称,因此需要在Cell中添加一个Image和一个文本控件。
IB中有一种带Image View和Text Field的NSTableCellView对象,我们可以使用它。
在Object library 面板中,找到 “Image & Text Table Cell View”,将它拖到Table View中。
在Table View中,将原来的cell删除(用delete键)。
选中Table View Cell,在Size面板中,将高度调整为32。
然后选中Image View和 Text Field,使它们位于单元格中心,并调整ImageView和Text Field的大小,使它们看起来如下图所示:
接下来要为每一列设置一个id。当然对于本教程来说,我们只有一个列,因此列id可能不是必须的。
在Objects面板中选择表格列,打开Identity面板,将Identifier设置为BugColumn。
如同在iOS中一样,Table View也有Data Source和Delegate属性。正常情况下,这两个属性都是同一个对象,即MasterViewController。
选择Table View,打开Connections面板,在Outlets一项下找到delegate和data source。
点击delegate右边的小圆圈,拖到Objects面板中的“File’s Owner”上。
这将吧Table View 的delegate 属性设置为MasterViewController。重复同样的动作,设置Data Source属性。
最终如下图所示:MasterViewController.swift将下列代码放在文件最后:
// MARK: - NSTableViewDataSource extension MasterViewController:NSTableViewDataSourcefuncnumberOfRowsInTableView(aTableViewNSTableView!)->Intreturnself.bugs.count functableView(tableView!,viewForTableColumn tableColumnNSTableColumn->!{ // 1 varcellView:NSTableCellView=tableView.makeViewWithIdentifier(tableColumn.identifier,ownerself)asNSTableCellView // 2 iftableColumn.identifier=="BugColumn"// 3 letbugDocself.bugs[row] cellView.imageView!.image=bugDoc.thumbImage cellView.textField!.stringValue=bugDoc.data.title returncellView } } // MARK: - NSTableViewDelegate extension MasterViewController:NSTableViewDelegate{ 我们通过扩展让MasterViewController采用NSTableViewDelegate和NSTableViewDataSource协议。 要让列表渲染数据至少需要实现两个数据源方法。 首先是numberOfRowsInTableView方法,OS通过这个方法获取要渲染的表格行数。 其次是tableView(_:viewForTableColumn:row:)方法。OS通过这个方法知道如何去渲染每行中的每个单元格。在这个方法中,我们需要用数据对单元格进行填充。 运行程序,如果一切正常,我们将在表格中看到昆虫列表。 下载资源 为了完成本教程,你可能需要下载这些压缩包,并解压缩。 :为了将昆虫分成 “一点也不可怕” 到 “极度恐怖”几个级别,你还需要用到一个开源的分级组件EDStarRating,这也被包含在压缩包中。 在本教程中,我们不会解释如何实现这个组件,而只是演示如何在项目中使用它。压缩包中还包括了一个NSImage类别,可以从一张大图片生成缩略图。 此外,还包括3张怪脸图片,分别用于显示昆虫的不同级别。 关于 EDStarRating组件,你可以参考它的github主页. 首先,在项目导航窗口中创建一个名为Art的文件夹,并将3个怪脸图片拖到这个文件夹中——确保“Copy items if needed” 已勾选,以及Add to targets中的“ScaryBugsMac” 已选上。 再创建一个名为“Views” 的文件夹,将EDStarRating.h和EDStarRating.m拖到该文件夹。 再次确保“Copy items if needed” 已勾选以及Add to targets中的“ScaryBugsMac” 已选上。 点击Finish. 在下一窗口当被问到 “Would you like to configure an Objective-C bridgingheader?” 时选择Yes。这将创建一个Objective-C 类到Swift 代码的桥接头文件。 对于NSImage+Extras.h和NSImage+Extras.m,重复上述步骤,只不过这次将它们拖进的是“Helpers”文件夹。 ScaryBugsMac-Bridging-Header.h加入以下import语句:
以下为最终效果,其中桥接头文件已经被我们移到 Supporting Files 文件夹中:
创建详情页面 在iOS中,典型的“主-细页面App”需要创建两个视图,但在 OS X,由于屏幕不再受到限制,我们可以将它们合并在同一个视图中。 MasterViewController.xib,选中view,将宽度和高度拖大。如图: 昆虫名用NSTextField控件显示,惊悚指数用EDStarRating控件显示,昆虫图片则用NSImageView显示。 此外,我们还需要两个Label,用于表示每个字段的意义(标题)。 拖一个 Text Field (昆虫名),2个Labels (字段标题),一个Image View 到view中。 EDStarRating控件是一个定制控件,无法在Objects Library中找到它,因此你需要先拖入一个 “Custom View”控件。 将这些控件放到view的右边,从上到下依次摆放: ·首先是一个Label,用于充当昆虫名的字段标题,在它下边是 textfield。 ·在text field下面是第二个 label(惊悚指数的字段标题)。 ·在这个label,下边是一个customview (后面将改成EDStarRating控件)。 ·最下面是image view below 控件。 所有控件左对齐,如下图所示: 然后选中custom view 控件,打开Identity面板(第三个标签按钮)将Class 修改为EDStarRating。 选择第一个label,打开Attributes 面板(第4个标签按钮),修改Title 为 “名称”. 依照上面的方法,将第二个label的title 改为“Rating”。 选择最顶级的 view (在document outline面板中显示为“Custom View”) ,打开Size 面板,查看它的大小: MainMenu.xib,选择 ScaryBugsMac window,设置window 的宽高为前面记住的宽高。然后勾选MinimumSize。 运行后效果如下: EDStarRating控件并没有在界面上显示,这是因为我们还没有配置它。 MasterViewController.xib,打开Assistant Editor (工具栏中“Editor” 面板的第二个按钮),并确保当前编辑的内容是MasterViewController.swift。 选中table View,按下右键,拖一条线到MasterViewController.swift文件中: 这将弹出一个窗口,允许你创建一个IBOutlet。在Name中输入bugsTableView,Storage 设置为 Weak,然后点击Connect。 重复上述步骤,为text field和image view创建两个IBOutlet: bugTitleView、bugImageView。 对于custom view,则创建一个IBOutlet:bugRating. 最终,MasterViewController.swift文件中将新增如下内容:
显示昆虫详情 MasterViewController.swift增加如下方法: 然后是这个方法: 实现deleteBug()方法如下: 编辑 MasterViewController.xib,打开 Assistant Editor,确保当前显示的文件是 选中text field,右键拖到MasterViewController.swift文件中的addBug()方法之前: 这将允许你为Text Field创建一个IBAction,Name 请使用bugTitleDidEndEdit。 这个方法将在text field结束编辑时调用(当用户按下回车键或者离开Text Field控件)。 回到MasterViewController.swift,添加方法: 这个 image picker属于 Quartz 框架。
运行程序,添加、删除或修改任意数据。然后点击Reset按钮,所有数据又恢复原样。
缩放MasterViewController.xib,在Size面板中查看 Custome View的大小。在本例中,它应该是540x400大小。但是读者的这个数字会有不同。不管是多大,请记下这个数字。待会会用到。 这将是App出口的最小大小。打开MainMenu.xib,选择 window 对象。在Size 面板中,勾上Constraint右边的Minimum Size 选项,然后将width 和 height 修改为同样的值。 运行程序。 改变出口的大小,这次当窗口缩小到最小尺寸后,就无法再缩小。 接下来我们需要解决控件自适应大小的问题,包括TableView和细节页面中的控件。 首先是MasterViewController视图。 AppDelegate.swift,在applicationDidFinishLaunching()方法最后加入:
在这里,我们允许MasterViewController在宽、高两个维度上使用自动布局。 接下来,我们使用IB的自动布局来约束来对table view进行布局,以便在窗口大小改变时,让它的高度自动增长,但宽度保持恒定。 MasterViewController.xib,选择table view,点击右下角的Pin 按钮, 勾上上、左、下3个约束,以及一个等宽约束,然后点击 “Add 4 Constraints”: 注意图片中的约束值可能和读者的实际值有所不同。 然后选择Reset按钮,设置其与Table View的上边距约束和一个相对于Main View的左边距约束: 接着选择分隔线,设置其与Main View的上、下边距约束,以及与TableView的左边距约束,确认在左边距约束的下拉列表中选择了 “Bordered ScrollView – Table View” : 接下来设置“Add” 和 “Delete” 按钮。我们不需要改变它们的大小,唯一需要改变的是它们和Table View之间的距离。对于两个按钮,我们需要设置它们的左、上,宽度和高度约束。已Add按钮为例,显示如下图: Delete按钮类似。 运行程序,改变窗口大小,查看效果。 然后是右边的细节页面。在这个页面中,当窗口宽度变大时,所有控件的宽度也会变大。以TableView相同的方法,分别设置它们的自动布局如下: ·设置Name标签的左、上约束。 ·设置 ·设置Rating标签的左、上约束。 bugRating的左、上、右和高约束。 bugImageView的左、上、下、右约束。 ·移动 Change Picture 按钮,以便它的右边沿刚好和 设置完 上述步骤做完后,故事板将如下图所示: 编译运行,再次缩放窗口。 bugImageView的Scale设置会对图片产生不同的显示效果。在IB中选择Image Well 控件,修改其scaling属性为“Proportionally Up or Down” 或者 “Axes Independently”,然后运行App,看看有什么不同。 注意: 如果你想限制窗口的最大缩放尺寸,则你也可以用设置窗口最小缩放尺寸同样的方式加以限制。 关注细节关于用户体验方面,我们仍然有一些细节值得注意。例如:运行App,不要选择任何昆虫,点击“Delete” 或者 “Change Picture” 按钮,什么都不会发生,Why? 作为程序员,你当然知道当用户什么都没选择的情况下,不应当执行任何操作,但对于用户而言,这种情况仍然显得不太友好: 我们通过以下方式来解决这个问题: ·如果用户选中了某个单元格,我们才让Delete按钮、Change picture按钮、文本框和rating view可用。 ·如果用户未选择任何行,我们禁用上述控件,用户将不能和它们进行任何交互。 MasterViewController.xib,选择Delete按钮,在属性面板,将Enabled属性前的勾去掉。 在ChangePicture 按钮、text field上重复上述步骤。 这样,当程序刚启动时,上述控件将被禁用。然后我们需要在用户选择了表格中的单元格之后再启用它们。要实现这个目的,我们首先需要为它们创建IBOutlet。 打开AssistantEditor 确保当前编辑的文件为MasterViewController.swift。 选择“Delete” 按钮,右键将它拖动到MasterViewController.swift文件中。 在弹出的出口中,选择connection为“Outlet”,name 栏输入deleteButton,然后点击Connect. 重复上述步骤,为Changepicture按钮创建一个IBOutlet,名为changePictureButton. MasterViewController.swift,在tableViewSelectionDidChange(_:),加入以下代码,位于 我们首先判断控件是否需要被启用,这是通过用户是否选中单元格来决定的。如果selectedDoc为空,则意味着没有行被选中,这说明控件应当被禁用,否则启用控件。 此外,ratingview 默认是启用的,所以我们还需要在 loadView()中禁用它。找到这行语句:
修改为 运行程序。 :你还可以在用户未选择有效行时讲整个细节页面都隐藏起来,但这完全取决于你。 保存数据 就像iOS,Mac App也能够使用 首先我们必须让模型类实现 首先我们让ScaryBugData实现NSCoding协议中的 同时还需要一个与之对应的初始化方法。不同的是,我们无法在扩展中定义required的init方法,因此必须把它定义在类代码中: required convenience init(coder decoder: NSCoder) { self.init() self.title = decoder.decodeObjectForKey("title") as String self.rating = decoder.decodeObjectForKey("rating") as Double } init(coder:)方法和 然后在ScaryBugDoc.swift中定义一个扩展实现
|