加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

使用swift开发OSX应用

发布时间:2020-12-14 01:30:41 所属栏目:百科 来源:网络整理
导读:原文:http://www.raywenderlich.com/87002/getting-started-with-os-x-and-swift-tutorial-part-1 翻译原文:http://blog.csdn.net/kmyhy/article/details/45150649 打开Xcode,使用 FileNewProject… 菜单,在弹出窗口中选择 “OS X/Application”,然后N

原文:http://www.raywenderlich.com/87002/getting-started-with-os-x-and-swift-tutorial-part-1

翻译原文:http://blog.csdn.net/kmyhy/article/details/45150649


打开Xcode,使用FileNewProject…菜单,在弹出窗口中选择 “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的主界面。使用

FileNewFile…菜单,在弹出窗口中,选择OS XSourceCocoa 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 XSourceCocoa Class 模板,然后点击Next。


类名输入ScaryBugData,Subclass of 输入NSObject,点击 Next。

在最后一个弹出界面中,点击Create。项目导航窗口将显示如下:


ScaryBugData.swift替换为如下内容:

importFoundation

classScaryBugData:NSObject{

vartitleString

varratingDouble

overrideinit()self.title=String()

self.rating=0.0

}

(titleString,ratingDouble)=title

=rating

}

}

然后创建另一个模型对象ScaryBugDoc

ScaryBugDoc.swift编辑为如下内容:

importFoundation

importAppKit

classScaryBugDocvardata:ScaryBugData

varthumbImageNSImage?

varfullImageself.data=ScaryBugData()

Double,thumbImageNSImage?,fullImageNSImage?:title,0)">:rating)

self.thumbImage=thumbImage

self.fullImage=fullImage

注意thumbImagefullImage声明为可空的NSImage,因此他们不需要在默认构造函数中初始化。

MasterViewController.swift,增加一个属性声明:

varbugs[ScaryBugDoc]()

这个数组属性用于存储昆虫列表,接下来我们将会用一些数据填充这个数组。

填充数据及图片

MasterViewController需要用一系列昆虫来填充。你可以从此处下载所需的

昆虫图片。

下载完图片之后,,将所有图片从Finder中拖到Images.xcassets中如下图右边AppIcon之下的位置:


MasterViewController.swift添加如下方法:

funcsetupSampleBugsletbug1=ScaryBugDoc"Potato Bug",0)">:4.0,

thumbImageNSImage(named"potatoBugThumb"),26)">"potatoBug"))

letbug2"House Centipede",217)">3.0,

thumbImage"centipedeThumb""centipede"letbug3"Wolf Spider",217)">5.0,26)">"wolfSpiderThumb""wolfSpider"letbug4"Lady Bug",217)">1.0,26)">"ladybugThumb""ladybug"))

bugs[bug1,bug2,bug3,bug4]

AppDelegate.swift,找到applicationDidFinishLaunching方法,在addSubview之前加入以下代码:

masterViewController.setupSampleBugs()

编译运行程序,确保编译通过。

接下来,我们将在UI中显示这些图片和数据。

显示昆虫列表

在 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采用NSTableViewDelegateNSTableViewDataSource协议。

要让列表渲染数据至少需要实现两个数据源方法。

首先是numberOfRowsInTableView方法,OS通过这个方法获取要渲染的表格行数。

其次是tableView(_:viewForTableColumn:row:)方法。OS通过这个方法知道如何去渲染每行中的每个单元格。在这个方法中,我们需要用数据对单元格进行填充。

运行程序,如果一切正常,我们将在表格中看到昆虫列表。

下载资源

为了完成本教程,你可能需要下载这些压缩包,并解压缩。

:为了将昆虫分成 “一点也不可怕” 到 “极度恐怖”几个级别,你还需要用到一个开源的分级组件EDStarRating,这也被包含在压缩包中。

