使用swift開發(fā)OSX應用

swift?/OSX

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

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

轉(zhuǎn)自:http://blog.csdn.net/u011349387/article/details/50451617

打開Xcode,使用File\NewProject…菜單,在彈出窗口中選擇 “OS X/Application”,然后Next。

在接下來的窗口中,配置App信息。在product name欄中輸入ScaryBugsMac,輸入你的機構(gòu)名以及機構(gòu)ID。剩余字段保留為空白。

選擇Swift作為開發(fā)語言,保持所有選項框反選,document extension欄保留為空白。然后點Next。

然后Xcode會要求你選擇項目保存路徑。選擇一個物理路徑,然后點擊Create。

項目就創(chuàng)建完了,這是一個單窗口App。點擊工具欄左上角的Run按鈕,運行這個程序,效果如下圖所示。

首先我們來總結(jié)一下。我們使用Xcode模板創(chuàng)建了一個Mac App項目,然后編譯運行了這個空白項目。與iOS開發(fā)的最大不同在于:

·??????窗口不需要特別指明大小,比如iPhone或iPad屏幕大小——MacApp的窗口是可以通過拖動來改變大小的。

·??????Map App可以擁有多個窗口,窗口支持最小化,重排等操作。

然后我們來新建一個View Controller,并在它上面放入App的主界面。使用

File\New\File…菜單,在彈出窗口中,選擇OS X\Source\Cocoa Class,然后點Next。

類名填入 MasterViewController, “Subclass of”填入NSViewController。確?!癆lso 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啟動時調(diào)用。

注意:這個方法等同于iOS中的application(_:didFinishLaunchingWithOptions:)方法。

applicationDidFinishLaunching方法內(nèi),加入以下語句:

masterViewController=MasterViewController(nibName:"MasterViewController", bundle:nil)

window.contentView.addSubview(masterViewController.view)

masterViewController.view.frame=(window.contentViewasNSView).bounds


在 OS X中,窗口(NSWindow對象)總是有一個默認的View,即contentView。它自動占據(jù)整個窗口的大小。當我們想在窗口中使用自己的視圖時,需要用addSubview方法將它添加到contentView的subviews中。

在iOS開發(fā)中,我們可以設置將一個View Controller直接設置為窗口的rootViewController屬性,但在OS X中你只能將視圖添加到contentView的subviews,因為OS X中沒有rootViewController的概念。

運行App,你將看到如下畫面:

數(shù)據(jù)模型

接下來創(chuàng)建數(shù)據(jù)模型。

首先我們來熟悉一下Xcode項目文件的組織結(jié)構(gòu):

默認模板會創(chuàng)建一個以項目名稱為名的文件夾。在這個文件夾下有一個supporting files的子文件夾,其中存放plist和資源文件。當項目很大時,會創(chuàng)建大量的文件,查找文件就會變得很困難。因此我們需要有一個良好的項目文件組織形式。

首先,我們新建一個文件夾(group),命名為GUI。在ScaryBugsMac文件夾上點擊右鍵,將彈出一個快捷菜單,選擇NewGroup,然后輸入GUI。

然后將所有跟UI有關(guān)的文件拖到這個文件夾(AppDelegate.swift,MasterViewController.swift/.xibandMainMenu.xib),如下圖所示:

然后新建另一個文件夾Model。

在Model文件夾中將包含如下內(nèi)容

·ScaryBugData: 包含兩個屬性:昆蟲的名稱及昆蟲的估價。

·ScaryBugDoc: 包含3個屬性:昆蟲圖片、昆蟲縮略圖及一個ScaryBugData屬性。

實現(xiàn)模型對象

注意:如果你閱讀過How ToCreate A Simple iPhone App on iOS 5 Tutorial, 你會發(fā)現(xiàn)接下來的內(nèi)容和那篇教程中的相應內(nèi)容幾乎一模一樣。這是因為Mac和iOS編程大部分SDK都是系統(tǒng)的,除了UI和操作系統(tǒng)相關(guān)的API。而模型對象不涉及UI,因此模型對象的代碼基本是一致的。

