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

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

ただいまの
回答率

90.38%

  • Swift

    9165questions

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

  • iOS

    4873questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

TableViewに貼り付けたスイッチのインデックスを取得する方法

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,447

Robokun

score 30

Swift3を使ってデータベースと連携したTableViewのコードについて質問です。

データベースからTableViewに貼り付けたスイッチの動作について質問です。
カスタムセルを使い、データベースの内容を表示する事、データを追加、削除はできました。

問題はセルに配置したスイッチを切り替えた時にBool値のデータを切り替えたいのですが
上手くいきません。インターネットでやり方を検索して実装したのですがイマイチ自信がありません。
今出ているエラーメッセージは

unrecognized selector sent to instance 
というものです。

そもそも、実装に関するロジックなのですが、
テーブルビューのスイッチが押されたという事をどこで判別するかという点ですが
InputViewControllerのスイッチが押されたアクションで取得なのかな?と考えました。
カスタムセルとInput用のビューコレクターのコードは以下になります。

import UIKit
import RealmSwift

class InputViewController: UIViewController ,UITableViewDelegate, UITableViewDataSource{

    //OUTLET
    @IBOutlet weak var pareText: UITextField!
    @IBOutlet weak var tableView: UITableView!

    //デフォルトのRealmインスタンスを取得する
    let realm = try! Realm()

    //DB内のデータを保持する配列(IDの順番でソート)
    let dataArray = try! Realm().objects(Pare.self).sorted(byProperty: "id", ascending: true)

    //データを追加するアクション
    @IBAction func appPare(_ sender: UIButton) {

        //入力チェック
        if isValidateInputContents() == false{
            print("入力値に問題あり")
            return
        }

        //登録するデータを作成
        let pare = Pare()
        pare.name = pareText.text!
        pare.isUse = true
        if(dataArray.count != 0){
            pare.id = dataArray.max(ofProperty: "id")! + 1
        }

        //Realmに書き込み
        do {
            try realm.write {
                realm.add(pare, update: true)
            }
        } catch {
            print("データ登録失敗")
        }
        tableView.reloadData()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // MARK: UITableViewDataSource プロトコルのメソッド
    //各セクションのセル数を返す
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataArray.count
    }

    //各セルの内容を返す
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //再利用可能なCellを得る
        //let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "Cell")
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! CustomTableViewCell

        //Cellに値を設定する
        let object = dataArray[indexPath.row]
        cell.setCell(idLabelText: "\(object.id)", tpareLabelText: object.name, swIsUseValue: object.isUse)
        return cell
    }

    //セルが選択された時に呼ばれるデリゲートメソッド
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("セル番号:\(indexPath.row) セルの内容:\(dataArray[indexPath.row])")
    }

    //セルの削除
    //セルが削除可能なことを伝える
    func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
        return UITableViewCellEditingStyle.delete;
    }

    //Deleteボタンが押された時データベース削除の処理
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == UITableViewCellEditingStyle.delete {
            try! realm.write {
                self.realm.delete(self.dataArray[indexPath.row])
                tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.fade)
            }
        }
    }

    //カスタムセルのスイッチが押された時の処理
    @IBAction func isUseSwitch(_ sender: UISwitch) {
        let point = self.tableView.convert(sender.frame.origin, from: sender.superview)
        if let indexPath = self.tableView.indexPathForRow(at: point) {
            print("section: \(indexPath.section) - row: \(indexPath.row)")
        }
    }

    //データベースの値をチェック
    private func isValidateInputContents()->Bool{
        //名前の入力
        if let name = pareText.text {
            if name.characters.count == 0{
                return false
            }
        }else{
            return false
        }
        return true
    }
}

CustomTableViewCellでBool値の物にボタンを割り当ててます

import UIKit

class CustomTableViewCell: UITableViewCell {
    ///OUTLET
    @IBOutlet weak var idLabel: UILabel!
    @IBOutlet weak var pareLabel: UILabel!
    @IBOutlet weak var swIsUse: UISwitch!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }

    //セルを設定するメソッド
    func setCell(idLabelText: String, tpareLabelText: String, swIsUseValue: Bool) {
        idLabel.text = idLabelText
        pareLabel.text = tpareLabelText
        swIsUse.isOn = swIsUseValue
    }
}

少し分かりにくい質問になりましたが、実装方法について
解決方法を知りたいと思ってます。
<補足します>
テスト中の画像を載せます

イメージ説明

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

0

あまりViewController自体でcellのイベント受けることはやらないですが、今の実装でスイッチの切り替えによりCellのindexPathが必要ならUISwitchのサブクラスを作成してcellのindexPathを保持するプロパティを追加すれば良いと思います。
※ コードは最低限で書いています(動確済)

import UIKit

// ----- ViewController -----

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath) as! CustomTableViewCell

        cell.swIsUse.cellIndexPath = indexPath
        return cell
    }

    @IBAction func isUseSwitch(_ sender: CustomSwitch) {
        print(sender.cellIndexPath)
    }
}

// ----- Custom Switch -----

class  CustomSwitch: UISwitch {
    var cellIndexPath: IndexPath!
}

// ----- Custom Cell -----

class CustomTableViewCell: UITableViewCell {
    @IBOutlet weak var swIsUse: CustomSwitch!
}

