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

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

ただいまの
回答率

90.84%

  • Swift

    6313questions

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

  • TableView

    99questions

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

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

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 141

Kw6ZG2sV

score 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!)
                    }
                }
            }
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • fuzzball

    2018/06/15 15:35

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

    キャンセル

  • Kw6ZG2sV

    2018/06/15 15:43

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

    キャンセル

回答 2

0

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

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

です。

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

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/15 16:47

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

    キャンセル

  • 2018/06/15 17:05

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

    キャンセル

  • 2018/06/15 18:39 編集

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

    キャンセル

  • 2018/06/15 18:13

    complete()とload_data()と、両方とも修正しましたか?

    load_data()の方は、

    //self.wideImage.append(resize!)
    //の代わりに
    self.wideImage[arrayIndex] = resize!

    です。

    ちなみに、これで解決した(ように見えた)としても、このままのコードだと問題山積みですよ。

    キャンセル

  • 2018/06/15 18:26

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

    キャンセル

  • 2018/06/15 18:34

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

    キャンセル

  • 2018/06/15 18:38

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

    キャンセル

  • 2018/06/15 18:41

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

    キャンセル

  • 2018/06/15 18:48

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

    complete()とload_date()のどちらもという意味ではありませんでした。
    すみません。。

    キャンセル

-1

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/14 13: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/15 08: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の表示を見くらべて問題点を探ってみてください。

    乱文ご容赦くださいませ。

    キャンセル

  • 2018/06/15 09:58

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

    キャンセル

  • 2018/06/15 10:05

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

    キャンセル

  • 2018/06/15 10:25

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

    キャンセル

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

  • ただいまの回答率 90.84%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Swift

    6313questions

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

  • TableView

    99questions

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