對于ScaryBug的模型類,將Mac版本與iOS版本只有一個地方不同,即將UIImage類修改為NSImage即可。當然,你也需要將它從O-C實現(xiàn)修改為Swift實現(xiàn)。

在Model文件夾上點擊右鍵,點擊 “New File…”,然后選擇OS X\Source\Cocoa Class 模板,然后點擊Next。

類名輸入ScaryBugData, Subclass of 輸入NSObject,點擊 Next。

在最后一個彈出界面中,點擊Create。項目導航窗口將顯示如下:

打開ScaryBugData.swift替換為如下內(nèi)容:

importFoundation

classScaryBugData:NSObject{

vartitle:String

varrating:Double

overrideinit(){

self.title=String()

self.rating=0.0

}

init(title:String, rating:Double){

self.title=title

self.rating=rating

}

}

然后創(chuàng)建另一個模型對象ScaryBugDoc。

打開ScaryBugDoc.swift編輯為如下內(nèi)容:

importFoundation

importAppKit

classScaryBugDoc:NSObject{

vardata:ScaryBugData

varthumbImage:NSImage?

varfullImage:NSImage?

overrideinit(){

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,因此他們不需要在默認構(gòu)造函數(shù)中初始化。

打開MasterViewController.swift,增加一個屬性聲明:

varbugs=[ScaryBugDoc]()

這個數(shù)組屬性用于存儲昆蟲列表,接下來我們將會用一些數(shù)據(jù)填充這個數(shù)組。

填充數(shù)據(jù)及圖片

MasterViewController需要用一系列昆蟲來填充。你可以從此處下載所需的

昆蟲圖片。

下載完圖片之后,,將所有圖片從Finder中拖到Images.xcassets中如下圖右邊AppIcon之下的位置:

打開MasterViewController.swift添加如下方法:

funcsetupSampleBugs(){

letbug1=ScaryBugDoc(title:"Potato Bug", rating:4.0,

thumbImage:NSImage(named:"potatoBugThumb"), fullImage:NSImage(named:"potatoBug"))

letbug2=ScaryBugDoc(title:"House Centipede", rating:3.0,

thumbImage:NSImage(named:"centipedeThumb"), fullImage:NSImage(named:"centipede"))

letbug3=ScaryBugDoc(title:"Wolf Spider", rating:5.0,

thumbImage:NSImage(named:"wolfSpiderThumb"), fullImage:NSImage(named:"wolfSpider"))

letbug4=ScaryBugDoc(title:"Lady Bug", rating:1.0,

thumbImage:NSImage(named:"ladybugThumb"), fullImage:NSImage(named:"ladybug"))

bugs=[bug1, bug2, bug3, bug4]

}

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

masterViewController.setupSampleBugs()

編譯運行程序,確保編譯通過。

接下來,我們將在UI中顯示這些圖片和數(shù)據(jù)。

顯示昆蟲列表

在 OS X中,Table View使用NSTableView類,它等同于iOS的UITableView類,但有一個最大的不同是:NSTableView的每一行有多個列或多個單元格。

·在OS X 10.7Lion之前,table view cell繼承于NSCell類。而后者并非NSView類,因此開發(fā)者需要自己處理繪圖和鼠標事件。

·從?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上的列,拖動它的大小使其占據(jù)整個表格寬度。

然后是單元格的配置。我們需要在單元格中顯示昆蟲的圖片和名稱,因此需要在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面板中,將高度調(diào)整為32。

然后選中Image View和 Text Field,使它們位于單元格中心,并調(diào)整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:NSTableViewDataSource{

funcnumberOfRowsInTableView(aTableView:NSTableView!)->Int{

returnself.bugs.count

}

functableView(tableView:NSTableView!, viewForTableColumn tableColumn:NSTableColumn!, row:Int)->NSView!{

// 1

varcellView:NSTableCellView=tableView.makeViewWithIdentifier(tableColumn.identifier, owner:self)asNSTableCellView

// 2

iftableColumn.identifier=="BugColumn"{

// 3

letbugDoc=self.bugs[row]

cellView.imageView!.image=bugDoc.thumbImage

cellView.textField!.stringValue=bugDoc.data.title

returncellView

}

returncellView

?? }

}

// MARK: - NSTableViewDelegate

extension MasterViewController:NSTableViewDelegate{}

我們通過擴展讓MasterViewController采用NSTableViewDelegateNSTableViewDataSource協(xié)議。

要讓列表渲染數(shù)據(jù)至少需要實現(xiàn)兩個數(shù)據(jù)源方法。

首先是numberOfRowsInTableView方法,OS通過這個方法獲取要渲染的表格行數(shù)。

其次是tableView(_:viewForTableColumn:row:)方法。OS通過這個方法知道如何去渲染每行中的每個單元格。在這個方法中,我們需要用數(shù)據(jù)對單元格進行填充。

運行程序,如果一切正常,我們將在表格中看到昆蟲列表。

下載資源

為了完成本教程,你可能需要下載這些壓縮包,并解壓縮。

注意:為了將昆蟲分成 “一點也不可怕” 到 “極度恐怖”幾個級別,你還需要用到一個開源的分級組件EDStarRating,這也被包含在壓縮包中。

在本教程中,我們不會解釋如何實現(xiàn)這個組件,而只是演示如何在項目中使用它。壓縮包中還包括了一個NSImage類別,可以從一張大圖片生成縮略圖。 此外,還包括3張怪臉圖片,分別用于顯示昆蟲的不同級別。

關(guān)于 EDStarRating組件,你可以參考它的github主頁.

首先,在項目導航窗口中創(chuàng)建一個名為Art的文件夾,并將3個怪臉圖片拖到這個文件夾中——確?!癈opy items if needed” 已勾選, 以及Add to targets中的“ScaryBugsMac” 已選上。

再創(chuàng)建一個名為“Views” 的文件夾, 將EDStarRating.hEDStarRating.m拖到該文件夾。 再次確?!癈opy items if needed” 已勾選以及Add to targets中的“ScaryBugsMac” 已選上。

點擊Finish. 在下一窗口當被問到 “Would you like to configure an Objective-C bridgingheader?” 時選擇Yes。這將創(chuàng)建一個Objective-C 類到Swift 代碼的橋接頭文件。

對于NSImage+Extras.hNSImage+Extras.m,重復上述步驟,只不過這次將它們拖進的是“Helpers”文件夾。

打開ScaryBugsMac-Bridging-Header.h加入以下import語句:

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


以下為最終效果,其中橋接頭文件已經(jīng)被我們移到 Supporting Files 文件夾中:

創(chuàng)建詳情頁面

在iOS中,典型的“主-細頁面App”需要創(chuàng)建兩個視圖,但在 OS X,由于屏幕不再受到限制,我們可以將它們合并在同一個視圖中。

打開MasterViewController.xib,選中view,將寬度和高度拖大。如圖:

我們需要顯示下列信息: 昆蟲名, 驚悚指數(shù)和昆蟲圖片。

昆蟲名用NSTextField控件顯示,驚悚指數(shù)用EDStarRating控件顯示,昆蟲圖片則用NSImageView顯示。

此外,我們還需要兩個Label,用于表示每個字段的意義(標題)。


拖一個 Text Field (昆蟲名), 2個Labels (字段標題), 一個Image View 到view中。

EDStarRating控件是一個定制控件,無法在Objects Library中找到它,因此你需要先拖入一個 “Custom View”控件。

將這些控件放到view的右邊,從上到下依次擺放:

·??????首先是一個Label,用于充當昆蟲名的字段標題,在它下邊是 textfield。

·??????在text field下面是第二個 label(驚悚指數(shù)的字段標題)。

·??????在這個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” 面板的第二個按鈕), 并確保當前編輯的內(nèi)容是MasterViewController.swift。

選中table View,按下右鍵,拖一條線到MasterViewController.swift文件中:

這將彈出一個窗口,允許你創(chuàng)建一個IBOutlet。在Name中輸入bugsTableView, Storage 設置為 Weak, 然后點擊Connect。

重復上述步驟,為text field和image view創(chuàng)建兩個IBOutlet:

bugTitleViewbugImageView。

對于custom view, 則創(chuàng)建一個IBOutlet:bugRating.

最終,MasterViewController.swift文件中將新增如下內(nèi)容:

@IBOutlet weakvarbugsTableView:NSTableView!

@IBOutlet weakvarbugTitleView:NSTextField!

@IBOutlet weakvarbugImageView:NSImageView!

@IBOutlet weakvarbugRating:EDStarRating!

顯示昆蟲詳情

打開MasterViewController.swift增加如下方法:

funcselectedBugDoc()-> ScaryBugDoc?{

letselectedRow=self.bugsTableView.selectedRow;

ifselectedRow >=0&&selectedRow

returnself.bugs[selectedRow]

}

returnnil

}