テーブルビューのスイッチが押されたという事をどこで判別するかという点ですが 
InputViewControllerのスイッチが押されたアクションで取得なのかな?

カスタムセルに乗っているSwitchのイベントなので以下の様にセルで受けるのが一般的かと思います。
Switchを変更した時にやりたいことがわからないので、とりあえずスイッチをON→OFF,OFF→ONを変更してViewControllerのモデルのSwitchのtrue,falseを書き換えることをやっています。

import UIKit

// ----- ViewController -----

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!
    private var dataArray: Array<CellObject> = []

    override func viewDidLoad() {
        super.viewDidLoad()

        createCellObject()
    }

    func createCellObject() {
        dataArray.append(CellObject(title: "title1", isSelect: true))
        dataArray.append(CellObject(title: "title2", isSelect: false))
        dataArray.append(CellObject(title: "title3", isSelect: true))
        dataArray.append(CellObject(title: "title4", isSelect: false))
    }


    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataArray.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath) as! CustomTableViewCell
        cell.cellObject = dataArray[indexPath.row]
        return cell
    }
}

// ----- Model Calss -----

class CellObject {
    var title: String!
    var isSelectSwtch: Bool!

    init(title: String, isSelect: Bool = false) {
        self.title = title
        self.isSelectSwtch = isSelect
    }
}



// ----- Custom Cell -----

class CustomTableViewCell: UITableViewCell {

    @IBOutlet weak var cellSwitch: UISwitch!
    @IBOutlet weak var titleLabel: UILabel!

    var cellObject: CellObject! {
        didSet {
            titleLabel?.text = cellObject.title
            cellSwitch.isOn = cellObject.isSelectSwtch
        }
    }

    @IBAction func changeSwitch(_ sender: UISwitch) {

        cellSwitch.isOn = !cellSwitch.isOn
        cellObject.isSelectSwtch = cellSwitch.isOn
    }
}

回答追記

Delegateを使用してセルのボタンが変更された時に、データを変更して戻しています。

import UIKit

// ----- ViewController -----

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, CustomTableViewCellDelegate {

    @IBOutlet weak var tableView: UITableView!
    private var dataArray: Array<CellObject> = []

    override func viewDidLoad() {
        super.viewDidLoad()

        createCellObject()
    }

    func createCellObject() {
        dataArray.append(CellObject(title: "title1", isSelect: true))
        dataArray.append(CellObject(title: "title2", isSelect: false))
        dataArray.append(CellObject(title: "title3", isSelect: true))
        dataArray.append(CellObject(title: "title4", isSelect: false))
    }


    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataArray.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath as IndexPath) as! CustomTableViewCell
        cell.cellObject = dataArray[indexPath.row]
        cell.delegate = self
        return cell
    }

    func updateCellObject(object: CellObject) {

        dump(object)
        // スイッチの状態が変更されたデータが渡されてくるのでRealmに保存
    }
}

// ----- Model Calss -----

class CellObject {
    var title: String!
    var isSelectSwtch: Bool!

    init(title: String, isSelect: Bool = false) {
        self.title = title
        self.isSelectSwtch = isSelect
    }
}



// ----- Custom Cell -----

protocol CustomTableViewCellDelegate: class {
    func updateCellObject(object: CellObject)
}

class CustomTableViewCell: UITableViewCell {

    @IBOutlet weak var cellSwitch: UISwitch!
    @IBOutlet weak var titleLabel: UILabel!

    weak var delegate: CustomTableViewCellDelegate!

    var cellObject: CellObject! {
        didSet {
            titleLabel?.text = cellObject.title
            cellSwitch.isOn = cellObject.isSelectSwtch
        }
    }

    @IBAction func changeSwitch(_ sender: UISwitch) {

        cellSwitch.isOn = !cellSwitch.isOn
        cellObject.isSelectSwtch = cellSwitch.isOn

        // DelegateでViewControllerに処理を渡す
        delegate?.updateCellObject(object: cellObject)
    }
}

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/10/22 08:14

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

    やりたいことはRealmのデータベースのデータを表示し、そこに表示されているBool値を変更したいということです。
    スイッチが押されたタイミングでデータベースにアクセスし、データベース本体のBool値をチェンジしたいと思ってます。
    データベースにアクセスするためにプライマリー値であるIDの値が必要と思ってます。
    テストしている画像を付けます

    キャンセル

  • 2016/10/22 08:38

    回答に追記しました、後はご自身のコードに合わせて適時修正してください。
    各セルのデータをセルに渡して、スイッチの変更をデータにもに反映してViewControllerに戻しています。

    ViewController側ではそれを保存すれば良いだけだと思います。

    キャンセル

  • 2016/10/22 23:45

    ご返事ありがとうございます。
    分かりやすい解説で本当に助かりました。

    私の環境では
    cellSwitch.isOn = !cellSwitch.isOn
    cellObject.isSelectSwtch = cellSwitch.isOn
    この部分でデータベースのプライマリーキーが重複するというエラーが出るので
    この部分は削除し、あくまでカスタムセルは値を渡すだけにして、データベスの更新処理は
    ViewController で処理するという事でやっと実装できました。
    ありがとうございました。

    キャンセル

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

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

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

  • Swift

    9165questions

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

  • iOS

    4873questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。