iOS

JSQMessagesViewControllerで画像を非同期でダウンロードする

More than 3 years have passed since last update.

JSQMessagesViewControllerでは、画像も簡単に吹き出しの中に表示することができます。しかしその画像はあらかじめ用意されている必要があるため、ネットワークの画像を表示しようとする時には非同期で画像を読み込んだ後に再描画をする必要があります。


完成イメージ

JSQMessage2Sample.gif


画像を表示する発言の作成

JSQMessagesViewControllerでは、種類によらず全ての発言をJSQMessageクラスで表現します。このクラスのインスタンスをcollectionView(collectionView: JSQMessagesCollectionView!, messageDataForItemAtIndexPath indexPath: NSIndexPath!)関数の返り値とすることで、画面に吹き出しが表示されます。

この吹き出しをJSQMessageクラスを画像の発言とするためには、次のようなコードでJSQMessageクラスをインスンタン化します。

// photoItem.imageに設定した画像が吹き出しに表示される

let photoItem = JSQPhotoMediaItem()
photoItem.image = UIImage(name: "SampleImage")
JSQMessage(senderId: "1", displayName: "user1", media: photoItem)


読み込み中の吹き出しにする

画像をまだ読み込んでいない時には、読み込み中の吹き出しにします(完成イメージでは、丸いローディングアイコンが回っています)。これはJSQPhotoMediaItem.image=nilとすれば、JSQMessagesViewControllerが自動で行ってくれます。

// photoItem.imageがnilなので、自動で読み込み中の吹き出しとなる

let photoItem = JSQPhotoMediaItem()
JSQMessage(senderId: "1", displayName: "user1", media: photoItem)


画像を非同期で読み込んだ後に再表示する

画像を非同期で読み込むため、ここではGCD(Grand Central Dispatch)を使います。

まず、urlを渡すとJSQPhotoMediaItemインスタンスを返すcreatePhotoItem関数を作成しました。このメソッドは呼び出した時には、常に読み込み中の画像を返します(コメント 1.)。

呼び出したタイミングで、urlに指定した画像をGCDを使ってバックグラウンドで読み込みます。読み込みが完了すると、createPhotoItemが返したインスタンスのimageプロパティを更新したのちに、メインキューでcollectionView.reloadDataを呼び出して画面を再描画します(コメント 2.)

    private func createPhotoItem(url: String, isOutgoing: Bool) -> JSQPhotoMediaItem {

print("creating photo item url = " + url)
let photoItem = JSQPhotoMediaItem()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
photoItem.image = self.createImage(url)
dispatch_async(dispatch_get_main_queue(), {
// 2. photoItem.imageにネットワークから読み込まれた画像が設定されている
self.collectionView?.reloadData()
})
})
photoItem.appliesMediaViewMaskAsOutgoing = isOutgoing
// 1. この時点ではphotoItem.image=nilなので、ローティングとなる
return photoItem
}

private func createImage(url: String) -> UIImage {
print("creating image url = " + url)
guard let nsurl = NSURL(string: url) else {
return UIImage(named: "noimage")!
}
guard let data = NSData(contentsOfURL: nsurl) else {
return UIImage(named: "noimage")!
}
guard let image = UIImage(data: data) else {
return UIImage(named: "noimage")!
}
return image
}


補足

createPhotoItem関数は、分かりやすさを重視してシンプルに書いています。実際にはメモリーやローカルDBなどに画像をキャッシュしておき、よりネットワークアクセスを減らす工夫などをした方が良いと思います。


サンプルコード

https://github.com/nakaken0629/JSQMessages2Sample