這個方法根據(jù)用戶選中的行索引,從數(shù)據(jù)模型中檢索響應的對象。

然后是這個方法:

funcupdateDetailInfo(doc:ScaryBugDoc?){

vartitle=""

varimage:NSImage?

varrating=0.0

ifletscaryBugDoc=doc{

title=scaryBugDoc.data.title

image=scaryBugDoc.fullImage

rating=scaryBugDoc.data.rating

}

self.bugTitleView.stringValue=title

self.bugImageView.image=image

self.bugRating.rating=Float(rating)

}

這個方法根據(jù)ScaryBugDoc對象,將昆蟲的信息和圖片在UI上顯示。然后是這個方法:

functableViewSelectionDidChange(notification:NSNotification!){

letselectedDoc=selectedBugDoc()

updateDetailInfo(selectedDoc)

}

當用戶改變了在表格中的選擇時,這個方法調(diào)用前兩個實用方法。

從OS X 10.10 Yosemite開始,View Controller 使用了新的

viewWillAppear,viewDidLoad,以及其它iOS風格的生命周期方法。而在OS X中傳統(tǒng)的創(chuàng)建視圖方法一般是loadView(), 這個方法是向后兼容的,因此我們使用這個方法:

overridefuncloadView(){

super.loadView()

self.bugRating.starImage=NSImage(named:"star.png")

self.bugRating.starHighlightedImage=NSImage(named:"shockedface2_full.png")

self.bugRating.starImage=NSImage(named:"shockedface2_empty.png")

self.bugRating.delegate=self

self.bugRating.maxRating=5

self.bugRating.horizontalMargin=12

self.bugRating.editable=true

self.bugRating.displayMode=UInt(EDStarRatingDisplayFull)

self.bugRating.rating=Float(0.0)

}

