質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

3374閲覧

[Swift] UIImageを持つTableViewCellの非同期表示

morizoo-

総合スコア65

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

1クリップ

投稿2017/01/09 08:45

###実現したいこと
画像を持つUITableViewのCellをキャッシュを利用して非同期に表示させたいのですが、キャッシュが働いてくれなくて困っています。
開発環境は XCode(8.2.1)、 Swift3.0 です

###発生している問題
DBから取得した画像(UIImage)を画像名文字列(NSString)をKeyとして受け取るNSCacheのサブクラス(ImageCache)を作成したのですが、ImageCache#setObject(AnyObject, forKey: AnyObject)でセット出来ていないのか、ImageCache#object(forKey: AnyObject)の返値がnilとなってしまいます(デバッガで確認済み)。
TableViewの表示自体に問題はなく、キャッシュが動いていないだけです。エラー文は出ていません。

###該当のソースコード

Swift

1struct ResultData { 2 let name : String! 3 let imgStr : String! 4} 5 6//メインスレッド 7class ResultTableViewController: UITableViewController { 8 9 var cellArray : [ResultData] = [] 10 11 override func viewDidLoad() { 12 super.viewDidLoad() 13 14 let query : NCMBQuery = NCMBQuery.init(className: "ImageTest") 15 query.whereKey("Category", equalTo: "TestButton") 16 query.findObjectsInBackground({(/*NSArray*/ objects, /*NSError*/ error) in 17 if error != nil { 18 print(error.debugDescription) 19 } else { 20 for object in objects! { 21 self.cellArray.append( 22 ResultData(name: (object as! NCMBObject).object(forKey: "Name") as! String, 23 imgStr: (object as! NCMBObject).object(forKey: "Image") as! String)) 24 } 25 self.tableView.reloadData() 26 } 27 }) 28 } 29 30 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 31 return self.cellArray.count 32 } 33 34 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 35 let cell = tableView.dequeueReusableCell(withIdentifier: "Results")! as! CustomCell 36 cell.result = cellArray[indexPath.row] 37 return cell 38 } 39}

Swift

1//画像付きカスタムUITableViewCell 2class CustomCell: UITableViewCell { 3 4 @IBOutlet weak var thumbnail: UIImageView! 5 @IBOutlet weak var name: UILabel! 6 7 //イテレーション 8 var generation: Int = 0 9 10 override func prepareForReuse() { 11 super.prepareForReuse() 12 self.generation += 1 13 self.result = nil 14 } 15 16 var result: ResultData? { 17 didSet { 18 self.name.text = result?.name 19 self.thumbnail.image = nil 20 self.thumbnail.alpha = 0 21 if let result = self.result { 22 let generation = self.generation 23 weak var wcell = self 24 25 let cache = ImageCache() 26 //画像文字列で検索してImageCache内に画像があったら表示、無かったらDBから取得して表示 27 if let img = cache.object(forKey: result.imgStr as AnyObject) { 28 print("got from ImageCache!") 29 wcell?.thumbnail.image = img as? UIImage 30 wcell?.thumbnail.alpha = 0 31 UIView.animate(withDuration: 0.25, animations: {() -> Void in 32 wcell?.thumbnail.alpha = 1 33 }) 34 } else { 35 let file = NCMBFile.file(withName: result.imgStr, data: nil) as! NCMBFile 36 file.getDataInBackground({(/*NSData*/ data, /*NSError*/ error) in 37 if error != nil { 38 print(error.debugDescription) 39 } else if let cell = wcell, let image = UIImage(data: data!), cell.generation == generation { 40 cache.setObject(image, forKey: result.imgStr as AnyObject) 41 cell.thumbnail.image = image 42 cell.thumbnail.alpha = 0 43 UIView.animate(withDuration: 0.25, animations: {() -> Void in 44 cell.thumbnail.alpha = 1 45 }) 46 } 47 }) 48 } 49 } 50 } 51 } 52}

Swift

1//画像を保持するNSCacheのサブクラス 2class ImageCache : NSCache<AnyObject, AnyObject> { 3 //シングルトン 4 static let sharedInstance = ImageCache() 5}

###試したこと
最初はキャッシュのライブラリについて調べたのですが、どれもURLから画像を取得し、キャッシュする仕様のものばかりでした。
僕のやりたいことはDB(NCMB)から画像を取得し、単純に画像名文字列をKeyとしてUIImageをキャッシュに格納するというものなので、URLは使いません。
一応HanekeCache#setImage()#fetchImage()を試してみましたが、思う様に動かなかったため、自分でキャッシュクラスを作成して実装してみることにしました。

###知りたいこと
・バグの解決方法(NSCacheの扱い方?tableViewCellの仕様?)
・(あったら)URLを扱わない、このコードの仕様に合うキャッシュライブラリ
どなたかご教授願います。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

swift

1let cache = ImageCache()

じゃなくて、

swift

1let cache = ImageCache.sharedInstance

ですね。
今のコードだと毎回違うインスタンスが生成されてしまいますのでキャッシュされません。

おまけ

Singleton側を、

swift

1final class ImageCache : NSCache<AnyObject, AnyObject> { 2 static let sharedInstance = ImageCache() 3 private init() { 4 } 5}

とすると、

  • finalで継承禁止。
  • private init()の追加で、外部からのインスタンス生成禁止。(ImageCache()でエラーになる)

となります。

投稿2017/01/10 00:47

fuzzball

総合スコア16731

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

morizoo-

2017/01/10 03:58

実装出来ました! Singletonの扱い方が間違っていたのですね、勉強になります。 ご回答ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問