用 JSQMessagesViewController 建立一個 iOS 聊天 App - 第 1 部分
在本教程中,我將介紹如何建立一個簡單的 iOS 聊天 App(用 swift 和 Syncano)。我們將從最基本的功能開始——發送、接收訊息,將訊息儲存到伺服器,然後再進入使用者帳號和認證等主題。
我們會在後端用到 Syncano 及其 iOS 庫,前端使用 JSQMessagesViewController 庫。
在第一部分,我們將包含建立新項目,添加 JSQMessagesViewController(然後在上面顯示一些測試訊息)。在第二部分,我們將資料存到伺服器並即時同步(當新訊息一存到資料庫就立即顯示,不需要任何重新整理操作)。最後,在第三部分,我們將增加使用者認證功能——註冊、登入並向使用者顯示正確的資訊。
建立新項目
實現,建立新項目。開啟 Xcode,建立一個 Single View Application 項目。項目名稱輸入 SyncanoChat。語言選擇 Swift。
安裝 CocoaPods
要使用 Syncano iOS 庫和 JSQMessagesViewController,我們必須使用 CocoaPods。
安裝 CocoaPods
如果你沒有安裝過 CocoaPods,需要在終端下安裝它:
sudo gem install cocoapods
輸入密碼,斷行符號,等待安裝完成。
匯入庫
在終端程式下,進入剛剛建立的項目目錄,例如:
cd ~/path/to/my/project/SyncanoChat
初始化 Cocoapods:
pod init
編輯 Podfile 檔案:
open Podfile
在檔案中匯入所需庫(在 target ‘SyncanoChat’ do 和 end 之間),然後取消注釋 use_frameworks 一行,或者直接替換檔案內容為:
# Uncomment this line to define a global platform for your project# platform :ios, '8.0'# Uncomment this line if you're using Swiftuse_frameworks!target 'SyncanoChat' dopod 'syncano-ios'pod 'JSQMessagesViewController'endtarget 'SyncanoChatTests' doendtarget 'SyncanoChatUITests' doend
儲存檔案,關閉文字編輯器(我們不會再修改這個檔案了),在終端中輸入:
pod install
當命令執行完成,關閉 Xcode,然後開啟 Workspace 檔案:
open SyncanoChat.xcworkspace
運行程式,確保沒有任何錯誤發生!
添加 Objective-C 橋接標頭檔
這一步不是必須的,如果你沒有取消注釋 use_frameworks! 一行的話。如果是這樣的話,跳到下一步。
如果因為某種原因,你無法使用 Cocoapods 的 use_frameworks! 特性(動態庫)或者 你寧願使用靜態庫,則你必須添加橋接標頭檔。
使用菜單:
File -> New -> File
選擇 Cocoa class,點擊 Next,隨便輸入一個類名(我們不會用到這個類),比如 Test,將語言修改為 Objective-C,然後點 Next。
當問到是否建立一個 Objective_C bridging header 時,選擇 Yes。
這會在預設位置建立2個檔案。然後在 Xcode 中選擇這個檔案並刪除它。
當問到是否將2個檔案放到垃圾桶時,選擇 Move to Trash。
注意在建立類檔案的同時,Xcode 也會建立一個 SyncanoChat-Bridging-Header.h 的橋接標頭檔。開啟這個檔案,加入2行:
#import #import
儲存檔案,再次編譯項目已確認編譯通過。現在,你可以在 Swift 代碼中使用這兩個庫了。
Message Controller子類化 JSQMessagesViewController
現在開始使用 JSQMessagesViewController。在 XCode 項目導航視窗中,開啟 ViewController.swift。
目前它還是一個 UIViewController 子類,將它改成 JSQMessagesViewController子類:
import UIKitclass ViewController: JSQMessagesViewController {//...// rest of the class//...}
如果你沒有使用橋接標頭檔,你可能需要在 import UIKit 下加 2 個 import 語句,如下所示:
import UIKitimport JSQMessagesViewControllerimport syncano_ios
編譯項目,確認 Xcode 能串連到所有檔案並能夠識別出 JSQMessagesViewController 類。
添加屬性
現在加幾個變數,用於儲存訊息資料和 UI 組件:
messages:用於存放接收訊息和發出的訊息。因為每當我們發送或接收到一條訊息時,messages 中都會添加這條訊息,因此它是可變的。 incomingBubble:JSQMessagesViewController 會用到它,用於定義接收訊息的背景圖片,我們不會改變它,因此它定義為常量。 outgoingBubble:JSQMessagesViewController 會用到它,用於定義已發出的訊息的背景圖片,我們不會改變它,因此它定義為常量。
增加上述屬性:
let incomingBubble = JSQMessagesBubbleImageFactory().incomingMessagesBubbleImageWithColor(UIColor(red: 10/255, green: 180/255, blue: 230/255, alpha: 1.0)) let outgoingBubble = JSQMessagesBubbleImageFactory().outgoingMessagesBubbleImageWithColor(UIColor.lightGrayColor()) var messages = [JSQMessage]()
添加測試訊息
在我們引入 Syncano 之前,我們加一些測試資料用於顯示。
在 ViewController 中新增一個方法:
func reloadMessagesView() { self.collectionView?.reloadData()}
這個方法用於重新整理訊息列表,我們將載入列表封裝為單獨的方法。
在 ViewController.swift 加入一個擴充:
//MARK - Setupextension ViewController { func addDemoMessages() { for i in 1...10 { let sender = (i%2 == 0) ? "Server" : self.senderId let messageContent = "Message nr. \(i)" let message = JSQMessage(senderId: sender, displayName: sender, text: messageContent) self.messages += [message] } self.reloadMessagesView() } func setup() { self.senderId = UIDevice.currentDevice().identifierForVendor?.UUIDString self.senderDisplayName = UIDevice.currentDevice().identifierForVendor?.UUIDString }}
setup 方法用於將裝置唯一 ID 指定給 senderID 和 senderDisplayName —— 二者都是 JSQMessagesViewController 所必須的,用於區分訊息是誰所發以及發給誰。
在 viewDidLoad 方法中調用這兩個方法:
override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. self.setup() self.addDemoMessages() }
運行 App。它不會顯示任何東西,但我們用於顯示的測試訊息已經準備好了。
實現 JSQMessagesViewController 協議
我們必須實現幾個方法,尤其是 JSQMessagesCollectionViewDataSource 協議方法。
在 ViewController.swift 中增加一個擴充:
//MARK - Data Sourceextension ViewController { override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.messages.count } override func collectionView(collectionView: JSQMessagesCollectionView!, messageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageData! { let data = self.messages[indexPath.row] return data } override func collectionView(collectionView: JSQMessagesCollectionView!, didDeleteMessageAtIndexPath indexPath: NSIndexPath!) { self.messages.removeAtIndex(indexPath.row) } override func collectionView(collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageBubbleImageDataSource! { let data = messages[indexPath.row] switch(data.senderId) { case self.senderId: return self.outgoingBubble default: return self.incomingBubble } } override func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource! { return nil }}
這些方法分別用於告訴 JSQMessagesViewController:
- 有多少條訊息要顯示(即 messages 數組中的訊息條數)- 每一行都分別顯示哪條訊息- 當一條訊息被刪除時做些什麼(從 messages 數組中移除)- 每一條訊息顯示什麼樣的氣泡(如果是我們發出的訊息顯示表示“發出訊息”的泡泡圖片,否則顯示表示“接收訊息”的泡泡圖片)- 頭像上要顯示什麼(這裡返回的是 Nil,也就是說不顯示頭像)運行 App,現在它會顯示測試訊息了!如果點擊工具列中的按鈕,會導致 App 崩潰。### 處理按鈕事件要解決 App 崩潰的問題,必須實現兩個方法,一個用於處理左邊的媒體按鈕事件,一個用於處理右邊的發送按鈕事件。在 ViewController.swift 中添加一個擴充:```swift//MARK - Toolbarextension ViewController { override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: NSDate!) { let message = JSQMessage(senderId: senderId, senderDisplayName: senderDisplayName, date: date, text: text) self.messages += [message] self.finishSendingMessage() } override func didPressAccessoryButton(sender: UIButton!) { }}
當點擊發送按鈕時,我們將輸入內容添加到 messages 並重新整理 UI。而在 didPressAccessoryButton(_) 方法中,我們什麼也不幹,只是讓 App 不再崩潰而已。
總結
你已經完成了一個聊天 App 的所有介面,可以發送訊息或看到別人發送的訊息了。
在第二部分,我們繼續學習和 Syncano 打交道,包括儲存訊息到伺服器,以及訊息的即時同步。
你可以在 GitHub 上下載這部分的原始碼。
如果你有任何問題,請 tweet 我。