使用swift開發(fā)OSX應(yīng)用

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抹估,輸入你的機(jī)構(gòu)名以及機(jī)構(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箫荡。確笨啵“Also create XIB file for user interface” 為勾選,然后點Next羔挡。

在最后一個彈出窗口中洁奈,點擊Create。新的View Controller將顯示在項目導(dǎo)航窗口中:

打開MasterViewController.xib绞灼。需要注意的是利术,在Mac App中,有大量的類和iOS中都類似,只不過是以NS前綴命名低矮。例如NSScrollView印叁、NSLabel、NSButton等军掂。

在右下角的UI Controls面板(位于第三個Tab)中轮蜕,選中NSTableView將它拖到MasterViewController.xib的畫布中。

不要擔(dān)心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對象)總是有一個默認(rèn)的View,即contentView皂甘。它自動占據(jù)整個窗口的大小玻驻。當(dāng)我們想在窗口中使用自己的視圖時,需要用addSubview方法將它添加到contentView的subviews中偿枕。

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

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

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

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

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

默認(rèn)模板會創(chuàng)建一個以項目名稱為名的文件夾档冬。在這個文件夾下有一個supporting files的子文件夾,其中存放plist和資源文件桃纯。當(dāng)項目很大時酷誓,會創(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)容和那篇教程中的相應(yīng)內(nèi)容幾乎一模一樣。這是因為Mac和iOS編程大部分SDK都是系統(tǒng)的锋谐,除了UI和操作系統(tǒng)相關(guān)的API遍尺。而模型對象不涉及UI,因此模型對象的代碼基本是一致的怀估。

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

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

類名輸入ScaryBugData, Subclass of 輸入NSObject催跪,點擊 Next锁蠕。

在最后一個彈出界面中,點擊Create懊蒸。項目導(dǎo)航窗口將顯示如下:

打開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,因此他們不需要在默認(rèn)構(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ā)者需要自己處理繪圖和鼠標(biāo)事件。

·從?OS X 10.7開始,table view從 NSView繼承机久。這樣就和UITableView差不多了臭墨。cell也有相應(yīng)的View類型,因此也和iOS中的類似——這樣我們就輕松得多了!

在本教程中膘盖,使用的是基于View的TableView胧弛。如果你想了解NSTableView的用法,你可以閱讀這里, 它對 table views 的用法進(jìn)行了詳細(xì)的說明侠畔。

打開MasterViewController.xib结缚,選中table view。注意Table View位于Scroll View中的Clip View中软棺,因此第一個點擊你選中的會是ScrollView红竭,第二次點擊你選中的才是ClipView,第三次點擊才會選中Table View。

當(dāng)然茵宪,你也可以直接從IB的Objects面板中選擇Table View對象(展開 Clip View對象)最冰。

選中Table View之后,在屬性面板中稀火,確認(rèn)Content Mode一項是設(shè)置為View Based而不是Cell Based暖哨。同時,因為我們的列表僅顯示單列憾股,所以將Columns屬性修改為1鹿蜀。

勾選 “Alternating Rows”屬性,讓表格以“明暗顏色交替”的方式繪制單元格服球。

反選 “Headers” 屬性罕拂,因為我們不需要在表格上方顯示一個標(biāo)題枫笛。

接下來我們修改單元格的大小。選擇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的大小哩罪,使它們看起來如下圖所示:

接下來要為每一列設(shè)置一個id授霸。當(dāng)然對于本教程來說,我們只有一個列际插,因此列id可能不是必須的碘耳。

在Objects面板中選擇表格列,打開Identity面板腹鹉,將Identifier設(shè)置為BugColumn藏畅。

如同在iOS中一樣,Table View也有Data Source和Delegate屬性。正常情況下愉阎,這兩個屬性都是同一個對象绞蹦,即MasterViewController

選擇Table View榜旦,打開Connections面板幽七,在Outlets一項下找到delegate和data source。

點擊delegate右邊的小圓圈溅呢,拖到Objects面板中的“File’s Owner”上澡屡。

這將吧Table View 的delegate 屬性設(shè)置為MasterViewController。重復(fù)同樣的動作咐旧,設(shè)置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{}

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

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

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

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

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

下載資源

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

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

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

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