在這里,我們初始化EDStarRating控件:用于表示昆蟲驚悚指數(shù)的圖片,控件的delegate屬性以及其它參數(shù)。

然后在MasterViewController.swift最后增加一個extension聲明:

// MARK: - EDStarRatingProtocol

extension MasterViewController:EDStarRatingProtocol{}

等下在來實現(xiàn)這個EDStarRatingProtocol協(xié)議。

先編譯運行程序,效果如下:

添加、刪除

打開MasterViewController.xib,拖兩個“Gradient Button” 到 table view下。 選擇其中一個按鈕, 打開 Attributes 面板,刪除Title屬性中的內(nèi)容,然后在Image屬性選擇,這將使按鈕顯示為一個“+”號。

同樣,將另一個按鈕設置為“-”號按鈕(Image屬性選擇為 “NSRemoveTemplate”)。

打開Assistant Editor 窗口,確保當前內(nèi)容為MasterViewController.swift文件,首先添加一個擴展的定義:

// MARK: - IBActions

extension MasterViewController{}

嚴格來說這個擴展并非必須,但通過這種方式,我們能更好地組織我們的Swift代碼。然后選擇加號按鈕,右鍵拖一條線到這個擴展上。

在彈出的窗口中,Connection一欄選擇Action,Name一欄輸入addBug, 然后點擊Connect.

