24011


テーブルビューにてセルを追加する際にタイトルとサブタイトルをつけたいのですが、タイトルは問題なく出来たのですが、新しいセルを追加するたびにサブタイトルに表示する文字が変わってしまってこまっています。

多分原因は y: Float = 0書いてあるからだと思っています。  
yにx1とx2を掛け算して求めた値を代入していて、それをサブタイトルをしてcell.detailTextLabel?.text =
String(format: "%.f回", y)で呼び出してるので保存するたびにサブタイトルが変わっているのだと思います。

そこでセル１つ１つに計算して求めた回数が表示されるようにサブタイトルをつけたいのですがどのように書き直せばよろしいでしょうか？

[code]

    import UIKit
    
    var　y: Float = 0
    private var score: UILabel!
    
    class ViewController: UIViewController,UITextFieldDelegate {
    
    private var myTextField: UITextField!
    private var myTextField2: UITextField!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
    
            // ボタンを生成する.
            let nextButton: UIButton = UIButton(frame: CGRectMake(0,0,self.view.bounds.width/3,self.view.bounds.height/11))
            nextButton.backgroundColor = UIColor.redColor();
            nextButton.layer.masksToBounds = true
            nextButton.setTitle("測定", forState: .Normal)
            nextButton.layer.cornerRadius = 20.0
            nextButton.layer.position = CGPoint(x: self.view.bounds.width/2 , y:self.view.bounds.height/1.8)
            nextButton.addTarget(self, action: "onClickMyButton:", forControlEvents: .TouchUpInside)
    
            // ボタンを追加する.
            self.view.addSubview(nextButton);
    
            // ボタンを生成する.
            let nextButton2: UIButton = UIButton(frame: CGRectMake(0,0,self.view.bounds.width/3,self.view.bounds.height/11))
            nextButton2.backgroundColor = UIColor.redColor();
            nextButton2.layer.masksToBounds = true
            nextButton2.setTitle("保存", forState: .Normal)
            nextButton2.layer.cornerRadius = 20.0
            nextButton2.layer.position = CGPoint(x: self.view.bounds.width/2 , y:self.view.bounds.height/1.5)
            nextButton2.addTarget(self, action: "onClickMyButton2:", forControlEvents: .TouchUpInside)
    
            // ボタンを追加する.
            self.view.addSubview(nextButton2);
    
            score = UILabel(frame: CGRectMake(0, 0,self.view.bounds.width/1.5,self.view.bounds.height/14))
            score.layer.position = CGPoint(x: self.view.bounds.width/2, y: self.view.bounds.height/3)
            self.view.addSubview(score);
        }
    
        internal func onClickMyButton(sender: UIButton) {
            let x1 = Float(myTextField.text!)
            let x2 = Float(myTextField2.text!)
            y = x1! * x2!
    
            score.text = String(format: "%.f回", y)
        }
    
        internal func onClickMyButton2(sender: UIButton){
            todoItem.append(self.myTextField3.text!)
    
            NSUserDefaults.standardUserDefaults().setObject(todoItem, forKey: "todoList")
    
    
            // TableViewを再読み込み.
            myTableView.reloadData()
        }
    
[/code]

〜〜〜〜〜〜〜

ここから別のファイル（ビューコントローラー）です。

〜〜〜〜〜〜

[code]

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell")
    
        cell.detailTextLabel?.text = String(format: "%.f回", y)        
    
        // Cellに値を設定
        cell.textLabel?.text = todoItem[indexPath.row]
    
        return cell
    }
    
[/code]

〜〜〜〜〜

編集後追加コード

〜〜〜〜〜

[code]

    override func viewDidLoad() {
        super.viewDidLoad()
    
        if NSUserDefaults.standardUserDefaults().objectForKey("todoList") != nil {
            todoItem = NSUserDefaults.standardUserDefaults().objectForKey("todoList") as! [String]
        }
    
        if NSUserDefaults.standardUserDefaults().objectForKey("todoList1") != nil {
            todoSubitem = NSUserDefaults.standardUserDefaults().objectForKey("todoList1") as! [String]
        }
    
        省略
    
    }
    
    //Cellの総数を返す
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return todoItem.count
    }
    
    //Cellに値を設定する
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell")
    
        // Cellに値を設定
        cell.textLabel?.text = todoItem[indexPath.row]
        cell.detailTextLabel?.text = todoSubitem[indexPath.row]        
    
        return cell
    }
    
    //Cellを挿入または削除しようとした際に呼び出される
    func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath){
        if editingStyle == UITableViewCellEditingStyle.Delete{
            todoItem.removeAtIndex(indexPath.row)
            NSUserDefaults.standardUserDefaults().setObject(todoItem, forKey: "todoList")
            todoSubitem.removeAtIndex(indexPath.row)
            NSUserDefaults.standardUserDefaults().setObject(todoSubitem, forKey: "todoList1")
            myTableView.reloadData()
        }
    }
    
[/code]

このように書いています。


CC BY-SA 3.0
2016-04-13T13:11:41.527
23989
2016-04-14T15:03:27.107
2016-04-14T15:03:27.107
5519
11019
question
ios swift iphone swift2
セル１つ１つに別なサブタイトルをつける方法を教えてください。