首先幌墓,在項目導(dǎo)航窗口中創(chuàng)建一個名為Art的文件夾但壮,并將3個怪臉圖片拖到這個文件夾中——確保“Copy items if needed” 已勾選, 以及Add to targets中的“ScaryBugsMac” 已選上常侣。

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

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

對于NSImage+Extras.hNSImage+Extras.m,重復(fù)上述步驟舞肆,只不過這次將它們拖進(jìn)的是“Helpers”文件夾焦辅。

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

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


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

創(chuàng)建詳情頁面

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

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

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

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

此外金吗,我們還需要兩個Label十兢,用于表示每個字段的意義(標(biāo)題)。


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

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

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

·??????首先是一個Label,用于充當(dāng)昆蟲名的字段標(biāo)題夕凝,在它下邊是 textfield宝穗。

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

·??????在這個label,下邊是一個customview (后面將改成EDStarRating控件)码秉。

·??????最下面是image view below 控件逮矛。

所有控件左對齊,如下圖所示:

然后選中custom view 控件转砖,打開Identity面板(第三個標(biāo)簽按鈕)將Class 修改為EDStarRating须鼎。

選擇第一個label,打開Attributes 面板(第4個標(biāo)簽按鈕),修改Title 為 “名稱”.

依照上面的方法晋控,將第二個label的title 改為“Rating”汞窗。

選擇最頂級的 view (在document outline面板中顯示為“Custom View”) ,打開Size 面板糖荒,查看它的大猩颊蕖:

打開MainMenu.xib, 選擇 ScaryBugsMac window, 設(shè)置window 的寬高為前面記住的寬高。然后勾選MinimumSize捶朵。

運行后效果如下:

EDStarRating控件并沒有在界面上顯示蜘矢,這是因為我們還沒有配置它。

打開MasterViewController.xib综看,打開Assistant Editor (工具欄中“Editor” 面板的第二個按鈕), 并確保當(dāng)前編輯的內(nèi)容是MasterViewController.swift品腹。

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

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

重復(fù)上述步驟羡鸥,為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ù)模型中檢索響應(yīng)的對象奕剃。

然后是這個方法:

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)

}

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

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

viewWillAppear,viewDidLoad操软,以及其它iOS風(fēng)格的生命周期方法嘁锯。而在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屬性選擇腻要,這將使按鈕顯示為一個“+”號复罐。

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

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

// MARK: - IBActions

extension MasterViewController{}

嚴(yán)格來說這個擴(kuò)展并非必須趟济,但通過這種方式乱投,我們能更好地組織我們的Swift代碼。然后選擇加號按鈕顷编,右鍵拖一條線到這個擴(kuò)展上戚炫。

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

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

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

// 1.?使用默認(rèn)值創(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, 確保當(dāng)前顯示的文件是MasterViewController.swift钮惠。

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

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

這個方法將在text field結(jié)束編輯時調(diào)用(當(dāng)用戶按下回車鍵或者離開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()通知單元格進(jìn)行刷新究西。

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

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

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

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

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

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

ifletselectedDoc=selectedBugDoc(){

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

}

}

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

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

獲取本地圖片

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

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

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

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

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