這樣將創(chuàng)建一個 addBug(_:) 方法,每當加號按鈕被點擊,系統(tǒng)將調(diào)用這個方法。在減號按鈕上重復同樣步驟, Name請使用deleteBug.

打開MasterViewController.swift實現(xiàn)addBug方法如下:

// 1.?使用默認值創(chuàng)建一個新的ScaryBugDoc實例

letnewDoc=ScaryBugDoc(title:"New Bug", rating:0.0, thumbImage:nil, fullImage:nil)

// 2.?將該實例添加到model?數(shù)組

self.bugs.append(newDoc)

letnewRowIndex=self.bugs.count-1

// 3.向table view插入新行

self.bugsTableView.insertRowsAtIndexes(NSIndexSet(index:newRowIndex), withAnimation:NSTableViewAnimationOptions.EffectGap)

// 4.?選中并滾動到新行

self.bugsTableView.selectRowIndexes(NSIndexSet(index:newRowIndex), byExtendingSelection:false)

self.bugsTableView.scrollRowToVisible(newRowIndex)

實現(xiàn)deleteBug()方法如下:

// 1. Get selected doc

ifletselectedDoc=selectedBugDoc(){

// 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(

NSIndexSet(index:self.bugsTableView.selectedRow),

withAnimation:NSTableViewAnimationOptions.SlideRight)

// 4. Clear detail info

updateDetailInfo(nil)

}

編輯

打開MasterViewController.xib, 打開 Assistant Editor, 確保當前顯示的文件是MasterViewController.swift。

選中text field, 右鍵拖到MasterViewController.swift文件中的addBug()方法之前:

這將允許你為Text Field創(chuàng)建一個IBAction,Name 請使用bugTitleDidEndEdit。

這個方法將在text field結(jié)束編輯時調(diào)用(當用戶按下回車鍵或者離開Text Field控件)。

回到MasterViewController.swift, 添加方法:

funcreloadSelectedBugRow(){

letindexSet=NSIndexSet(index:self.bugsTableView.selectedRow)

letcolumnSet=NSIndexSet(index:0)

self.bugsTableView.reloadDataForRowIndexes(indexSet, columnIndexes:columnSet)

}

在這個方法中,我們重新加載該行數(shù)據(jù)模型,你需要在模型數(shù)據(jù)被改動后調(diào)用這個方法。

bugTitleDidEndEdit方法實現(xiàn)如下:

ifletselectedDoc=selectedBugDoc(){

selectedDoc.data.title=self.bugTitleView.stringValue

reloadSelectedBugRow()

}

首先,調(diào)用selectedBugDoc()獲得相關(guān)昆蟲的信息,然后從text field讀取文本字符串,并用它來更新模型中的昆蟲名稱。最后調(diào)用reloadSelectedBugRow()通知單元格進行刷新。

注意:通知table view自己刷新cell要比直接操縱cell的內(nèi)容要好。

運行App,從列表選中某個昆蟲,嘗試修改其名稱(記得按回車鍵),表格中的昆蟲名將隨之改變!

但是如果你切換到其他昆蟲,然后返回修改的那一個昆蟲,你會發(fā)現(xiàn)數(shù)據(jù)又回到原來(未改動前)了。這是因為我們沒有將模型對象進行持久化(保存進文件)。

接下來實現(xiàn)EDStarRating的編輯。 在loadView 方法中,我們已經(jīng)配置了EDStarRating的delegate屬性,我們僅僅需要實現(xiàn)相關(guān)委托方法即可。

打開MasterViewController.swift?EDStarRatingProtocol擴展中添加如下方法:

funcstarsSelectionChanged(control:EDStarRating!, rating:Float){

ifletselectedDoc=selectedBugDoc(){

selectedDoc.data.rating=Double(self.bugRating.rating)

}

}

跟前面幾乎一樣: 獲得用戶選定的昆蟲模型,用修改后的值賦值給它。

