CollectionViewにURLから画像を表示
解決済
回答 2
投稿
- 評価
- クリップ 0
- VIEW 1,837
なんどもすみません。。
FirebaseRDBに保存した画像のURLをCollectionViewに表示する処理を書いています。
以下のコードで起動してもcollectionviewには表示されませんでした。
RDBからURLの取得自体ができていないのかと思い、print(画像のURL)で確認してみたのですが、URLは取得できていました。
残るは原因はcellForItemAtのところくらいだと思うのですが自分には何が原因なのかわかりませんでした。
import UIKit
import Firebase
import SDWebImage
class PostList: UICollectionViewController {
var posts = [NSDictionary]()
override func viewDidLoad() {
super.viewDidLoad()
//ログインチェック
if UserDefaults.standard.object(forKey: "check") != nil {
//何もしない
} else {
let loginViewController = self.storyboard?.instantiateViewController(withIdentifier: "login")
self.present(loginViewController!, animated: true, completion: nil)
}
loadAllData()
collectionView?.reloadData()
}
@IBAction func loginOrOut(_ sender: Any) {
performSegue(withIdentifier: "goLogin", sender: nil)
}
//RDBにある投稿画像の参照(URL)を取ってくる
func loadAllData() {
let RDBRef = Database.database().reference().child("posts")
RDBRef.queryLimited(toLast: 10).observe(DataEventType.childAdded, with: { (snapshot) in
var tempPosts = [NSDictionary]()
for post in(snapshot.children) {
let child = post as! DataSnapshot
let dict = child.value
tempPosts.append(dict as! NSDictionary)
print(tempPosts)
}
self.posts = tempPosts
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return posts.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
let dict = posts[(indexPath as IndexPath).row]
//投稿画像(postimageを表示)
let postImageView = cell.contentView.viewWithTag(1) as! UIImageView
let postStr = dict["postimage"] as? String
let postUrl = URL(string: postStr!)
postImageView.sd_setImage(with: postUrl)
//let iconImageView = cell.contentView.viewWithTag(2) as! UIImageView
//let nameLabel = cell.contentView.viewWithTag(3) as! UIImageView
return cell
}
}
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+1
collectionView?.reloadData()
のタイミングの問題が気になりました。
上記の質問のコードではおそらく非同期処理にてデータベースにデータを問い合わせ、実際にデータが返ってくる前にcollectionView?.reloadData()が実施されているように見受けられます。
RDBRef.queryLimited(toLast: 10).observe(DataEventType.childAdded, with: { (snapshot) in
var tempPosts = [NSDictionary]()
for post in(snapshot.children) {
let child = post as! DataSnapshot
let dict = child.value
tempPosts.append(dict as! NSDictionary)
print(tempPosts)
}
self.posts = tempPosts
self.collectionView?.reloadData()
})
このようにメソッドの完了時クロージャ内でデータをpostsにセットした後にreloadData()したらどうなりますでしょうか?
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
516kさんのご回答の内容で動作はすると思いますので、1点だけ補足します。
ご質問のコードではメモリリークを起こしてしまっています。
Firebase RDBのデータ監視リスナーはデタッチしなければイベントがずっと送られてくる状態になってしまいますので、 viewDidUnload
あたりでデタッチするようにすべきです。
参考: 公式ドキュメント
// コード例
let handle = RDBRef
.queryLimited(toLast: 10)
.observe(...) { ... }
...
override func viewDidUnload() {
super.viewDidUnload()
// リスナーのデタッチ
RDBRef.removeObserver(withHandle: handle)
}
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.34%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2018/04/05 17:46
2018/04/07 17:31
回答ありがとうございます!
非同期処理でデータが帰ってくる前にcollectionView?.reloadData()が呼ばれていたのですね、、
それを防ぐためにクロージャ内でreloadData()を呼ぶ必要があったのですね!
515k様にお教えいただいた様に、メソッドの完了時クロージャ内でデータをpostsにセットした後にreloadData()をすることでcollectionviewに表示される様になりました!
ありがとうございますm(_ _)m
もう一つお聞きしたいのですが、投稿画像は違うものを複数投稿していたはずなのですが表示される画像は全部同じものになっていました。。
postsに入れるURLが全部同じになってしまっているか、cellForItemAt内で最初のURLだけを何個も表示しているのかだと思ったのですが、自分にはどちらも異常ない様に見えてしまいます、、
質問のコードから516k様の回答のご指摘部分以外は変えていないのですが、もしよろしければそちらの方もご覧いただけると幸いです。
2018/04/07 17:41
のIndexPathのキャストは何の意味がありますかね?ひょっとしたらここで毎回ゼロが返ってきてるかもしれません。
2018/04/07 18:01
let dict = posts[indexPath.row]
記述したらなぜか画面が真っ白になって、いろいろ試しているうちにキャストをして普通に画面が表示される様になったのですが、
今、キャストを解いて
let dict = posts[indexPath.row]
で起動してみたのですが今度はちゃんと表示されました、、笑
多分違う部分の修正の効果をそこの効果と勘違いしてその様に記述していたものと思われます、、
let dict = posts[indexPath.row]
に直して起動してみた結果なのですが、先ほどと同じく同じ画像がずらっと並んでいました、、
2018/04/07 18:14 編集
let dict = posts[indexPath.row]に直して起動してみた結果はやはり同じ画像がずらっと表示されていました。
ということです!
2018/04/07 21:12
2018/04/07 23:01
posts配列の中身が同じになっているのかなとも思ったのですが、print()で確認してURLが違うことを確認していますし、それもないですよね、、
2018/04/07 23:19
let dict = posts[(indexPath as IndexPath).row]
と
let dict = posts[indexPath.item]
の結果が同じなのですか?
その場合
let postUrl = URL(string: postStr!)
print(postUrl)
の表示結果も全く同じでしょうか。
2018/04/08 15:07
と
let dict = posts[indexPath.item]
の結果は同じでした。
print(postUrl)の表示結果は異なるURLがpostsの数分だけコンソールに表示されました。
そうですね、、申し訳ありません。
質問を立て直してみます。
最後までありがとうございますm(_ _)m