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

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

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

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

Q&A

解決済

3回答

755閲覧

collectionviewの引っ張って更新のエラー

po_tato

総合スコア97

Swift

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

0グッド

0クリップ

投稿2019/08/02 17:14

編集2019/08/03 15:45

jsonからデータを取得し、それを非同期通信で配列に入れ画面に表示させるページに、
引っ張って画面を更新する機能を実装してみたのですが、
強く下に引っ張ったり、3回のうち1回くらいのペースでクラッシュしてしまいます。
軽く引っ張るくらいで更新処理が進む場合はあまりクラッシュしないですが、
強く引っ張るとほとんどの確率でクラッシュしてしまいます。

予想外の強さで引っ張ってしまった時にcellectioViewのセルのインデックス番号に不具合か何かあるのでしょうか?
また、回避策などございますでしょうか?

ちなみに、引っ張って更新を付ける前は一度もエラーはありませんでした。

エラーコードはこちらです。

Fatal error: Index out of range

文中のprint文の表示は以下のようになります

インデックス:0 インデックス:1 インデックス:2 インデックス:3 インデックス:4 インデックス:5 インデックス:4

コード本文

import UIKit import Alamofire import AlamofireImage import SwiftyJSON class ViewController: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate{ @IBOutlet weak var collectionView: UICollectionView! //Appdelegateのインスタンス生成 var appdelegate:AppDelegate = UIApplication.shared.delegate as! AppDelegate var imageURL_box:[String] = [] var title_box:[String] = [] var publisher_box:[String] = [] var articleURL_box:[String] = [] //引っ張って更新のやつ private var refreshControl = UIRefreshControl() override func viewDidLoad() { super.viewDidLoad() //下に引っ張って更新 refreshControl = UIRefreshControl.init() refreshControl.attributedTitle = NSAttributedString(string: "引っ張って更新") refreshControl.addTarget(self, action: #selector(ViewController.refresh(sender:)), for: .valueChanged) if #available(iOS 10.0, *) { collectionView.refreshControl = refreshControl } else { // Fallback on earlier versions collectionView.addSubview(refreshControl) } } // refresh処理 @objc func refresh(sender: UIRefreshControl) { self.dateSet() DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { // 完了したらUIRefreshControlのendRefreshing()を呼ばないとくるくるが終わらない self.refreshControl.endRefreshing() } } override func viewWillAppear(_ animated: Bool) { print("viewWillAppear呼ばれた") dateSet() } //非同期通信して配列にデータ入れる func dateSet(){ self.title_box.removeAll() self.publisher_box.removeAll() self.imageURL_box.removeAll() self.articleURL_box.removeAll() let request = URLRequest(url: URL(string: "http://~.json")!, cachePolicy: .reloadIgnoringLocalCacheData, //キャッシュ残らせない timeoutInterval: 5.0) //非同期通信 Alamofire.request(request).validate().responseJSON { response in switch response.result { case .success(let value): let json = JSON(value) print(json) for i in 0 ..< json.count{ self.title_box.append(json[i]["articleTitle"].string!) self.publisher_box.append(json[i]["siteTitle"].string!) self.articleURL_box.append(json[i]["articleURL"].string!) self.imageURL_box.append(json[i]["imageURL"].string!) } } case .failure(let error): print(error) } self.collectionView.reloadData() } } //セルの個数指定するデリゲートメソッド func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return self.imageURL_box.count } //セルに値を追加するデリゲートメソッド func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell print("インデックス:(indexPath.row)") cell.news_detail_lbl.text = self.title_box[indexPath.row] cell.news_publisher_lbl.text = self.publisher_box[indexPath.row] //画像が読み込まれるまでのimage let placeholder = UIImage(named: "img1") let filter = AspectScaledToFitSizeFilter(size: CGSize(width: 375, height: 130)) //画像urlが取得出来てない,""の場合 //AlamofireImage使用 if self.imageURL_box[indexPath.row] != ""{ cell.newsImage.af_setImage(withURL: URL(string: self.imageURL_box[indexPath.row])!,placeholderImage: placeholder, filter: filter) }else{ cell.newsImage.image = UIImage(named: "img2") } return cell } } class CollectionViewCell :UICollectionViewCell{ @IBOutlet weak var newsImage: UIImageView! @IBOutlet weak var news_detail_lbl: UILabel! @IBOutlet weak var news_publisher_lbl: UILabel! }

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

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

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

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

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

guest

回答3

0

自己解決

〜解決〜
スマートな対応の仕方ではないかもしれないですが、思いっきり引っ張ってもクラッシュもせず、
データの更新もできる状態に出来たので記載致します。
とりあえず、引っ張って更新した際に各配列の要素数が0の場合はクラッシュし、成功する場合は配列へのデータ挿入が出来た場合だと思ったので、そもそもデータがある場合の時だけセルに値をセットするようにしました。
この方法だと何回か引っ張らないとデータの更新が出来ないということになりますが、そもそも軽く引っ張った場合は問題なく更新出来、問題は強く引っ張った場合にたまにクラッシュするということなので、このスマートでないやり方でもとりあえず解決ということにしておきます。

//セルに値を追加するデリゲートメソッド func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell //全ての配列に値が入っている時(このif文追加) if self.title_box.count != 0 && self.publisher_box.count != 0 && self.articleURL_box.count != 0 && self.imageURL_box.count != 0{ cell.news_detail_lbl.text = self.title_box[indexPath.row] cell.news_publisher_lbl.text = self.publisher_box[indexPath.row] //画像が読み込まれるまでのimage let placeholder = UIImage(named: "img1") let filter = AspectScaledToFitSizeFilter(size: CGSize(width: 375, height: 130)) //画像urlが取得出来てない,""の場合 //AlamofireImage使用 if self.imageURL_box[indexPath.row] != ""{ cell.newsImage.af_setImage(withURL: URL(string: self.imageURL_box[indexPath.row])!,placeholderImage: placeholder, filter: filter) }else{ cell.newsImage.image = UIImage(named: "img2") } } return cell }

投稿2019/08/03 15:45

po_tato

総合スコア97

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

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

0

本文に記述しております。

投稿2019/08/03 15:42

po_tato

総合スコア97

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

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

0

CollectionView を更新する部分をメインスレッドにしてみると結果が変わりますか?
通信は非同期で行っていいのですが,ビュー周りはメインスレッドにする必要があります。

swift

1DispatchQueue.main.async { 2 self.collectionView.reloadData() 3}

問題ないということなので
配列の要素数が一致していない可能性があります。
ブレークポイントなどを上記のコード部分に設置し各配列の要素数を確認してください!

swift

1//非同期通信 2Alamofire.request(request).validate().responseJSON { response in 3 switch response.result { 4 case .success(let value): 5 let json = JSON(value) 6 print(json) 7 for i in 0 ..< json.count{ 8 self.title_box.append(json[i]["articleTitle"].string!) 9 self.publisher_box.append(json[i]["siteTitle"].string!) 10 self.articleURL_box.append(json[i]["articleURL"].string!) 11 self.imageURL_box.append(json[i]["imageURL"].string!) 12 } 13 print(String(self.title_box.count)) 14 print(String(self.publisher_box.count)) 15 print(String(self.articleURL_box.count)) 16 print(String(self.imageURL_box.count)) 17 DispatchQueue.main.async { 18 self.collectionView.reloadData() 19 } 20 } 21 22 case .failure(let error): 23 print(error) 24 } 25}

投稿2019/08/02 18:18

編集2019/08/03 12:12
TakuyaAso

総合スコア1361

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

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

po_tato

2019/08/03 04:27

ご解答ありがとうございます。残念ながら結果は変わらず、同じエラーでクラッシュします。 ちなみにprint文の下の行の cell.news_detail_lbl.text = self.title_box[indexPath.row] がそのエラー箇所になります。
TakuyaAso

2019/08/03 04:54

通信が終わった後のそれぞれの配列の要素数は一致しているか確認お願いします。エラー内容から imageURL_box の要素数が title_box の要素数より多い可能性があります。 imageURL_box title_box publisher_box articleURL_box
po_tato

2019/08/03 06:30

全て要素数は0と表示されます。removeall()とself.collectionView.reloadData()のタイミングか何かなのでしょうか?
po_tato

2019/08/03 14:16

指定の位置にコードを追加し試すと、全て要素数は120と表示されます。120はjsonの全部の数なので合っています。ちなみに全て要素数は0と表示されますと記載しましたが、それはクラッシュした際の、cellForItemAtメソッドの中にカウント数のプリント文を記載し試した結果です。
po_tato

2019/08/03 15:43

解決方法本文に追記致しました。貴重なアドバイスありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問