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

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

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

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

Swift

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

Q&A

2回答

534閲覧

UITableViewのスクロールが一番下まで行った時にデータを表示する際

po_tato

総合スコア97

TableView

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

Swift

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

0グッド

1クリップ

投稿2018/06/13 09:34

編集2022/01/12 10:55

UITableViewに、
この画像のように、
横長画像の上に日付が上に被せて配置してあるように作成しているのですが、
最初に表示されているもの(この場合5つ)は不具合がありません。ですが、一番下まで行った際に追加のデータをスクロールして下に見に行けるようになっていて、その際の横長画像の順序がバラバラになってしまいます。日付は正常な順番で出力されます。日付と横長画像はapiにリクエストして取得しており、返ってくるjsonの値は何も問題がありません。ちなみにapiは10セットずつ情報を返してくれて(日付と横長画像で1セット)、リクエストパラメータの"page"の値を増やすことでその次の10セットを取得しています。下にソース全文を添付致しますが、何がおかしいのでしょうか?さっぱりわかりません。

コード中のAlamofire.requestの部分でimageをリサイズしており、
foreachの中でそのような処理をしているので、間に合わなくなり、早く終わったものから
配列に入ってしまっているのかとか考えましたが、よくわかりませんでした。

試したことは、foreachのindexの順番を調べると、[1,3,2,4,5...]と順番がおかしくなる部分があったので、また別の配列を用意してそれにindexを正しい順番でソートしたものを格納し、
最後にそれでwideImageを並び替えるといったことも試しましたが、ダメでした。

何か教えていただけますと幸いでございます。