運行程序。需要注意的是,用戶設定新的評級后這個值是被持久化的,哪怕你切換到其他昆蟲然后有切換回來。

獲取本地圖片

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

修改按鈕的title 為 “Change Picture”:

如同加號按鈕和減號按鈕,為Change Picture 按鈕創(chuàng)建一個IBAction,命名為changePicture。

這個action在按鈕點擊時調(diào)用。

OS X 有一個特有的控件叫做IKPictureTaker,允許用戶從計算機上選擇一張圖片,或者從攝像頭捕捉一張圖片。

當用戶選擇了圖片之后,這個控件會調(diào)用指定的delegate方法。

打開MasterViewController.swift加入以下import 語句:

importQuartz

這個 image picker屬于 Quartz 框架。

changePicture方法中,添加代碼:

ifletselectedDoc=selectedBugDoc(){

IKPictureTaker().beginPictureTakerSheetForWindow(self.view.window,

withDelegate:self,

didEndSelector:"pictureTakerDidEnd:returnCode:contextInfo:",

contextInfo:nil)

}

我們先檢查用戶是否選擇了有效的昆蟲,如果是,顯示picture taker控件。

然后實現(xiàn)pictureTakerDidEnd(_:returnCode:contextInfo:)方法:

funcpictureTakerDidEnd(picker:IKPictureTaker, returnCode:NSInteger, contextInfo:UnsafePointer){

letimage=picker.outputImage()

ifimage!=nil&&returnCode==NSOKButton{

self.bugImageView.image=image

ifletselectedDoc=selectedBugDoc(){

selectedDoc.fullImage=image

selectedDoc.thumbImage=image.imageByScalingAndCroppingForSize(CGSize(width:44, height:44))reloadSelectedBugRow()

}

}

}

首先檢查用戶是否點擊了OK (NSOKButton) 以及選擇的圖片是否有效。

如果是,獲取用戶選定的昆蟲模型,修改昆蟲的圖片及縮略圖,然后更新cell。

運行程序,選擇一個昆蟲,點擊Change Picture, 從本地文件或攝像頭中獲取一張圖片,這張圖片將立即在選定的cell中得到更新。

一些細節(jié)上的問題

當你運行程序,視圖改變窗口大小,你會發(fā)現(xiàn)控件并不能自動適應大小。

這是窗口拖大后的效果。

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數(shù)據(jù)進行持久化。一旦App重啟,用戶對數(shù)據(jù)進行的增加和修改都會丟失。

打開MasterViewController.xib,將View的Size縮小至最小能夠足以顯示所有控件的程度。

在上圖中,3個按鈕放在了同一排。在右邊細節(jié)展示區(qū)域中,所有的控件都左對齊,且寬度一致(除了ChangePicture按鈕)。

然后,我們在中間增加一個分割線。拖一個VerticalLine到View的中央。

復原操作

復原操作用于將數(shù)據(jù)恢復至原來的狀態(tài)。拖一個Push 按鈕在Table View下方,修改其標題為Reset。然后打開Assistant Editor,為按鈕創(chuàng)建一個IBAction,名為resetData(確認當前打開的源文件為MasterViewController.swift)。

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

setupSampleBugs()

updateDetailInfo(nil)

bugsTableView.reloadData()

setupSampleBugs()方法調(diào)用會恢復所有模型數(shù)據(jù)。?以nil作為參數(shù)值調(diào)用updateDetailInfo方法將清除所有細節(jié)字段。然后刷新Table View。

運行程序,添加、刪除或修改任意數(shù)據(jù)。然后點擊Reset按鈕,所有數(shù)據(jù)又恢復原樣。

縮放

打開MasterViewController.xib,在Size面板中查看 Custome View的大小。在本例中,它應該是540x400大小。但是讀者的這個數(shù)字會有不同。不管是多大,請記下這個數(shù)字。待會會用到。

這將是App出口的最小大小。打開MainMenu.xib, 選擇 window 對象。在Size 面板中,勾上Constraint右邊的Minimum Size 選項,然后將width 和 height 修改為同樣的值。

