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

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

ただいまの
回答率

90.49%

  • Swift

    8908questions

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

TableViewでセルを追加してもindexPathが更新されない

解決済

回答 3

投稿 編集

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

akiraa

score 3

 前提・実現したいこと

下記のようなTableViewを作成しています
表示上はセルを追加されたように見えてもindexPathが増加していないため
didSelectRowAtのindexPathは常に一番目のデータが帰ってきます
どこがいけないんでしょうか?

 発生している問題・エラーメッセージ

セルをタップしても、該当のIndexPathが取得できない

 該当のソースコード

import UIKit
import AVFoundation
import AudioToolbox

class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate,UITableViewDelegate,UITableViewDataSource{


    //var itemTable: UITableView?
    var captureDevice:AVCaptureDevice?
    var videoPreviewLayer:AVCaptureVideoPreviewLayer?
    var captureSession:AVCaptureSession?
    let detectionArea = UIView()
    var timer: Timer!
    var counter = 0
    var tableView: UITableView!
    //let items = ["Apple","Banana","Orange"]
    //var barcodeList = [String]()
    var items = [Item!]()
    var item: Item! = Item()


    override func viewDidLoad() {
        super.viewDidLoad()

        print("viewDidLoad...")
        // Do any additional setup after loading the view, typically from a nib.
        navigationItem.title = "Scanner"
        view.backgroundColor = .white

        captureDevice = AVCaptureDevice.default(for: .video)
        // カメラがあるか確認し,取得する
        if let captureDevice = captureDevice {

            do {
                let input = try AVCaptureDeviceInput(device: captureDevice)

                captureSession = AVCaptureSession()
                guard let captureSession = captureSession else { return }
                captureSession.addInput(input)

                // metadata取得に必要な初期設定
                let captureMetadataOutput = AVCaptureMetadataOutput()
                captureSession.addOutput(captureMetadataOutput)

                captureMetadataOutput.setMetadataObjectsDelegate(self, queue: .main)
                captureMetadataOutput.metadataObjectTypes = [.code128, .qr, .ean13,  .ean8, .code39] //AVMetadataObject.ObjectType

                captureSession.startRunning()

                // カメラからの取得映像を画面に表示する
                videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
                videoPreviewLayer?.videoGravity = .resizeAspectFill
                //videoPreviewLayer?.frame = view.layer.bounds
                videoPreviewLayer?.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height / 4)
                view.layer.addSublayer(videoPreviewLayer!)

            } catch {
                print("Error Device Input")
            }
        } //end if let

        view.addSubview(codeLabel)
        codeLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        codeLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        codeLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true
        codeLabel.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true

        // どの範囲を解析するか設定する
        let x: CGFloat = 0.05
        let y: CGFloat = 0.45
        let width: CGFloat = 0.9
        let height: CGFloat = 0.1

        //detectionArea.frame = CGRect(x: view.frame.size.width * x, y: view.frame.size.height * y, width: view.frame.size.width * width, height: view.frame.size.height * height)
        detectionArea.frame = CGRect(x: (videoPreviewLayer?.frame.width)! * x, y: (videoPreviewLayer?.frame.height)! * y, width: view.frame.size.width * width, height: view.frame.size.height * height)
        detectionArea.layer.borderColor = UIColor.red.cgColor
        detectionArea.layer.borderWidth = 3
        view.addSubview(detectionArea)

        timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
        timer.fire()
        //self.tableView.register(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: "customCell")
        self.tableView = { [weak self] in
            guard let `self` = self else { return UITableView() }
            //let tableView = UITableView(frame: self.view.bounds, style: .plain)
            let tableView = UITableView(frame: CGRect(x: 0, y: (videoPreviewLayer?.frame.height)!, width: view.frame.size.width, height: view.frame.size.height - (videoPreviewLayer?.frame.height)!), style: .plain);
            tableView.autoresizingMask = [
                .flexibleWidth,
                .flexibleHeight
            ]
            tableView.delegate = self
            tableView.dataSource = self

            self.view.addSubview(tableView)

            return tableView
            }()

    } // end viewDidLoad

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    override func viewWillAppear(_ animated: Bool) {
        self.tableView.reloadData()
    }

    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        if metadataObjects.count == 0 {
            //print("No Input Detected")
            codeFrame.frame = CGRect.zero
            codeLabel.text = "No Data"
            return
        }
        print("call metadataOutput")
        let metadataObject = metadataObjects[0] as! AVMetadataMachineReadableCodeObject

        guard let stringCodeValue = metadataObject.stringValue else { return }
        counter = 0

        self.item?.title = stringCodeValue
        self.item?.qty = 1
        print(self.item!.title as Any)
        print(self.item!.qty as Any)
        self.items.append(self.item)

        self.tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: UITableViewRowAnimation.right)

        guard let barcodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObject) else { return }

        // Play system sound with custom mp3 file
        if let customSoundUrl = Bundle.main.url(forResource: "beep-07", withExtension: "mp3") {
            var customSoundId: SystemSoundID = 0
            AudioServicesCreateSystemSoundID(customSoundUrl as CFURL, &customSoundId)
            //let systemSoundId: SystemSoundID = 1016  // to play apple's built in sound, no need for upper 3 lines

            AudioServicesAddSystemSoundCompletion(customSoundId, nil, nil, { (customSoundId, _) -> Void in
                AudioServicesDisposeSystemSoundID(customSoundId)
            }, nil)

            AudioServicesPlaySystemSound(customSoundId)
            //captureSession?.stopRunning()
        }

        // Stop capturing and hence stop executing metadataOutput function over and over again
        print("stop Running")
        captureSession?.stopRunning()

        // Call the function which performs navigation and pass the code string value we just detected
        //displayDetailsViewController(scannedCode: stringCodeValue)
    }

    @objc func update(tm: Timer) {
        counter += 1
        //print(counter)
        if 1 < counter {
            captureSession?.startRunning()
            detectionArea.layer.borderColor = UIColor.red.cgColor
            detectionArea.layer.borderWidth = 3
            codeLabel.text = ""
        }
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        print("call numberOfSections")
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        print("items.count \(self.items.count)")
        return self.items.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
            ?? UITableViewCell(style: .default, reuseIdentifier: "Cell")

        cell.textLabel?.text = self.items[indexPath.row].title

        print("call cellForRowAt")
        return cell

    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        print("Selected! \(self.items[indexPath.row].title)")
        print("indexPath \(self.items[indexPath.row])")

    }

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexpath: IndexPath) {
        if editingStyle == UITableViewCellEditingStyle.delete {
            //リストから削除
            items.remove(at: indexpath.row)

            //セルを削除
            tableView.deleteRows(at: [indexpath], with: UITableViewRowAnimation.fade)
        }
    }
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • nakasho_dev

    2018/03/19 00:29

    質問するときのヒント(https://teratail.com/help/question-tips)を読んで、ソースコードにシンタックスハイライトを付けてください。ソースが読みづらくて回答が得られにくいと思います。

    キャンセル

回答 3

checkベストアンサー

0

tableView.reloadData()を呼んでいないのでテーブルの更新がされないのではないでしょうか。

【追記】
viewWillAppearでreloadDataを呼んでいるとのことですが、
metadataOutputにviewWillAppearは呼ばれているのでしょうか。
もし呼ばれていてreloadDataが実行されているとしたらself.itemsの中身が変わっていないのかもしれません。

気になったのはクラス変数としてitemを作成していることです。
itemを使いまわしているので、items[0]、items[1]と追加されても常に同じ内容になります。
metadataOutput関数内でしか使用していないので都度生成したほうが良いと考えます。

また、metadataOutput内の以下のコードで関数を抜けていないかなども確認したほうが良いのではないでしょうか。

guard let stringCodeValue = metadataObject.stringValue else { return }

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/03/19 18:57

    ご回答ありがとうございます。
    viewWillAppear内にreloadData()を記述してみましたが状況は変わりませんでした。

    キャンセル

  • 2018/03/20 21:03

    ご回答ありがとうございます。
    ご指摘いただいた通りitemを局所化したところ一発で解決しました
    大変助かりました、ありがとうございます

    キャンセル

0

insertRowsの部分を下記のように変更すると何か変わりますでしょうか?

DispatchQueue.main.async { [weak self] in
  self?.tableView.beginUpdates()
                                          // ↓0から変更
  self?.tableView.insertRows(at: [IndexPath(row: self.items.count - 1, section: 0)], with: UITableViewRowAnimation.right)
  self?.tableView.endUpdates()
}

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/03/19 17:56

    回答ありがとうございます。
    記述してみましたが、状況は変わりませんでした。

    キャンセル

  • 2018/03/19 19:31

    一部処理を修正しました。また、self.items.appendの後でprint(self.items)を出力すると何が出力されますでしょうか?

    キャンセル

  • 2018/03/19 20:31

    ご回答ありがとうございます。

    printさせると [BarcodeScanner.Item] と表示されます

    キャンセル

  • 2018/03/20 17:25

    metadataOutput関数が複数呼ばれた場合にself.itemsには複数アイテムが入っていますでしょうか?また、print(self.item!.title as Any)の中身も教えて頂けましたら幸いです。

    キャンセル

  • 2018/03/20 21:01

    ご回答ありがとうございます。
    self.items には同じデーターが複数入ってました
    print(self.item!.title as Any)ですが
    [BarcodeScanner.Item, BarcodeScanner.Item] という内容です

    キャンセル

0

自分でテーブルにセルを追加してはいけません。
保持している配列にデータを追加した後、テーブルをリロードして下さい。

self.items.append(self.item)
//self.tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: UITableViewRowAnimation.right)
self.tableView.reloadData()

テーブルの上に追加したいのであれば、配列の先頭にデータを追加して下さい。

self.items.insert(self.item, at: 0)

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

  • Swift

    8908questions

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