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

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

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

TableView(UITableView)とは、リスト形式で表示するコントロールで、ほとんどのアプリに使用されています。画面を「行」に分けて管理し、一般的には各行をタップした際に詳細画面に移動します。

iOS

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

Swift

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

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

解決済

1回答

3102閲覧

Swiftで通信後TableViewCellに画像表示できない

oOz

総合スコア18

TableView

TableView(UITableView)とは、リスト形式で表示するコントロールで、ほとんどのアプリに使用されています。画面を「行」に分けて管理し、一般的には各行をタップした際に詳細画面に移動します。

iOS

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

Swift

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

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

0グッド

0クリップ

投稿2018/10/28 06:10

現在、このような形でqiitaのAPIにアクセスしています。
取得したiconのurlからイメージを取得してcellに表示しようとしていますが、イメージ画像が表示されません。
cellをタップすると一時的に表示はされるのでイメージは取得できています。
iconのurlからイメージ取得後にtableViewがリロードできていないんだと思うのですが、どのタイミングでリロードすればいいのかわかりません。
またイメージを表示するために良い方法があればご教示いただければ助かります。

swift

1import UIKit 2 3struct Article: Codable { 4 var title: String 5 var user: User 6 struct User: Codable { 7 var id: String 8 var iconUrl: String 9 enum CodingKeys: String, CodingKey { 10 case id = "id" 11 case iconUrl = "profile_image_url" 12 } 13 } 14 var url: String 15 var tags: [Tag] 16 struct Tag: Codable { 17 var name: String 18 } 19} 20 21 22struct Qiita { 23 24 static func fetchArticle(completion: @escaping ([Article]) -> Swift.Void) { 25 26 let url = "https://qiita.com/api/v2/items" 27 28 guard var urlComponents = URLComponents(string: url) else { 29 return 30 } 31 32 urlComponents.queryItems = [ 33 URLQueryItem(name: "per_page", value: "50"), 34 ] 35 36 let task = URLSession.shared.dataTask(with: urlComponents.url!) { data, response, error in 37 38 guard let jsonData = data else { 39 return 40 } 41 42 do { 43 let articles = try JSONDecoder().decode([Article].self, from: jsonData) 44 completion(articles) 45 } catch { 46 print(error.localizedDescription) 47 } 48 } 49 task.resume() 50 } 51} 52 53class ViewController: UIViewController { 54 55 private var tableView = UITableView() 56 fileprivate var articles: [Article] = [] 57 58 override func viewDidLoad() { 59 super.viewDidLoad() 60 title = "最新記事" 61 62 setUpTableView: do { 63 tableView.frame = view.frame 64 tableView.dataSource = self 65 tableView.delegate = self 66 view.addSubview(tableView) 67 } 68 69 Qiita.fetchArticle(completion: { (articles) in 70 self.articles = articles 71 DispatchQueue.main.async { 72 self.tableView.reloadData() 73 } 74 }) 75 } 76} 77 78extension ViewController: UITableViewDataSource { 79 80 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 81 82 let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell") 83 84 let article = articles[indexPath.row] 85 cell.textLabel?.text = article.title 86 cell.detailTextLabel?.text = article.user.id 87 cell.imageView?.downloadImage(from: article.user.iconUrl) 88 89 return cell 90 } 91 92 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 93 return articles.count 94 } 95} 96 97extension UIImageView { 98 99 func downloadImage(from url: String) { 100 101 let urlRequest = URLRequest(url: URL(string: url)!) 102 103 let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in 104 105 if error != nil { 106 print(error as Any) 107 return 108 } 109 110 DispatchQueue.main.async { 111 self.image = UIImage(data: data!) 112 } 113 } 114 task.resume() 115 } 116} 117 118extension ViewController: UITableViewDelegate { 119 120 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 121 122 let article = articles[indexPath.row] 123 let articleURL = URL(string: article.url) 124 openWithSafari(url: articleURL) 125 } 126 127 func openWithSafari(url: URL?) { 128 guard let openUrl = url else { 129 print("無効なURLです") 130 return 131 } 132 133 if UIApplication.shared.canOpenURL(openUrl as URL) { 134 print(openUrl) 135 UIApplication.shared.open(openUrl) 136 } 137 } 138} 139

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

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

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

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

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

guest

回答1

0

ベストアンサー

若干難しいですね。
これで動きます。

swift

1 2import UIKit 3 4struct Article: Codable { 5 var title: String 6 var user: User 7 struct User: Codable { 8 var id: String 9 var iconUrl: String 10 var iconImage:UIImage? 11 enum CodingKeys: String, CodingKey { 12 case id = "id" 13 case iconUrl = "profile_image_url" 14 } 15 } 16 var url: String 17 var tags: [Tag] 18 struct Tag: Codable { 19 var name: String 20 } 21} 22 23 24struct Qiita { 25 26 static func fetchArticle(completion: @escaping ([Article]) -> Swift.Void) { 27 28 let url = "https://qiita.com/api/v2/items" 29 30 guard var urlComponents = URLComponents(string: url) else { 31 return 32 } 33 34 urlComponents.queryItems = [ 35 URLQueryItem(name: "per_page", value: "50"), 36 ] 37 38 let task = URLSession.shared.dataTask(with: urlComponents.url!) { data, response, error in 39 40 guard let jsonData = data else { 41 return 42 } 43 44 do { 45 let articles = try JSONDecoder().decode([Article].self, from: jsonData) 46 completion(articles) 47 } catch { 48 print(error.localizedDescription) 49 } 50 } 51 task.resume() 52 } 53} 54 55class ViewController: UIViewController { 56 57 private var tableView = UITableView() 58 fileprivate var articles: [Article] = [] 59 60 override func viewDidLoad() { 61 super.viewDidLoad() 62 title = "最新記事" 63 64 setUpTableView: do { 65 tableView.frame = view.frame 66 tableView.dataSource = self 67 tableView.delegate = self 68 view.addSubview(tableView) 69 } 70 71 Qiita.fetchArticle(completion: { (articles) in 72 self.articles = articles 73 DispatchQueue.main.async { 74 self.tableView.reloadData() 75 } 76 }) 77 } 78} 79 80extension ViewController: UITableViewDataSource { 81 82 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 83 84 let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell") 85 86 let article = articles[indexPath.row] 87 cell.textLabel?.text = article.title 88 cell.detailTextLabel?.text = article.user.id 89 90 if let iconImage = article.user.iconImage { 91 cell.imageView?.image = iconImage 92 } 93 else { 94 let placeHolder = UIImage.from(color: UIColor.lightGray, size: CGSize(width: 100, height: 100)) 95 cell.imageView?.image = placeHolder 96 97 cell.imageView?.downloadImage(from: article.user.iconUrl, completion: { (image) in 98 DispatchQueue.main.async { 99 self.articles[indexPath.row].user.iconImage = image 100 if let currentCell = tableView.cellForRow(at: indexPath) { 101 currentCell.imageView?.image = image 102 } 103 } 104 }) 105 } 106 107 return cell 108 } 109 110 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 111 return articles.count 112 } 113} 114 115extension UIImageView { 116 117 func downloadImage(from url: String, completion:@escaping ((_ image:UIImage?) -> Void)) { 118 119 let urlRequest = URLRequest(url: URL(string: url)!) 120 121 let task = URLSession.shared.dataTask(with: urlRequest) { (data,response,error) in 122 123 if error != nil { 124 print(error as Any) 125 return 126 } 127 128 let image = UIImage(data: data!) 129 completion(image) 130 } 131 task.resume() 132 } 133} 134 135extension ViewController: UITableViewDelegate { 136 137 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 138 139 let article = articles[indexPath.row] 140 let articleURL = URL(string: article.url) 141 openWithSafari(url: articleURL) 142 } 143 144 func openWithSafari(url: URL?) { 145 guard let openUrl = url else { 146 print("無効なURLです") 147 return 148 } 149 150 if UIApplication.shared.canOpenURL(openUrl as URL) { 151 print(openUrl) 152 UIApplication.shared.open(openUrl) 153 } 154 } 155} 156 157 158 159extension UIImage { 160 static func from(color: UIColor, size:CGSize) -> UIImage { 161 let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) 162 UIGraphicsBeginImageContext(rect.size) 163 let context = UIGraphicsGetCurrentContext() 164 context!.setFillColor(color.cgColor) 165 context!.fill(rect) 166 let img = UIGraphicsGetImageFromCurrentImageContext() 167 UIGraphicsEndImageContext() 168 return img! 169 } 170} 171

問題はいくつかあります。
・デフォルトのcellの仕様で、最初にcell.ImageViewをセットしないとImageViewがないことになるようなのでplaceholderをセットしておかなければならない
→別解:カスタムセルを使う
・cell内の要素に非同期でアクセスする際は、cellがreuseされている可能性を考慮しなければならない
・cellは使い回されるので、imageは保持しておかなければならない

ちなみにcell内のimageの管理はとても難しいので大抵はライブラリを使います
ライブラリの大きなポイントは画像をキャッシュするところです
urlに対して画像を保持しているので、画像を保持しておく必要がありません

投稿2018/10/28 11:36

kosanai

総合スコア471

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

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

oOz

2018/10/28 13:00

おお、動きました、有難うございます。 placeholderをセットしなくてはいけなかったのですね。 原因がいまいちわかっていませんでした。cellの使い回しについてもう一度勉強し直します。あと、cell内でのイメージの管理についても、、、 問題点まであげてくださり有難うございます。 一点、質問です。 ・デフォルトのcellの仕様で、最初にcell.ImageViewをセットしないとImageViewがないことになるようなのでplaceholderをセットしておかなければならない →これはストーリーボードでtableViewを用意することで解決できるという認識でよろしいでしょうか?
kosanai

2018/10/28 13:08

そうです UITableVIewとUITableVIewCellをViewController内にセットするか xibでUITableViewCellを作って、tableViewにregisterするかです (なおデフォルトのセルについてはちょっと自信がないです。ほぼ使わないのでナレッジがなく。imageViewを最初にセットした時点で内部で何かが決まってるんだと思います。試しにplaceholderを1ピクセルで作ったらうまくいきませんでした)
oOz

2018/10/28 13:31

回答有難うございます。 確かにデフォルトのセルを使うことってないですよね。自分も今回初めて使いました。 本当に勉強になりました。 もう少し色々試してみます。 色々腑に落ちました。自分ではここまで来ることはなかったので本当に感謝しております。 有難うございました。  また機会がございましたら、よろしくお願い致します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問