簡単にまとめると「`tableView(_:cellF‌​orRowAtIndexPath:)`は、いつ呼ばれるかわからないので、いつ呼ばれても大丈夫なようにコードを書かないといけない」と言うことになります。

どう言うことかと言うと、例えば10行の表がある時に、後ろの6行目から10行目までの部分が表示されているとします。ユーザのスクロール操作で1行目から5行目までを再表示する必要が出た場合、`UITableView`は`UITableViewDataSource`に対して、「1行目から5行目の内容をよこせ」と言う意味で`index.row`の値を0...4に変化させながら5回順不同で`tableView(_:cellF‌​orRowAtIndexPath:)`を呼び出します。その時サブタイトルとして作成している文字列`String(format:
"%.f回",
y)`の中の`y`は、(1〜5行目用の`y`ではなく)「最後に計算した`y`の値」でしかありませんから、その同じ値が1行目から5行目まで全てのサブタイトルになってしまいます。

と言うわけで、サブタイトルを正しく表示したいのであれば、サブタイトルに使用するデータもタイトルと同じ個数を持つ配列でないといけません。

まず、`todoItem`が宣言されている場所(これもコードに含めて欲しかったんですが、見当たりませんね)にサブタイトル用の配列も追加します。

[code]

        var todoSubitem: [String] = []
    
[/code]

`todoItem`と`todoSubitem`の要素の個数は必ず一致しないといけませんから、この内容を操作するとしたら、`onClickMyButton2(_:)`メソッドの中ということになるでしょう。

[code]

        func onClickMyButton2(sender: UIButton){
    
            todoItem.append(self.myTextField3.text!)
            todoSubitem.append(String(format: "%.f回", y))
    
            NSUserDefaults.standardUserDefaults().setObject(todoItem, forKey: "todoList")
            //必要ならtodoSubitemも保存
    
            // TableViewを再読み込み.
            myTableView.reloadData()
    
        }
    
[/code]

以上の変更で「`todoItem`も`todoSubitem`も表の行数分のデータを保持している」状態になるはずですから、後は`tableView(_:cellF‌​orRowAtIndexPath:)`も、それに合わせて書き直せば良い、と言うことになります。

[code]

        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
            let cell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell")
            // Cellに値を設定
            cell.textLabel?.text = todoItem[indexPath.row]
            cell.detailTextLabel?.text = todoSubitem[indexPath.row]
            return cell
        }
    
[/code]

今後、機能を増やして削除やら順序の入れ替えやらが必要になった場合も、「`todoItem`の内容を修正したら`todoSubitem`の内容もつじつまが合うように修正する」必要が出てきますので、本当はデータ構造自体を変えたほうが良いんですが、話が複雑になりすぎるので最小限の修正にとどめておきます。  
`todoSubitem`とかに入れるのは、文字列じゃなくて`y`の値そのものにした方が良いかもしれませんが、そう言った点も含めて、上記のコードを参考に、ご自身のアプリを修正してみてください。

* * *

(編集後追加コードの部分について)

[code]

    if NSUserDefaults.standardUserDefaults().objectForKey("todoList") != nil {
        todoItem = NSUserDefaults.standardUserDefaults().objectForKey("todoList") as! [String]
    }
    
    if NSUserDefaults.standardUserDefaults().objectForKey("todoList1") != nil {
        todoSubitem = NSUserDefaults.standardUserDefaults().objectForKey("todoList1") as! [String]
    }
    
[/code]

この書き方だと`objectForKey(_:)`を2回ずつ呼び出すことになるので、if-
letなんかの使い方も覚えて欲しいのですが、そこが間違っているわけではないので、とりあえず置いておきます。

この回答の前半に『「todoItemの内容を修正したらtodoSubitemの内容もつじつまが合うように修正する」必要が出てきます』と書きましたが、このコード、バグのせいなどで「`"todoList"`のデータはあるけど`"todoList1"`のデータは存在しない」なんて状態になった場合(今まさにその状態なのだと思われます)、`todoItem`と`todoSubitem`に不整合(要はデータの個数が違ってしまう)が発生して、このあと正しく処理できなくなります。

今はシミュレータでテストしているでしょうから、コンテンツのリセット(Reset Contents and
Settings...)をしてやれば「どっちも空」でつじつまが合うはずですが、他の開発中アプリのデータまで消えてしまいますし、バグが出るたびにリセットするのも大変ですから、こんなコードを上の行のあとに追加してみてください。

[code]

        //バグなどのせいで、todoItemとtodoSubitemに不整合が生じたら、両方リセットしてしまう
        if todoItem.count != todoSubitem.count {
            todoItem = []
            todoSubitem = []
            NSLog("todoItemとtodoSubitemに不整合が生じたので両方リセットしました")
        }
    
[/code]

もちろん開発を進めていくうちにバグを全部潰して、上記の`NSLog`のメッセージは絶対に出ないようにしないといけません。  
試してみてまた何かあれば(この回答に対する疑問点は、質問に対してのコメントではなく、この回答に対してのコメントの方が良いですね)コメント等でお知らせください。


CC BY-SA 3.0
2016-04-14T09:29:59.493
24011
2016-04-14T14:40:24.210
2016-04-14T14:40:24.210
13972
13972
23989
answer