運行程序。

改變出口的大小,這次當窗口縮小到最小尺寸后,就無法再縮小。

接下來我們需要解決控件自適應大小的問題,包括TableView和細節(jié)頁面中的控件。

首先是MasterViewController視圖。

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

// 3. 設置 masterViewController.view的布局約束masterViewController.view.translatesAutoresizingMaskIntoConstraints=false

letverticalConstraints=NSLayoutConstraint.constraintsWithVisualFormat("V:|[subView]|",

options:NSLayoutFormatOptions(0),

metrics:nil,

views:["subView":masterViewController.view])

lethorizontalConstraints=NSLayoutConstraint.constraintsWithVisualFormat("H:|[subView]|",

options:NSLayoutFormatOptions(0),

metrics:nil,

views:["subView":masterViewController.view])

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按鈕類似。

運行程序,改變窗口大小,查看效果。

然后是右邊的細節(jié)頁面。在這個頁面中,當窗口寬度變大時,所有控件的寬度也會變大。以TableView相同的方法,分別設置它們的自動布局如下:

·??????設置Name標簽的左、上約束。

·??????設置bugTitleView的左、上約束。

·??????設置Rating標簽的左、上約束。

·??????設置bugRating的左、上、右和高約束。

·??????設置bugImageView的左、上、下、右約束。

·??????移動 Change Picture 按鈕,以便它的右邊沿剛好和bugImageView的右邊沿平齊,然后設置它的右、下約束。


設置完bugImageView按鈕可能會出現(xiàn)幾個警告,但ChangePicture 按鈕之后,這些警告會消除。

上述步驟做完后,故事板將如下圖所示:

編譯運行,再次縮放窗口。

bugImageView的Scale設置會對圖片產(chǎn)生不同的顯示效果。在IB中選擇Image Well 控件, 修改其scaling屬性為“Proportionally Up or Down” 或者 “Axes Independently”,然后運行App,看看有什么不同。

注意: 如果你想限制窗口的最大縮放尺寸,則你也可以用設置窗口最小縮放尺寸同樣的方式加以限制。

關(guān)注細節(jié)

關(guān)于用戶體驗方面,我們?nèi)匀挥幸恍┘毠?jié)值得注意。例如:運行App,不要選擇任何昆蟲,點擊“Delete” 或者 “Change Picture” 按鈕,什么都不會發(fā)生,Why?

作為程序員,你當然知道當用戶什么都沒選擇的情況下,不應當執(zhí)行任何操作,但對于用戶而言,這種情況仍然顯得不太友好:

我們通過以下方式來解決這個問題:

·??????如果用戶選中了某個單元格,我們才讓Delete按鈕、Change picture按鈕、文本框和rating view可用。

·??????如果用戶未選擇任何行,我們禁用上述控件,用戶將不能和它們進行任何交互。

打開MasterViewController.xib,選擇Delete按鈕,在屬性面板,將Enabled屬性前的勾去掉。

在ChangePicture 按鈕、text field上重復上述步驟。

這樣,當程序剛啟動時,上述控件將被禁用。然后我們需要在用戶選擇了表格中的單元格之后再啟用它們。要實現(xiàn)這個目的,我們首先需要為它們創(chuàng)建IBOutlet。

打開AssistantEditor 確保當前編輯的文件為MasterViewController.swift。

選擇“Delete” 按鈕,右鍵將它拖動到MasterViewController.swift文件中。

在彈出的出口中,選擇connection為“Outlet”, name 欄輸入deleteButton,然后點擊Connect.

重復上述步驟,為Changepicture按鈕創(chuàng)建一個IBOutlet,名為changePictureButton.

打開MasterViewController.swift, 在tableViewSelectionDidChange(_:),加入以下代碼,位于updateDetailInfo(selectedDoc)一行以后:

// Enable/disable buttons based on the selection

letbuttonsEnabled=(selectedDoc!=nil)