在本教程中,我们不会解释如何实现这个组件,而只是演示如何在项目中使用它。压缩包中还包括了一个NSImage类别,可以从一张大图片生成缩略图。 此外,还包括3张怪脸图片,分别用于显示昆虫的不同级别。

关于 EDStarRating组件,你可以参考它的github主页.

首先,在项目导航窗口中创建一个名为Art的文件夹,并将3个怪脸图片拖到这个文件夹中——确保“Copy items if needed” 已勾选,以及Add to targets中的“ScaryBugsMac” 已选上。

再创建一个名为“Views” 的文件夹,将EDStarRating.hEDStarRating.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.hNSImage+Extras.m,重复上述步骤,只不过这次将它们拖进的是“Helpers”文件夹。

ScaryBugsMac-Bridging-Header.h加入以下import语句:

#import "EDStarRating.h"#import "NSImage+Extras.h"

以下为最终效果,其中桥接头文件已经被我们移到 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文件中将新增如下内容:

@IBOutlet weakvarbugsTableView!

@IBOutlet weakvarbugTitleViewNSTextFieldvarbugImageViewNSImageViewvarbugRating:EDStarRating!


显示昆虫详情

MasterViewController.swift增加如下方法:

funcselectedBugDoc-> ScaryBugDoc?letselectedRowself.bugsTableView.selectedRow;