當(dāng)用戶選擇了圖片之后妆距,這個控件會調(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中得到更新属铁。

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

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

這是窗口拖大后的效果。

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

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

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

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

復(fù)原操作

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

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

setupSampleBugs()

updateDetailInfo(nil)

bugsTableView.reloadData()

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

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

縮放

打開MasterViewController.xib颂斜,在Size面板中查看 Custome View的大小轮纫。在本例中,它應(yīng)該是540x400大小焚鲜。但是讀者的這個數(shù)字會有不同。不管是多大放前,請記下這個數(shù)字忿磅。待會會用到。

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

運行程序搓谆。

改變出口的大小,這次當(dāng)窗口縮小到最小尺寸后豪墅,就無法再縮小泉手。

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

首先是MasterViewController視圖斩萌。

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

// 3. 設(shè)置 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進(jìn)行布局,以便在窗口大小改變時,讓它的高度自動增長倔既,但寬度保持恒定。

打開MasterViewController.xib实蓬,選擇table view,點擊右下角的Pin 按鈕酌伊, 勾上上虹脯、左、下3個約束,以及一個等寬約束蔼紧,然后點擊 “Add 4 Constraints”:

注意圖片中的約束值可能和讀者的實際值有所不同向楼。

然后選擇Reset按鈕逻卖,設(shè)置其與Table View的上邊距約束和一個相對于Main View的左邊距約束:

接著選擇分隔線,設(shè)置其與Main View的上盗迟、下邊距約束,以及與TableView的左邊距約束邮弹,確認(rèn)在左邊距約束的下拉列表中選擇了 “Bordered ScrollView – Table View” :

接下來設(shè)置“Add” 和 “Delete” 按鈕或粮。我們不需要改變它們的大小氯材,唯一需要改變的是它們和Table View之間的距離。對于兩個按鈕听盖,我們需要設(shè)置它們的左、上,寬度和高度約束毛雇。已Add按鈕為例,顯示如下圖:

Delete按鈕類似。

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

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

·??????設(shè)置Name標(biāo)簽的左、上約束。

·??????設(shè)置bugTitleView的左、上約束。

·??????設(shè)置Rating標(biāo)簽的左、上約束。

·??????設(shè)置bugRating的左沥寥、上淮野、右和高約束。

·??????設(shè)置bugImageView的左、上萝勤、下趟径、右約束幕屹。

·??????移動 Change Picture 按鈕说敏,以便它的右邊沿剛好和bugImageView的右邊沿平齊迅诬,然后設(shè)置它的右撑蚌、下約束模软。


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

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

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

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

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

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

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

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

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

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

·??????如果用戶未選擇任何行吉捶,我們禁用上述控件夺鲜,用戶將不能和它們進(jìn)行任何交互皆尔。

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

在ChangePicture 按鈕食呻、text field上重復(fù)上述步驟流炕。

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

打開AssistantEditor 確保當(dāng)前編輯的文件為MasterViewController.swift揭斧。

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

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

重復(fù)上述步驟,為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為空,則意味著沒有行被選中淆两,這說明控件應(yīng)當(dāng)被禁用断箫,否則啟用控件。

此外秋冰,ratingview 默認(rèn)是啟用的仲义,所以我們還需要在

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

self.bugRating.editable=true

修改為

self.bugRating.editable=false

運行程序剑勾。

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

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

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

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

// 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方法捂刺。這個方法用于對自定義類進(jìn)行編碼鸳惯。

同時還需要一個與之對應(yīng)的初始化方法商蕴。不同的是,我們無法在擴(kuò)展中定義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中定義一個擴(kuò)展實現(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")

}

}

然后在類代碼中(不要在擴(kuò)展定義中)定義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格郁。當(dāng)然數(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之后,所有上次進(jìn)行的修改都被保留住了坟奥。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凉敲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子寺旺,更是在濱河造成了極大的恐慌爷抓,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阻塑,死亡現(xiàn)場離奇詭異废赞,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)叮姑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門唉地,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人传透,你說我怎么就攤上這事耘沼。” “怎么了朱盐?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵群嗤,是天一觀的道長。 經(jīng)常有香客問我兵琳,道長狂秘,這世上最難降的妖魔是什么骇径? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮者春,結(jié)果婚禮上破衔,老公的妹妹穿的比我還像新娘。我一直安慰自己钱烟,他們只是感情好晰筛,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拴袭,像睡著了一般读第。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拥刻,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天怜瞒,我揣著相機(jī)與錄音,去河邊找鬼般哼。 笑死吴汪,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的逝她。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼睬捶,長吁一口氣:“原來是場噩夢啊……” “哼黔宛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起擒贸,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤臀晃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后介劫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體徽惋,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年座韵,在試婚紗的時候發(fā)現(xiàn)自己被綠了险绘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡誉碴,死狀恐怖宦棺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情黔帕,我是刑警寧澤代咸,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站成黄,受9級特大地震影響呐芥,放射性物質(zhì)發(fā)生泄漏逻杖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一思瘟、第九天 我趴在偏房一處隱蔽的房頂上張望荸百。 院中可真熱鬧,春花似錦潮太、人聲如沸管搪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽更鲁。三九已至,卻和暖如春奇钞,著一層夾襖步出監(jiān)牢的瞬間澡为,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工景埃, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留媒至,地道東北人。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓谷徙,卻偏偏與公主長得像拒啰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子完慧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359