import UIKit import Firebase import Alamofire import AlamofireImage import SwiftyJSON class TestViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,CreateDatadelegate,UIWebViewDelegate { @IBOutlet var table: UITableView! var wideImage:[UIImage] = [] //横長画像 var date:[String] = [] //日時 var appDelegate:AppDelegate = UIApplication.shared.delegate as! AppDelegate let sw = DeviceSize.screenWidth() let sh = DeviceSize.screenHeight() var scrollcount:Int = 1 var loadingflg = true override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. table.delegate = self table.dataSource = self self.table.backgroundColor = UIColor.clear self.table.estimatedRowHeight = 200.0 self.table.rowHeight = UITableViewAutomaticDimension //self.table.reloadData() } func complete(_ param: [String : String]) { let ud = UserDefaults.standard let id = ud.string(forKey: "id") let pass = ud.string(forKey: "pass") let param = ["id":id!,"passwd":pass!,"page":"1"] self.scrollcount = 1 self.loadingflg = true //wideImage Alamofire.request("http://testapi~.php",method: .post, parameters: param).responseJSON { response in guard let object = response.result.value else{ return } let jsons = JSON(object) self.date = [] self.wideImage = [UIImage](repeating: UIImage(), count: jsons["list"].count) jsons["list"].forEach { (index, img_path) in self.date.append(img_path["date"].string!) Alamofire.request(img_path["url"].string!).responseImage { response in debugPrint(response.result) if let image = response.result.value { let imgratio:CGFloat = (image.size.height)/(image.size.width) let iw = CGFloat(self.sw) let resize = image.ResizeÜIImage(width: iw, height: iw*imgratio) self.wideImage.remove(at: Int(index)!) self.wideImage.insert(resize!, at: Int(index)!) } if self.wideImage.count == jsons["list"].count { print("wideImagecount(self.wideImage.count)") self.table.reloadData() } } } }//wideImage } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func viewDidLayoutSubviews() { print(self.sw) } /// セルの個数を指定するデリゲートメソッド(必須) func tableView(_ table: UITableView, numberOfRowsInSection section: Int) -> Int { return self.wideImage.count } // Section数 func numberOfSections(in table: UITableView) -> Int { return 1// 表示したいSection数 } // Sectionの上部のスペース func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 0 } // Sectionの下部のスペース func tableView(_ tableView: UITableView,heightForFooterInSection section: Int) -> CGFloat { return 0 } /// セルに値を設定するデータソースメソッド(必須) func tableView(_ table: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // セルを取得する guard let cell = table.dequeueReusableCell(withIdentifier: "thirdpagecell", for: indexPath) as? Thirdpagecell else { fatalError("Your cellIdentifier is invalid") } guard self.wideImage.count != 0 else { return cell } print(wideImage.count) print("indexPath.rowの値は:(indexPath.row)") cell.layoutWithData(wideImage[indexPath.row],Text1: date[indexPath.row]) cell.backgroundColor = UIColor.clear ///選択時のセルの色 let cellSelectedBgView = UIView() cellSelectedBgView.backgroundColor = UIColor.clear cell.selectedBackgroundView = cellSelectedBgView return cell } /// セルが選択された時に呼ばれるデリゲートメソッド func tableView(_ table: UITableView, didSelectRowAt indexPath: IndexPath) { } /* *section header の設定 */ func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let label = UIView() label.backgroundColor = UIColor.clear return label } /* *section footer の設定 */ func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { let label = UIView() label.backgroundColor = UIColor.clear return label } func scrollViewDidScroll(_ scrollView: UIScrollView) { if self.table.contentOffset.y + self.table.bounds.size.height > self.table.contentSize.height && self.table.isDragging { guard self.loadingflg else { return } load_data() } } func load_data(){ self.loadingflg = false self.scrollcount = self.scrollcount + 1 print("一番下に来た時の処理") let ud = UserDefaults.standard let id = ud.string(forKey: "id") let pass = ud.string(forKey: "pass") let param = ["id":id!,"passwd":pass!,"page":String(self.scrollcount)] //wideImage Alamofire.request("http://testapi~.php",method: .post, parameters: param).responseJSON { response in guard let object = response.result.value else{ return } let jsons = JSON(object) jsons["list"].forEach { (_, img_path) in self.date.append(img_path["date"].string!) Alamofire.request(img_path["url"].string!).responseImage { response in debugPrint(response.result) if let image = response.result.value { let imgratio:CGFloat = (image.size.height)/(image.size.width) let iw = CGFloat(self.sw) let resize = image.ResizeÜIImage(width: iw, height: iw*imgratio) self.wideImage.append(resize!) } } } if self.wideImage.count > 16*(self.scrollcount/4){ self.loadingflg = true self.table.reloadData() } }//wideImage } } class Thirdpagecell: UITableViewCell { @IBOutlet weak var wideImage: UIImageView! @IBOutlet weak var date: UILabel! func layoutWithData(_ img:UIImage,Text1 text1:String) { wideImage.image = img wideImage.contentMode = UIViewContentMode.scaleAspectFit date.text = text1 } }

また配列のことで気づいたことがあるので、追記致します。
load_date()のforeach部分ですが
indexを取得しようとしたところ、alamofireImageのメソッドに入るまでは
綺麗な順番で数字が出力されるのに対し、メソッド内ではindexの順番がバラバラに
なります。また、新たに"BannerImage"というString型の配列を用意して、
"banner"と同じ工程をさせて、画像url表示の配列の中身を確認すると、バラバラなindex
通りに画像が並んでおり、tableviewに表示されているものと同じものでした。
このことから、最終的には"banner"の配列の中身を,綺麗に並び変えることができれば良いのではないかと考えたのですが、そもそもなぜalamofireImageの中のindex番号はバラバラになるのでしょうか。。

Alamofire.request("http://testapi~.php",method: .post, parameters: param).responseJSON { response in guard let object = response.result.value else{ return } let jsons = JSON(object) jsons["list"].forEach { (index, img_path) in self.race_date_time.append(img_path["date"].string!) print("ここでのindexの値は:(index)")//結果:0,1,2,3,.....綺麗な順番 Alamofire.request(img_path["url"].string!).responseImage { response in debugPrint(response.result) if let image = response.result.value { print("image downloaded: (image)") print("ここでのindexの値は:(index)")//結果:3,0,1,2,.....バラバラな順番 let imgratio:CGFloat = (image.size.height)/(image.size.width) let iw = CGFloat(self.sw) let resize = image.ResizeÜIImage(width: iw, height: iw*imgratio) self.BannerImage.append(img_path["img_path"].string!) print("BannerImageは(self.BannerImage)") self.banner.append(resize!) } } }

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

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

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

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

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

fuzzball

2018/06/15 06:35

追加の10件全ての読み込みが完了した時点でセルが追加されればいいのでしょうか?
po_tato

2018/06/15 06:43

追加されていく10件がバラバラに表示されてしまう(wideImageだけ)ので、追加の画像もapiから取得される順番ごとに表示したいのですが、foreach内の順番ごとに配列に入れて行っているせいで順番がバラバラになっているのではないかというところまで現在進んでいる状態でして、、
guest

回答2

0

おかしいと思ったところは、

  • dateを追加するときに画像を追加していない。
  • 画像を追加していないのにremove/insertしている。
  • remove/insertのときにindexをそのまま使っている。(indexは今回読み込んだデータ内での番号で、今までの通し番号ではない)

です。

1.apiからデータを取得したときにダミー画像を追加。

swift

1let arrayIndex = self.date.count //※追加 要素番号を保存 2self.date.append(img_path["date"].string!) 3self.wideImage.append(UIImage()) //※追加 カラ画像を登録(ダミー画像などの方がいいです)

2.画像の読み込みが完了したら差し替え

swift

1//self.wideImage.remove(at: Int(index)!) 2//self.wideImage.insert(resize!, at: Int(index)!) 3//の代わりに 4self.wideImage[arrayIndex] = resize!

投稿2018/06/15 07:11

fuzzball

総合スコア16731

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

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

po_tato

2018/06/15 07:47

試してみました! ですが、最後の行で(self.wideImage[arrayIndex] = resize!) Thread 1: Fatal error: Index out of range このエラーが表示されました。
fuzzball

2018/06/15 08:05

その結果に対してあなたは何か考えたのでしょうか? 落ちたときのarrayIndexの値を教えてもらえますか?(これくらいは聞く前に書いて欲しいですが)
po_tato

2018/06/15 09:39 編集

alamofireの外でのprintでは9、中は12でした
fuzzball

2018/06/15 09:13

complete()とload_data()と、両方とも修正しましたか? load_data()の方は、 //self.wideImage.append(resize!) //の代わりに self.wideImage[arrayIndex] = resize! です。 ちなみに、これで解決した(ように見えた)としても、このままのコードだと問題山積みですよ。
po_tato

2018/06/15 09:26

両方とも修正しましたが、conplete()のself.wideImage[arrayIndex] = resize!この行で落ちます。 値は9でした
fuzzball

2018/06/15 09:34

18:02のコメントで「foreachの外、中」という表現をしていますが、どちらもforeachの中ですよね?正確に書いてもらえますか?
po_tato

2018/06/15 09:38

alamofireの中、外でした。失礼しました。
fuzzball

2018/06/15 09:41

どちらもAlamofireの中でしょう?
po_tato

2018/06/15 09:48

すみません。私が言っていたのは、foreachの中だけど、alamofireの外の部分(indexが正常に順序よく表示される)ところでの値と、alamofireメソッドの中(indexがなぜかバラバラ)の部分どちらともの値という意味でした。 complete()とload_date()のどちらもという意味ではありませんでした。 すみません。。
guest

0

お話を聞くと、cellが再描出される際に問題がありそうな気がします。

具体的にはdequeueReusableCellのとこが間違っているのではないでしょうか?
見てみると、guardをつける必要がないと思うのですが、、、
tableviewcellのtutorialでも
あまりguardがついてるのを見たことがないのですが、
何を参考にしましたか?

もしかしたら、古いバージョンのものなのではないでしょうか?
guardをのぞいて試して見てください。

投稿2018/06/13 17:14

hameji

総合スコア1380

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

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

po_tato

2018/06/14 04:24 編集

返信ありがとうございます! そこがあったか!と慌ててこのように編集して実行して見ましたが、 結果は変わらずでした。。 また、guardを入れたのはこちらを参考にしたからです。"https://qiita.com/tanjo/items/8d6fa23ad9cd56686d8e" ``` let cell = table.dequeueReusableCell(withIdentifier: "thirdpagecell", for: indexPath) as? Thirdpagecell cell?.layoutWithData(banner[indexPath.row],Text1: race_date_time[indexPath.row]) cell?.backgroundColor = UIColor.clear ///選択時のセルの色 let cellSelectedBgView = UIView() cellSelectedBgView.backgroundColor = UIColor.clear cell?.selectedBackgroundView = cellSelectedBgView return cell! } ```
退会済みユーザー

退会済みユーザー

2018/06/14 23:49 編集

↑の書き方自分には見やすくていいです。 何かの足しにでもなればと思い書いてみます。 問題点の切り分けをしてみてはどうでしょうか? 1.配列作成時点の問題 2.セルの再利用の問題 3.非同期的な画像取得時の問題 のどれかだと思うので、一番上から潰していったらどうでしょうか。 cell?.layoutWithData(banner[indexPath.row],Text1: race_date_time[indexPath.row]) の下で、配列の中身プリントして、まずは、意図したimageとdateの組み合わせが配列に出力されているか確認してみてください。 print("index:",indexPath.row,"type:",type(of:banner),"image:",banner[indexPath.row],"type:"type(of: race_date_time),"date:",race_date_time[indexPath.row]) typeは、念の為なのでArray<UIImage>,Array<String>とか表示されたら print()内から消してください。 意図したimageとdateの組み合わせが配列に出力されているようでしたら、それと、tableViewの表示を見くらべて問題点を探ってみてください。 乱文ご容赦くださいませ。
po_tato

2018/06/15 00:58

回答して頂きありがとうございます! 問題の切り分け行っていこうと思います。 これは、少し違う質問なのですが、 昨日、printで同じようにセルの中身や、bannerの中身の並び順を確認しようとしていたのですが、ログに出してもUIImage型は"0x1d42aed60"このような英数字が出るだけで何が入っているかわかりませんでした。String型に型変換を行おうとしたのですが、なぜかはまってしまいうまくいきませんでした。そこでUIImage型の情報を確認する方法を知っていましたら、ご教示願いたいのですが、何かいい方法ありますでしょうか?
fuzzball

2018/06/15 01:05

切り分けをすると言いながら切り分けできていないという‥。 もとの質問に関係のないことは新規に質問して下さい。
po_tato

2018/06/15 01:25

失礼致しました。 新規に質問させて頂きました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問