deleteButton.enabled=buttonsEnabled

changePictureButton.enabled=buttonsEnabled

bugRating.editable=buttonsEnabled

bugTitleView.enabled=buttonsEnabled

我們首先判斷控件是否需要被啟用,這是通過用戶是否選中單元格來決定的。如果selectedDoc為空,則意味著沒有行被選中,這說明控件應當被禁用,否則啟用控件。

此外,ratingview 默認是啟用的,所以我們還需要在

loadView()中禁用它。找到這行語句:

self.bugRating.editable=true

修改為

self.bugRating.editable=false

運行程序。

注意:你還可以在用戶未選擇有效行時講整個細節(jié)頁面都隱藏起來,但這完全取決于你。

保存數(shù)據(jù)

就像iOS,Mac App也能夠使用NSUserDefaults, 因此我們完全可以把數(shù)據(jù)存放到那里。

首先我們必須讓模型類實現(xiàn)NSCoding協(xié)議。在ScaryBugData.swift中定義一個擴展:

// MARK: - NSCoding

extension ScaryBugData:NSCoding{

funcencodeWithCoder(coder:NSCoder){

coder.encodeObject(self.title, forKey:"title")

coder.encodeObject(Double(self.rating), forKey:"rating")

}

}

首先我們讓ScaryBugData實現(xiàn)NSCoding協(xié)議中的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方法向反,用于從文件中讀取數(shù)據(jù)并反編碼為對象。

然后在ScaryBugDoc.swift中定義一個擴展實現(xiàn)NSCoding協(xié)議:

// MARK: - NSCoding

extension ScaryBugDoc:NSCoding{

funcencodeWithCoder(coder:NSCoder){

coder.encodeObject(self.data, forKey:"data")

coder.encodeObject(self.thumbImage, forKey:"thumbImage")

coder.encodeObject(self.fullImage, forKey:"fullImage")

}

}

然后在類代碼中(不要在擴展定義中)定義Init方法:

required convenienceinit(coder decoder:NSCoder){

self.init()

self.data=decoder.decodeObjectForKey("data")asScaryBugData

self.thumbImage=decoder.decodeObjectForKey("thumbImage")asNSImage?

self.fullImage=decoder.decodeObjectForKey("fullImage")asNSImage?

}

接下來將模型數(shù)據(jù)保存到NSUserDefaults. 在MasterViewController.swift中添加一個方法:

funcsaveBugs(){

letdata=NSKeyedArchiver.archivedDataWithRootObject(self.bugs)

NSUserDefaults.standardUserDefaults().setObject(data, forKey:"bugs")

NSUserDefaults.standardUserDefaults().synchronize()

}

這個方法首先將bugs數(shù)組構(gòu)建為一個NSData對象,然后保存到

NSUserDefaults.NSKeyedArchiver。當然數(shù)組中的所有對象都實現(xiàn)了NSCoding.

打開AppDelegate.swift, 在applicationWillTerminate()中加入:

masterViewController.saveBugs()

這樣,在App退出之前,將所有昆蟲數(shù)據(jù)保存到了NSUserDefaults.

加載數(shù)據(jù)

AppDelegate.swift, 找到applicationDidFinishLaunching

masterViewController.setupSampleBugs()

替換為

ifletdata=NSUserDefaults.standardUserDefaults().objectForKey("bugs")as?NSData{

masterViewController.bugs=NSKeyedUnarchiver.unarchiveObjectWithData(data)as[ScaryBugDoc]

}else{

masterViewController.setupSampleBugs()

}

運行程序,添加、刪除和編輯昆蟲數(shù)據(jù),然后退出程序。重新啟動App之后,所有上次進行的修改都被保留住了。

注意:如果應用程序不是正常的退出,則saveBugs()方法不會調(diào)用 — 請用Command-Q 退出程序,而不是從Xcode中終止程序。要解決這個問題,你可以在MasterViewController的某個恰當?shù)臅r機調(diào)用saveBug()方法——只要用戶進行過新建、刪除和編輯操作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容