原文: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
}
}
注意thumbImage和fullImage聲明為可空的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采用NSTableViewDelegate和NSTableViewDataSource協(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.h和EDStarRating.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.h和NSImage+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:
bugTitleView、bugImageView忠寻。
對于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中得到更新属铁。
當(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ù)原操作用于將數(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)于用戶體驗方面殴俱,我們?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é)頁面都隱藏起來埃撵,但這完全取決于你。
就像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.
在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)行過新建、刪除和編輯操作访敌。