你在本系列文章中將會學到
- 如何安裝和使用cocoapods來整合第三方庫
- 如何搭建一個類似於iOS簡訊app的介面,以及使用SnapKit來用程式碼設定autolayout
- 如何使用Parse雲服務平臺儲存和同步聊天資訊,學習相應地資料庫知識
- 如何使用Parse的遠端推送功能
- 如何使用Alamofire實現與智慧機器人聊天功能
初始專案下載地址:
百度網盤下載地址配置初始專案
1.cocoapods的安裝
cocoapods的安裝是通過ruby,幸運的是Mac電腦都是預設安裝ruby的,所以安裝ruby的過程就省去了,唯一的前提就是安裝Xcode的CommandLineTools。
commandLineTools的安裝也很簡單,只要在終端輸入以下命令:
1 |
$ xcode-select --install |
如果確實沒有安裝commandLineTools會提示你要安裝它,點安裝就可以開始下載,然後等待下載完成後安裝即可
下面開始安裝cocoapods,本來只需要簡單地在終端輸入以下命令即可:
1 |
$ sudo gem install cocoapods |
但是由於中國的網際網路是”自由的”。。咳咳,所以呢,你要改變gem的預設下載源:
1 |
$ gem sources -a https://ruby.taobao.org |
看到以下結果
1 |
http://ruby.taobao.org added to sources |
將淘寶的ruby源加入進來,看來淘寶也不光是賣東西哈,也是對開發者做了一些貢獻的~
刪除原來的下載源:
1 |
$ gem sources -r https://rubygems.org/ |
看到以下結果就說明已經成功
1 |
https://rubygems.org/ removed from sources |
然後呢,就可以愉快地安裝上cocoapods了!
1 |
$ sudo gem install cocoapods |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
Password:(你的管理員密碼,這裡不會顯示出來) Fetching: cocoapods-core-0.38.2.gem (100%) Successfully installed cocoapods-core-0.38.2 Fetching: claide-0.9.1.gem (100%) Successfully installed claide-0.9.1 Fetching: xcodeproj-0.26.3.gem (100%) Successfully installed xcodeproj-0.26.3 Fetching: cocoapods-downloader-0.9.3.gem (100%) Successfully installed cocoapods-downloader-0.9.3 Fetching: cocoapods-stats-0.5.3.gem (100%) Successfully installed cocoapods-stats-0.5.3 Fetching: cocoapods-try-0.4.5.gem (100%) Successfully installed cocoapods-try-0.4.5 Fetching: cocoapods-trunk-0.6.4.gem (100%) Successfully installed cocoapods-trunk-0.6.4 Fetching: molinillo-0.3.1.gem (100%) Successfully installed molinillo-0.3.1 Fetching: cocoapods-0.38.2.gem (100%) Successfully installed cocoapods-0.38.2 Parsing documentation for cocoapods-core-0.38.2 Installing ri documentation for cocoapods-core-0.38.2 Parsing documentation for claide-0.9.1 Installing ri documentation for claide-0.9.1 Parsing documentation for xcodeproj-0.26.3 Installing ri documentation for xcodeproj-0.26.3 Parsing documentation for cocoapods-downloader-0.9.3 Installing ri documentation for cocoapods-downloader-0.9.3 Parsing documentation for cocoapods-stats-0.5.3 Installing ri documentation for cocoapods-stats-0.5.3 Parsing documentation for cocoapods-try-0.4.5 Installing ri documentation for cocoapods-try-0.4.5 Parsing documentation for cocoapods-trunk-0.6.4 Installing ri documentation for cocoapods-trunk-0.6.4 Parsing documentation for molinillo-0.3.1 Installing ri documentation for molinillo-0.3.1 Parsing documentation for cocoapods-0.38.2 Installing ri documentation for cocoapods-0.38.2 9 gems installed |
OK,cocoapods順利安裝完畢!
2.cocoapods的使用
那麼cocoapods怎麼用呢,當然第一次使用會覺得它非常麻煩,但是呢漸漸地你會發現這是一個非常好用的工具,可以說是iOS開發者必備!
首先建立我們的Xcode工程:File/New/Project…/Single View Application
起名叫圖靈聊天。
我們將要使用圖靈機器人的api進行開發:
圖靈機器人官網
開啟專案,新建一個空檔案:File/New/File…/ iOS/Others/Empty
起名叫Podfile,這一點非常重要,因為這是cocoapods的配置檔案,也就是指定你要使用哪些第三方庫!
我們要使用以下幾個庫:
- Alamofire,網路請求庫,用來呼叫圖靈機器人的api
- SnapKit,用程式碼進行autolayout設定
- Parse ,Parse雲服務平臺的SDK
- ParseUI,Parse提供的便捷UI元件
在Podfile中,輸入以下程式碼:
123source 'https://github.com/CocoaPods/Specs.git'platform :ios, '8.4'use_frameworks!
指定下載源,指定平臺版本,使用framework進行整合由於swift的特殊性,某些第三方庫必須使用framework來整合,但是這樣也有一個好處,我也是最近才發現,就是Parse和ParseUI其實是OC編寫的庫,但是呢卻不需要OC-Swift的橋接檔案了!可以直接當做swift庫來使用!
1 2 3 4 |
pod 'Alamofire', '~> 1.3' pod 'SnapKit', '~> 0.12.0' pod 'Parse','~>1.7.1' pod 'ParseUI','~>1.1.3' |
選擇指定的第三方庫及其版本
開始安裝第三方庫,開啟終端,將當前目錄轉到Podfile所在目錄:
1 |
$ cd |
輸入以下命令開始配置第三方庫:
1 |
$ pod install |
1 2 3 4 5 6 7 8 9 10 |
Analyzing dependencies Downloading dependencies Using Alamofire (1.3.1) Using Bolts (1.2.1) Using Parse (1.7.5.3) Using ParseUI (1.1.4) Using SnapKit (0.12.0) Generating Pods project Integrating client project Sending stats |
然後等待幾分鐘,如果一切正常,沒有出現錯誤的話,開啟專案檔案後你會看到workspace的檔案,以後都要使用這個檔案來開啟專案。
開啟專案,看一下專案的結構:
點一下Pods專案,你會發現所需的framework已經編譯好了,只要在使用前import他們就可以了:
OK,到此我們的專案就配置好了,在我們開始搭建UI之前,先了解一下Parse的使用和一些必要配置
配置Parse
首先開啟Parse的官網:
點我
註冊一個新的使用者,點選右上角的sign up :
以上使用者名稱只是示例,但是app名稱輸入TuringChat。
註冊完畢後,用你剛才註冊的使用者名稱登陸,應該會出現以下介面:
然後匯入我們的示例資料:
點我下載
點選import按鈕:
選擇剛才下載的檔案:
然後剛才匯入的資料就會顯示出來,並自動新建了一個資料庫類:Messages
我們來看一眼Messages類裡都有什麼:
名稱 | 型別 | 含義 | 備註 |
---|---|---|---|
objectId | String | 系統預設鍵 | 每一條資料都對應一個獨一無二的id |
incoming | Boolean | 用來確定該條資訊是傳送給我們的還是傳送出去的 | true就是傳送來的反之就是我們傳送出去的 |
sentDate | Date | 訊息傳送時間 | 無 |
text | String | 訊息的內容 | 無 |
createdAt | Date | 系統預設鍵 | 資料建立時間 |
updatedAt | Date | 系統預設鍵 | 資料上一次更新的時間 |
ACL | ACL | 系統預設鍵 | 資料的讀寫模式 |
接下來我們來測試一下能否讀取到這些資料,首先要獲得該app的application ID和Client Key:
紅線劃掉的那兩行就是我們需要的。
然後開啟專案中的AppDelegate.swift,增加對Parse庫的引用:
1 |
import Parse |
找到以下方法
1 |
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool |
在裡面新增程式碼:
1 |
Parse.setApplicationId("CYdFL9mvG8jHqc4ZA5PJsWMInBbMMun0XCoqnHgf", clientKey: "6tGOC1uIKeYp5glvJE6MXZOWG9pmLtMuIUdh2Yzo") |
連線Parse的伺服器
1 2 3 4 5 6 7 8 9 10 |
var query = PFQuery(className: "Messages") query.orderByAscending("sentDate") query.findObjectsInBackgroundWithBlock { (objects,error) -> Void in for object in objects as! [PFObject]{ let incoming:Bool = object["incoming"] as! Bool let text:String = object["text"] as! String let sentDate:NSDate = object["sentDate"] as! NSDate println("\(object.objectId!)\n\(incoming)\n\(text)\n\(sentDate)") } } |
新建查詢,查詢我們剛才所建的Messages
類,用findObjectsInBackgroundWithBlock
方法取出查詢結果,並用一個迴圈全部列印出來。
cmd+R執行一下,如果沒有問題會輸出類似下面的內容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
oYtildSAOz false 你叫什麼名字? 2015-08-28 06:42:00 +0000 LX7kxmmiEp true 我叫靈靈,聰明又可愛的靈靈 2015-08-28 06:43:00 +0000 p62dmgGIAS false 你愛不愛我? 2015-08-28 06:43:00 +0000 oWReOM43Nf true 愛你麼麼噠 2015-08-28 06:44:00 +0000 mtl2BGt3Mu false 今天北京天氣如何? 2015-08-29 03:59:00 +0000 DikAu5P2Nn true 北京:08/29 週六,20-29° 28° 雷陣雨 微風小於3級;08/30 週日,19-27° 雷陣雨 微風小於3級;08/31 週一,19-27° 雷陣雨 微風小於3級;09/01 週二,20-26° 雷陣雨 微風小於3級; 2015-08-29 03:59:01 +0000 |
很好,我們的資料庫連線沒有問題,那麼下面開始搭建我們的UI。
搭建UI
我們需要搭建的UI只是聊天頁面,我們首先來看一看聊天頁面的結構:
介面主要由以下三個部分組成
那麼這三部分怎樣去實現呢,我先向大家做一些簡單的介紹:
1.導航欄
這一部分實現比較簡單,只要把檢視控制器巢狀在一個導航控制器(UINavigationController)中即可,然後對其外觀進行一些定製化操作。
2.聊天視窗
這一部分用UITableView來構建。仔細觀察你會發現這裡一共有三種UITableViewCell:
- 用來顯示訊息傳送日期的cell
- 傳送訊息氣泡的cell
- 接收訊息氣泡的cell
但其實我們只需要兩個,因為後兩種cell區別只是是顏色和位置,我們只要判斷一下該訊息是傳送的還是接收的,然後相應進行處理即可!
兩種cell都是用的以下這個素材:MessageBubble.png但是,你會問,它為啥是黑色的!怎麼讓他變成圖中的兩種顏色呢?還有明明聊天氣泡的大小是不定的,這樣一張圖怎麼能滿足所有尺寸呢?
有疑問很好,因為它可以成為你學習的動力,我們會在接下來向大家解釋這是如何實現的!Be patient!
3.輸入框
這裡我們要通過重寫UIResponder
類的inputAccessoryView
屬性來自定義我們的輸入框,這樣做的好處是我們的輸入框會和系統的鍵盤結合起來,可以讓其成為第一響應者(first responder),一旦它成為第一響應者,我們自定義的輸入框會跟隨鍵盤一同彈出和收回,就像真正的簡訊app那樣,這個方法比我有一篇文章所寫的實現類似微信的輸入框跟隨鍵盤彈出的效果的方法還要更好一些,所以說方法不是絕對的,因為你總是能夠找到更好的方法,所以,程式設計的時候要經常在腦子裡想”嗯,一定還有更好的方法”。
嗯好嘞,廢話不多說,下面我們就來一步一步地一一實現它們!
首先從最簡單的做起,實現自定義導航欄:
開啟初始專案你會看到模板檔案已經全部建好:
找到AppDelegate.swift檔案中的以下方法:
1 |
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool |
在其中新增如下程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var ChatVC:ChatViewController = ChatViewController() ChatVC.title = "靈靈" UINavigationBar.appearance().tintColor = UIColor(red: 0.05, green: 0.47, blue: 0.91, alpha: 1.0) UINavigationBar.appearance().barTintColor = UIColor(red: 0.05, green: 0.47, blue: 0.91, alpha: 1.0) UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()] UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.LightContent var navigationVC:UINavigationController = UINavigationController(rootViewController: ChatVC) let frame = UIScreen.mainScreen().bounds window = UIWindow(frame: frame) window!.rootViewController = navigationVC window!.makeKeyAndVisible() |
設定app啟動時顯示我們自定義的檢視控制器,並設定一下導航欄的外觀。
ok,第一部分完成。
接下來我們來實現一下第三部分:輸入框,我們要把最難的第二部分留在最後( ⊙ o ⊙ )
開啟ChatViewController.swift檔案:
新增一些全域性常量,在import下面class的定義之上:
1 2 |
let messageFontSize: CGFloat = 17 let toolBarMinHeight: CGFloat = 44 |
第一個是訊息所用的字型大小,第二個是我們輸入框的高度。
新增一些組成輸入框的元件:
1 2 3 |
var toolBar: UIToolbar! var textView: UITextView! var sendButton: UIButton! |
toolBar用來承載輸入框中的元件,之所以用UIToolbar是因為它預設出現在螢幕最下方,就像你的簡訊輸入框那樣。
textView是我們輸入文字的地方,而sendButton則是我們的傳送按鈕。
下面實現我們重寫的inputAccessoryView,在這之前先讓我們的檢視控制器遵循UITextViewDelegate
協議:
1 2 3 4 |
class ViewController: UIViewController,UITextViewDelegate { .... .... } |
下面新增以下程式碼來宣告對inputAccessoryView的重寫:
1 2 3 |
override var inputAccessoryView: UIView! { } |
用get的方式將輸入框的元件進行配置:
在大括號內部新增程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
get { if toolBar == nil { toolBar = UIToolbar(frame: CGRectMake(0, 0, 0, toolBarMinHeight-0.5)) textView = InputTextView(frame: CGRectZero) textView.backgroundColor = UIColor(white: 250/255, alpha: 1) textView.delegate = self textView.font = UIFont.systemFontOfSize(messageFontSize) textView.layer.borderColor = UIColor(red: 200/255, green: 200/255, blue: 205/255, alpha:1).CGColor textView.layer.borderWidth = 0.5 textView.layer.cornerRadius = 5 // textView.placeholder = "Message" textView.scrollsToTop = false textView.textContainerInset = UIEdgeInsetsMake(4, 3, 3, 3) toolBar.addSubview(textView) sendButton = UIButton.buttonWithType(.System) as! UIButton sendButton.enabled = false sendButton.titleLabel?.font = UIFont.boldSystemFontOfSize(17) sendButton.setTitle("傳送", forState: .Normal) sendButton.setTitleColor(UIColor(red: 142/255, green: 142/255, blue: 147/255, alpha: 1), forState: .Disabled) sendButton.setTitleColor(UIColor(red: 0.05, green: 0.47, blue: 0.91, alpha: 1.0), forState: .Normal) sendButton.contentEdgeInsets = UIEdgeInsets(top: 6, left: 6, bottom: 6, right: 6) sendButton.addTarget(self, action: "sendAction", forControlEvents: UIControlEvents.TouchUpInside) toolBar.addSubview(sendButton) // 對元件進行Autolayout設定 textView.setTranslatesAutoresizingMaskIntoConstraints(false) sendButton.setTranslatesAutoresizingMaskIntoConstraints(false) toolBar.addConstraint(NSLayoutConstraint(item: textView, attribute: .Left, relatedBy: .Equal, toItem: toolBar, attribute: .Left, multiplier: 1, constant: 8)) toolBar.addConstraint(NSLayoutConstraint(item: textView, attribute: .Top, relatedBy: .Equal, toItem: toolBar, attribute: .Top, multiplier: 1, constant: 7.5)) toolBar.addConstraint(NSLayoutConstraint(item: textView, attribute: .Right, relatedBy: .Equal, toItem: sendButton, attribute: .Left, multiplier: 1, constant: -2)) toolBar.addConstraint(NSLayoutConstraint(item: textView, attribute: .Bottom, relatedBy: .Equal, toItem: toolBar, attribute: .Bottom, multiplier: 1, constant: -8)) toolBar.addConstraint(NSLayoutConstraint(item: sendButton, attribute: .Right, relatedBy: .Equal, toItem: toolBar, attribute: .Right, multiplier: 1, constant: 0)) toolBar.addConstraint(NSLayoutConstraint(item: sendButton, attribute: .Bottom, relatedBy: .Equal, toItem: toolBar, attribute: .Bottom, multiplier: 1, constant: -4.5)) } return toolBar } |
你會發現有一個錯誤,這是因為我們的InputTextView是一個單獨定義的類,它還沒有定義,我們在之後會對他做一些操作,目前先不用管它,不過我們先把它定義出來,在檢視控制器類之外定義該類:
1 2 3 |
class InputTextView: UITextView { } |
還有一個問題,用系統預設的程式碼實現autolayout看起來很難理解,所以這裡可以用第三方庫SnapKit來實現,把上面設定autolayout的程式碼替換成以下程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
textView.setTranslatesAutoresizingMaskIntoConstraints(false) sendButton.setTranslatesAutoresizingMaskIntoConstraints(false) textView.snp_makeConstraints({ (make) -> Void in make.left.equalTo(self.toolBar.snp_left).offset(8) make.top.equalTo(self.toolBar.snp_top).offset(7.5) make.right.equalTo(self.sendButton.snp_left).offset(-2) make.bottom.equalTo(self.toolBar.snp_bottom).offset(-8) }) sendButton.snp_makeConstraints({ (make) -> Void in make.right.equalTo(self.toolBar.snp_right) make.bottom.equalTo(self.toolBar.snp_bottom).offset(-4.5) }) |
是不是看起來簡潔多了?我們來解釋一下這段程式碼:
每一個元件都有一個 snp_makeConstraints
的閉包方法,用來設定約束,textView.snp_makeConstraints
就是來設定textView的約束
閉包中make.left.equalTo(self.toolBar.snp_left).offset(8)
這行程式碼可以用公式來表示:
也就是textView.left = self.toolBar.left + 8
,這樣一看就很直觀了,文字框的左側距輸入框左側8點。
make.top.equalTo(self.toolBar.snp_top).offset(7.5)
可以用公式
textView.top = self.toolBar.top +7.5
表示,剩下的程式碼以此類推,如下圖所示:
sendButton的部分也是如此:
make.right.equalTo(self.toolBar.snp_right)
表示傳送按鈕右側直接貼輸入框的右側,沒有位移
make.bottom.equalTo(self.toolBar.snp_bottom).offset(-4.5)
傳送按鈕底部距離輸入框底部4.5點
這樣是不是讓autoLayout變得簡單很多了?後面的專案我們就一直使用它來進行autoLayout設定了!
現在沒有錯誤了,cmd+R執行一下,啊哦,為啥是空白!作者你騙人!( ⊙ o ⊙ )
= =好吧,我們還差一步,記得嗎,它要變成第一響應者才能彈出鍵盤哦,我們要重寫一個方法它才能生效!在檢視控制器類中增加以下方法:
1 2 3 |
override func canBecomeFirstResponder() -> Bool { return true } |
告訴我們的系統我們自定義的輸入框可以成為第一響應者,我們也是有身份證的!
然後在執行一下,如果沒有錯誤,應該會有以下效果:
忽略黑洞洞的背景,因為我們還沒有新增內容。。。
但是你會發現一個問題,鍵盤怎麼回來啊。。不管怎麼點都沒有反應啊!
好吧,下面我們來用一個巧妙的辦法來解決它。由於聊天頁面是一個UITableView,所以我們可以使用UITableViewContoller來替代我們的UIViewContoller,這樣我們的頁面中就預設有了一個UITableView,然後它有一個非常實用的屬性—keyboardDismissMode
,我們把它設定為.Interactive
也就是鍵盤的彈出和收回狀態可以根據你對tableView的拖拽進行改變,也就是你的手指拖到哪裡你的鍵盤就到哪裡,是不是很酷。
改變檢視控制器的型別:
1 2 3 4 5 |
class ChatViewController:UITableViewController,UITextViewDelegate { .... .... .... } |
在viewDidLoad裡新增一行程式碼來設定keyboardDismissMode
:
1 |
tableView.keyboardDismissMode = .Interactive |
再次執行,你會發現黑洞洞的背景不見了,取而代之的是空白的TableView!而且鍵盤也實現了炫酷的效果!
文章本部分原始碼
好的,第三部分順利實現!第二部分是我們的重頭戲,內容較多,所以我把它放到教程的第二部分中。
第二部分教程已經出爐,歡迎圍觀!
swift實現一個與智慧機器人聊天的app(2)