在本教程中,我將介紹如何創建一個簡單的 iOS 聊天 App(用 swift 和 Syncano)。我們將從最基本的功能開始——發送、接收消息,將消息保存到服務器,然後再進入用戶賬號和認證等主題。
我們會在後端用到 Syncano 及其 iOS 庫,前端使用 JSQMessagesViewController 庫。
在第一部分,我們將包含創建新項目,添加 JSQMessagesViewController(然後在上面顯示一些測試消息)。在第二部分,我們將數據存到服務器並實時同步(當新消息一存到數據庫就立即顯示,不需要任何刷新操作)。最後,在第三部分,我們將增加用戶認證功能——注冊、登錄並向用戶顯示正確的信息。
實現,創建新項目。打開 Xcode,創建一個 Single View Application 項目。項目名稱輸入 SyncanoChat。語言選擇 Swift。
要使用 Syncano iOS 庫和 JSQMessagesViewController,我們必須使用 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 Swift
use_frameworks!
target 'SyncanoChat' do
pod 'syncano-ios'
pod 'JSQMessagesViewController'
end
target 'SyncanoChatTests' do
end
target 'SyncanoChatUITests' do
end
保存文件,關閉文本編輯器(我們不會再修改這個文件了),在終端中輸入:
pod install
當命令執行完成,關閉 Xcode,然後打開 Workspace 文件:
open SyncanoChat.xcworkspace
運行程序,確保沒有任何錯誤發生!
這一步不是必須的,如果你沒有取消注釋 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 代碼中使用這兩個庫了。
現在開始使用 JSQMessagesViewController。在 XCode 項目導航窗口中,打開 ViewController.swift。
目前它還是一個 UIViewController 子類,將它改成 JSQMessagesViewController子類:
import UIKit
class ViewController: JSQMessagesViewController {
//...
// rest of the class
//...
}
如果你沒有使用橋接頭文件,你可能需要在 import UIKit 下加 2 個 import 語句,如下所示:
import UIKit
import JSQMessagesViewController
import 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 - Setup
extension 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。它不會顯示任何東西,但我們用於顯示的測試消息已經准備好了。
我們必須實現幾個方法,尤其是 JSQMessagesCollectionViewDataSource 協議方法。
在 ViewController.swift 中增加一個擴展:
//MARK - Data Source
extension 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 - Toolbar
extension 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 我。