ifselectedRow >0&&selectedRow <count[selectedRownil

这个方法根据用户选中的行索引,从数据模型中检索响应的对象。

然后是这个方法:

funcupdateDetailInfo(doc:ScaryBugDoc?=""

varimage0.0

ifletscaryBugDoc=doc{

title=scaryBugDoc.data.title

image=scaryBugDoc.fullImage

rating=scaryBugDoc.data.rating

self.bugTitleView.stringValueself.bugImageView.image=image

self.bugRating.ratingFloat(rating)

这个方法根据ScaryBugDoc对象,将昆虫的信息和图片在UI上显示。然后是这个方法:

functableViewSelectionDidChange(notificationNSNotificationletselectedDoc=selectedBugDoc()

updateDetailInfo(selectedDoc 当用户改变了在表格中的选择时,这个方法调用前两个实用方法。

从OS X 10.10 Yosemite开始,View Controller 使用了新的

viewWillAppear,viewDidLoad,以及其它iOS风格的生命周期方法。而在OS X中传统的创建视图方法一般是loadView(),这个方法是向后兼容的,因此我们使用这个方法:

funcloadViewsuper.loadViewself.bugRating.starImage="star.png"self.bugRating.starHighlightedImage"shockedface2_full.png""shockedface2_empty.png"self.bugRating.delegateself

self.bugRating.maxRating5

self.bugRating.horizontalMargin12

self.bugRating.editabletrue

self.bugRating.displayModeUInt(EDStarRatingDisplayFull(0.0 在这里,我们初始化EDStarRating控件:用于表示昆虫惊悚指数的图片,控件的delegate属性以及其它参数。

然后在MasterViewController.swift最后增加一个extension声明:

// MARK: - EDStarRatingProtocol

extension MasterViewController:EDStarRatingProtocol 等下在来实现这个EDStarRatingProtocol协议。

先编译运行程序,效果如下:

添加删除

MasterViewController.xib,拖两个“Gradient Button” 到 table view下。 选择其中一个按钮,打开 Attributes 面板,删除Title属性中的内容,然后在Image属性选择,这将使按钮显示为一个“+”号。

同样,将另一个按钮设置为“-”号按钮(Image属性选择为 “NSRemoveTemplate”)。


打开Assistant Editor 窗口,确保当前内容为MasterViewController.swift文件,首先添加一个扩展的定义:

// MARK: - IBActions

extension MasterViewController 严格来说这个扩展并非必须,但通过这种方式,我们能更好地组织我们的Swift代码。然后选择加号按钮,右键拖一条线到这个扩展上。


在弹出的窗口中,Connection一栏选择Action,Name一栏输入addBug,然后点击Connect.


这样将创建一个 addBug(_:) 方法,每当加号按钮被点击,系统将调用这个方法。在减号按钮上重复同样步骤,Name请使用deleteBug.

MasterViewController.swift实现addBug方法如下:

// 1.使用默认值创建一个新的ScaryBugDoc实例

letnewDoc"New Bug",217)">0.0,144)">nil,0)">)

// 2.将该实例添加到model数组

self.bugs.append(newDocletnewRowIndex-1

// 3.table view插入新行

self.bugsTableView.insertRowsAtIndexes(NSIndexSet(index:newRowIndex:NSTableViewAnimationOptions.EffectGap// 4.选中并滚动到新行

self.bugsTableView.selectRowIndexesfalseself.bugsTableView.scrollRowToVisible(newRowIndex)

实现deleteBug()方法如下:

// 1. Get selected doc

// 2. Remove the bug from the model

self.bugs.removeAtIndex(self.bugsTableView.selectedRow// 3. Remove the selected row from the table view

self.bugsTableView.removeRowsAtIndexes(

withAnimation:NSTableViewAnimationOptions.SlideRight// 4. Clear detail info

updateDetailInfo}


编辑

MasterViewController.xib,打开 Assistant Editor,确保当前显示的文件是 选中text field,右键拖到MasterViewController.swift文件中的addBug()方法之前:


这将允许你为Text Field创建一个IBAction,Name 请使用bugTitleDidEndEdit


这个方法将在text field结束编辑时调用(当用户按下回车键或者离开Text Field控件)。

回到MasterViewController.swift,添加方法:

funcreloadSelectedBugRowletindexSetletcolumnSetself.bugsTableView.reloadDataForRowIndexes(indexSet,columnIndexes:columnSet 在这个方法中,我们重新加载该行数据模型,你需要在模型数据被改动后调用这个方法。

bugTitleDidEndEdit方法实现如下:

{

selectedDoc.data.titleself.bugTitleView.stringValue

reloadSelectedBugRow 首先,调用selectedBugDoc()获得相关昆虫的信息,然后从text field读取文本字符串,并用它来更新模型中的昆虫名称。最后调用reloadSelectedBugRow()通知单元格进行刷新。

:通知table view自己刷新cell要比直接操纵cell的内容要好。

运行App,从列表选中某个昆虫,尝试修改其名称(记得按回车键),表格中的昆虫名将随之改变!

但是如果你切换到其他昆虫,然后返回修改的那一个昆虫,你会发现数据又回到原来(未改动前)了。这是因为我们没有将模型对象进行持久化(保存进文件)。




接下来实现EDStarRating的编辑。 在loadView 方法中,我们已经配置了EDStarRating的delegate属性,我们仅仅需要实现相关委托方法即可。

MasterViewController.swift在EDStarRatingProtocol扩展中添加如下方法:

funcstarsSelectionChanged(control{

selectedDoc.data.rating 跟前面几乎一样: 获得用户选定的昆虫模型,用修改后的值赋值给它。

运行程序。需要注意的是,用户设定新的评级后这个值是被持久化的,哪怕你切换到其他昆虫然后有切换回来。

获取本地图片

MasterViewController.xib,拖一个“Push Button” 控件到image view下方。

修改按钮的title 为 “Change Picture”:


如同加号按钮和减号按钮,为Change Picture 按钮创建一个IBAction,命名为changePicture

这个action在按钮点击时调用。

OS X 有一个特有的控件叫做IKPictureTaker,允许用户从计算机上选择一张图片,或者从摄像头捕捉一张图片。

当用户选择了图片之后,这个控件会调用指定的delegate方法。

MasterViewController.swift加入以下import 语句:

importQuartz

这个 image picker属于 Quartz 框架。

changePicture方法中,添加代码:

{

IKPictureTaker().beginPictureTakerSheetForWindowself.view.window,

withDelegateself,

didEndSelector"pictureTakerDidEnd:returnCode:contextInfo:",

contextInfo 我们先检查用户是否选择了有效的昆虫,如果是,显示picture taker控件。

然后实现pictureTakerDidEnd(_:returnCode:contextInfo:)方法:

funcpictureTakerDidEnd(picker:IKPictureTaker,returnCodeNSInteger,contextInfo:UnsafePointer<Void>letimage=picker.outputImageifimage!=&&returnCode==NSOKButton{

selectedDoc.fullImage=image

selectedDoc.thumbImage=image.imageByScalingAndCroppingForSizeCGSize(width44,height44)) reloadSelectedBugRow 首先检查用户是否点击了OK (NSOKButton) 以及选择的图片是否有效。

如果是,获取用户选定的昆虫模型,修改昆虫的图片及缩略图,然后更新cell。

运行程序,选择一个昆虫,点击Change Picture,从本地文件或摄像头中获取一张图片,这张图片将立即在选定的cell中得到更新。

一些细节上的问题

当你运行程序,视图改变窗口大小,你会发现控件并不能自动适应大小。


这是窗口拖大后的效果。


pplns:o="urn:schemas-microsoft-com:office:office"xmlns:w="urn:schemas-microsoft-com:office:word"xmlns:m="http://schemas.microsoft.com/office/2004/12/omml"xmlns="http://www.w3.org/TR/REC-html40">

这是窗口缩小后的效果。

另外,我们还没有为App数据进行持久化。一旦App重启,用户对数据进行的增加和修改都会丢失。

打开MasterViewController.xib,将View的Size缩小至最小能够足以显示所有控件的程度。


在上图中,3个按钮放在了同一排。在右边细节展示区域中,所有的控件都左对齐,且宽度一致(除了ChangePicture按钮)。

然后,我们在中间增加一个分割线。拖一个VerticalLine到View的中央。


复原操作

复原操作用于将数据恢复至原来的状态。拖一个Push 按钮在Table View下方,修改其标题为Reset。然后打开Assistant Editor,为按钮创建一个IBAction,名为resetData(确认当前打开的源文件为MasterViewController.swift)。


resetData()方法加入如下代码:

setupSampleBugs() 
updateDetailInfo) 
bugsTableView.reloadData()

setupSampleBugs()方法调用会恢复所有模型数据。以nil作为参数值调用updateDetailInfo方法将清除所有细节字段。然后刷新Table View。

运行程序,添加、删除或修改任意数据。然后点击Reset按钮,所有数据又恢复原样。




缩放

MasterViewController.xib,在Size面板中查看 Custome View的大小。在本例中,它应该是540x400大小。但是读者的这个数字会有不同。不管是多大,请记下这个数字。待会会用到。


这将是App出口的最小大小。打开MainMenu.xib,选择 window 对象。在Size 面板中,勾上Constraint右边的Minimum Size 选项,然后将width 和 height 修改为同样的值。


运行程序。


改变出口的大小,这次当窗口缩小到最小尺寸后,就无法再缩小。

接下来我们需要解决控件自适应大小的问题,包括TableView和细节页面中的控件。

首先是MasterViewController视图。

AppDelegate.swift,在applicationDidFinishLaunching()方法最后加入:

// 3. 设置 masterViewController.view的布局约束 masterViewController.view.translatesAutoresizingMaskIntoConstraints = false 
let verticalConstraints = NSLayoutConstraint.constraintsWithVisualFormat("V:|[subView]|",
 options: NSLayoutFormatOptions metrics:  views: ["subView" : masterViewController.view]) 
let horizontalConstraints "H:|[subView]|",0)">])  
NSLayoutConstraint.activateConstraints(verticalConstraints + horizontalConstraints)

在这里,我们允许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标签的左、上约束。

·设置bugTitleView的左、上约束。

·设置Rating标签的左、上约束。

bugRating的左、上、右和高约束。

bugImageView的左、上、下、右约束。

·移动 Change Picture 按钮,以便它的右边沿刚好和bugImageView的右边沿平齐,然后设置它的右、下约束。

设置完bugImageView按钮可能会出现几个警告,但ChangePicture 按钮之后,这些警告会消除。

上述步骤做完后,故事板将如下图所示:


编译运行,再次缩放窗口。


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(_:),加入以下代码,位于updateDetailInfo(selectedDoc)一行以后:

// Enable/disable buttons based on the selection
let buttonsEnabled = (selectedDoc != ) 
deleteButton.enabled = buttonsEnabled 
changePictureButton.enabled = buttonsEnabled 
bugRating.editable = buttonsEnabled 
bugTitleView.enabled = buttonsEnabled

我们首先判断控件是否需要被启用,这是通过用户是否选中单元格来决定的。如果selectedDoc为空,则意味着没有行被选中,这说明控件应当被禁用,否则启用控件。

此外,ratingview 默认是启用的,所以我们还需要在

loadView()中禁用它。找到这行语句:

self.bugRating.editable true

修改为

false

运行程序。

:你还可以在用户未选择有效行时讲整个细节页面都隐藏起来,但这完全取决于你。

保存数据

就像iOS,Mac App也能够使用NSUserDefaults,因此我们完全可以把数据存放到那里。

首先我们必须让模型类实现NSCoding协议。在ScaryBugData.swift中定义一个扩展:

// MARK: - NSCoding
extension ScaryBugData: NSCoding {
 func encodeWithCoder(coder: NSCoder) {
coder.encodeObjectself.title,forKey: "title")
coder.encodeObject"rating")
 } 
}

首先我们让ScaryBugData实现NSCoding协议中的encodeWithCoder方法。这个方法用于对自定义类进行编码。

同时还需要一个与之对应的初始化方法。不同的是,我们无法在扩展中定义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:)方法和encodeWithCoder方法向反,用于从文件中读取数据并反编码为对象。

然后在ScaryBugDoc.swift中定义一个扩展实现NSCoding协议:

// MARK: - NSCoding
extension ScaryBugDocself.data,26)">"data"self.thumbImage,26)">"thumbImage") 
coder.encodeObjectself.fullImage,26)">"fullImage")
 然后在类代码中(不要在扩展定义中)定义Init方法:

required convenience (coder decoderself.()
 self.data = decoder.decodeObjectForKey) as ScaryBugData
 self.thumbImage as NSImage?
 self.fullImage NSImage? 
 接下来将模型数据保存到NSUserDefaults. 在MasterViewController.swift中添加一个方法:

func saveBugs()let data =NSKeyedArchiver.archivedDataWithRootObject)
 NSUserDefaults.standardUserDefaults().setObject(data,26)">"bugs"().synchronize() 
}
这个方法首先将bugs数组构建为一个NSData对象,然后保存到

NSUserDefaults.NSKeyedArchiver。当然数组中的所有对象都实现了NSCoding.

masterViewController.saveBugs 这样,在App退出之前,将所有昆虫数据保存到了NSUserDefaults.

加载数据

applicationDidFinishLaunching

masterViewController.setupSampleBugs 替换为

if().objectForKeyas?NSData{
 masterViewController.bugs NSKeyedUnarchiver.unarchiveObjectWithData(dataas ] 
} else {
 masterViewController.setupSampleBugs 运行程序,添加、删除和编辑昆虫数据,然后退出程序。重新启动App之后,所有上次进行的修改都被保留住了。

:如果应用程序不是正常的退出,则saveBugs()方法不会调用 — 请用Command-Q 退出程序,而不是从Xcode中终止程序。要解决这个问题,你可以在MasterViewController的某个恰当的时机调用saveBug()方法——只要用户进行过新建、删除和编辑操作。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!