29034


Tableview上にcellが約60あり、全てのcellにtextfieldが1つずつ配置されています。cellの数は設定によって変わり、高さもまちまちです。  
複数のtextfieldに入力された値を一括で保存するのですが、保存後に前の画面に戻り再度同じ画面を開くと後半のcell(50セル目の辺りから)の値がずれて表示される、という不具合がユーザーから報告されました。

入力を以下のようにしたら、  
項目A:「1」  
項目B:「2」  
項目C:「3」  
項目D:「4」

再表示すると以下のようになるようです。  
項目A:「1」  
項目B:「3」  
項目C:「4」  
項目D:「」（空になる）

情報が限られている為自身の環境では再現できず、原因箇所は断定できないのですが、「ずれる」という現象から、cellからTableViewControllerへ入力値の変更を通知している箇所が怪しいのではないかと考えています。(前の画面に表示されるデータがずれた値になっていることから、保存時点で既にずれていると推測)  
以下がそのコードですが、「_arr」配列変数をデータのI/Oで使用しており、textfieldの値が書き換えられたら、この配列の該当インデックスにセットし直しています。

[code]

    class MyCell: UITableViewCell, UITextFieldDelegate {
        func textFieldDidEndEditing(textField: UITextField) {
            if _strTxtBfr != textField.text { //変更されたかチェック
                UIApplication.sharedApplication().sendAction(#selector(MyTableController.cell_Change(_:)), to: nil, from: self, forEvent: nil)
            }
        }
    
        func dispVal(dic: [String: String]) {
            lblKey.text      = dic["key"]
            lblTitle.text    = dic["title"]
            txtVal.text      = dic["val"]
        }
    
        func getVal() -> (key: String, val: String) {
            return (lblKey.text!, txtVal.text!)
        }
    }
    
    class MyTableController: UITableViewController {
        override func cell_Change(sender: MyCell) {
            if let indexPath = self.tableView.getIndexPath(sender) {
                let (_, val) = sender.getVal() //入力値を取得
                _arr[indexPath.section][indexPath.row]["VAL"] = val //入力値を変数にセット
            }
        }
    
        override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
            let data = _arr[indexPath.section][indexPath.row]
            let cell = tableView.dequeueReusableCellWithIdentifier("cellTxt", forIndexPath: indexPath) as! MyCell
            cell.dispVal([
                "key":    data["CD"]!,
                "title":  data["NAME"]!,
                "val":    data["VAL"]!,
                ])
            return cell
        }
    }
    
    extension UITableView {
        /// cellのNSIndexPathを取得する
        func getIndexPath(sender: UIView?) -> NSIndexPath? {
            if let obj = sender {
                let point = self.convertPoint(obj.frame.origin, fromView: obj.superview)
                if let indexPath = self.indexPathForRowAtPoint(point) {
                    return indexPath
                } else {
                    print("tableView.indexPathForRowAtPointがnil")
                }
            } else {
                print("senderがnil")
            }
            return nil
        }
    }
    
[/code]

DBのテーブルのキーとなるCDとその値が対になって保持されているべきですが、この箇所だけはCDではなくindexPathで値の書き換えを行なっています。  
indexPathForRowAtPointが正しくない場合この不具合の説明がつきそうなのですが、そんなことは起こりえるでしょうか？？  
セルの数や高さが固定でないこと、セルの後半の50セル目辺りからずれていることから、indexPathForRowAtPointのバグかな？と推測したのですがどうでしょうか？  
不具合の出ている端末はおそらくiphone6か6sです。

あるいはindexPathForRowAtPointではなく他の箇所に問題があるでしょうか？コードがおかしいようでしたらご指摘ください。  
よろしくお願いいたします。


CC BY-SA 3.0
2016-09-16T03:50:04.540
28918
2016-09-21T14:35:25.667
2016-09-16T05:50:30.160
14125
14125
question
swift iphone tableview
TableViewCell上に配置したTextfieldの値がずれて表示される


ご提示のコードを拝見した限り、そして、セルあたり3つのText
Fieldがあるのを1つに変更するなど、やや簡略化したコードを、実際にiOSシミュレータで動かしてみたところ、不具合は見つかりませんでした。  
しかし、このプログラムに、グローバル変数、エクステンション、Target-
Actionパラダイムとレスポンダチェーンなど、いくつもの技術を導入して、複雑な構造になっているのは、看過できない点といえると思います。構造が複雑だと、バグが入りこむ確率が高くなり、かつ特殊な条件でのみ発生するバグが起きやすくもなります。めったに起きないバグは、テスト段階で見つけることがむずかしいので、やっかいなバグといえます。  
どこにバグがあるのかを見つけるよりも、プログラムの構造をできるだけシンプルなものにすることに注力するほうが、有意義ではないでしょうか？

下のコードは、私からのひとつの提案です。かなりシンプルな構造にできたのではないかと思います。  
セルあたりのText
Fieldを1つにし、`UITextField`のDelegateをリターンキーを押した時に呼ばれるものに変更するなど、可読性を優先したものにしていますので、ご了解ください。

[code]

    import UIKit
    
    class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
    
        var textArray = [[String]]()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // データとして、二次元配列を用意する。
            textArray = (1...10).map{i -> [String] in
                (1...10).map{j -> String in
                    "Section" + "\(i), " + "Line" + "\(j)"
                }
            }
        }
    
        func numberOfSections(in tableView: UITableView) -> Int {
            return textArray.count
        }
    
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return textArray[section].count
        }
    
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell
            // textArrayからテキストを読み取り、代入。
            cell.textField.text = textArray[indexPath.section][indexPath.row]
            // UITextFieldのプロパティtagに、セクション番号、行番号を書き込む。
            // Int型整数の上位ビットに、セクション番号、下位ビットに、行番号を入力。
            cell.textField.tag = indexPath.section << 8 + indexPath.row
            // textFieldのDelegate先をViewControllerにする。
            cell.textField.delegate = self
    
            return cell
        }
        // UITextFieldのDelegateメソッド。
        func textFieldShouldReturn(_ textField: UITextField) -> Bool {
            if let text = textField.text {
                // プロパティtagから、ビットシフトとビットマスクを使って、セクション番号、行番号を取得する。そして、textArrayの要素に書き込み。
                textArray[textField.tag >> 8][textField.tag & 0xff] = text
            }
            textField.resignFirstResponder()
    
            return true
        }
    
    }
    
    class CustomCell: UITableViewCell {
    
        @IBOutlet weak var textField: UITextField!
    
    }
    
[/code]

`UITextField`（`UIView`）のプロパティ`tag`に、セクション番号、行番号を書き込むのが、ポイントです。

※Swift3で記述しています。


CC BY-SA 3.0
2016-09-17T10:16:16.380
28950
2016-09-17T10:16:16.380
18540
28918
answer


質問者です。問題が解決しましたので共有いたします。  
結論から申し上げますと、indexPathForRowAtPointのバグだったようです。indexPathForRowAtPointで取得したNSIndexPathを_arrのインデックスとして新たな値をセットし直していましたが、この部分を_arrの中身の"CD"で検索し、同じ"CD"の要素に新たな値をセットするという処理に書き換えユーザーに提供してみましたところ、問題が解消したそうです。  
なので、indexPathForRowAtPointが正確な値を返していなかったと結論づけました。


CC BY-SA 3.0
2016-09-21T14:35:25.667
29034
2016-09-21T14:35:25.667
14125
28918
answer