🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
TableView

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

iOS

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

button

HTMLで用いる<button>タグです。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

Q&A

解決済

3回答

3135閲覧

セルの番号とtag番号を揃えたい

Larry

総合スコア28

TableView

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

iOS

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

button

HTMLで用いる<button>タグです。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

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

0グッド

0クリップ

投稿2019/10/27 13:22

編集2019/10/28 03:17

前提・実現したいこと

[単語または文章を入力し、それらを読み上げる機能]

tableViewのセルの番号とそれらに紐付けたボタンtagの番号を揃えたい

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

下記のコードでは、セルに配置されたボタンがどのセル番号のボタンなのかどうかはbuttonのtagを用いて判定しています。 セルの並び替えを行うとセルの内容は更新するようにできましたが、tag番号は変わらないため、選択した単語と読み上げる単語に齟齬が生まれてしまいます。 初心者なので分かりませんが、 let item = words[sourceIndexPath.row] words.remove(at: sourceIndexPath.row) words.insert(item, at: destinationIndexPath.row) のように順番を一気に差し替えるような方法があったりするのでしょうか。 ご教授いただければ幸いです。よろしくお願いします。

該当のソースコード

swift

1import UIKit 2import AVFoundation 3 4class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 5 6 @IBOutlet weak var tableView: UITableView! 7 8 var speak:AVSpeechSynthesizer = AVSpeechSynthesizer() 9 var words = [String]() 10 var cellNumber = 0 11 12 override func viewDidLoad() { 13 super.viewDidLoad() 14 tableView.rowHeight = UITableView.automaticDimension 15 self.tableView.tableFooterView = UIView() 16 17 navigationItem.leftBarButtonItem = editButtonItem 18 let addButton = UIBarButtonItem(barButtonSystemItem: .add, 19 target: self, 20 action:#selector(onAddTapped(_:))) 21 navigationItem.rightBarButtonItem = addButton 22 23 tableView.delegate = self 24 tableView.dataSource = self 25 } 26 //セルを追加 27 func add(_ word: String){ 28 let index = 0 29 words.insert(word, at: index) 30 let indexPath = IndexPath(row: index, section: 0) 31 tableView.insertRows(at: [indexPath], with: .right) 32 } 33 34 @objc 35 func onAddTapped(_ sender: Any) { 36 37 let alert = UIAlertController(title: "Edit Name", message: "Enter Chinese",preferredStyle: .alert) 38 alert.addTextField {(ChineseTH) in 39 ChineseTH.placeholder = "words" 40 } 41 42 let OkAction = UIAlertAction(title: "Add", style: UIAlertAction.Style.default) { (_) in 43 guard let word = alert.textFields?.first?.text else { return } 44 self.add(word) 45 print("OK") 46 } 47 alert.addAction(OkAction) 48 present(alert, animated: true, completion: nil) 49 50 } 51 52 //セルTap時 53 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 54 tableView.deselectRow(at: indexPath, animated: true) 55 } 56 //挿入セルの内容 57 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->UITableViewCell { 58 59 let cell = UITableViewCell(style: UITableViewCell.CellStyle.subtitle, reuseIdentifier:"none") 60 let word = words[indexPath.row] 61 62 cell.textLabel?.text = word 63 cell.textLabel?.numberOfLines=0 64 65 if let transform = word.applyingTransform(.mandarinToLatin, reverse: false) { 66 cell.detailTextLabel?.text = transform 67 cell.detailTextLabel?.numberOfLines=0 68 } 69 70 //UIButton 71 let speakButton = UIButton(type: .system) 72 speakButton.addTarget(self, action: #selector(buttonTapped(_:)), for: UIControl.Event.touchUpInside) 73 speakButton.setImage(#imageLiteral(resourceName: "audio"), for: .normal) 74 speakButton.frame = CGRect(x:0, y:0, width:30, height:30) 75 speakButton.tag = indexPath.row + cellNumber 76 cellNumber = cellNumber + 1 77 cell.accessoryView = speakButton 78 79 return cell 80 } 81 82 @objc func buttonTapped(_ sender: UIButton) { 83 let content = AVSpeechUtterance(string: words[words.count - 1 - sender.tag]) 84 content.voice = AVSpeechSynthesisVoice(language: "zh-CN") 85 self.speak.speak(content) 86 } 87 88 //編集モード 89 override func setEditing(_ editing: Bool, animated: Bool) { 90 super.setEditing(editing, animated: animated) 91 tableView.setEditing(editing, animated: animated) 92 } 93 94 //順序入れ替え 95 func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { 96 return true 97 } 98 99 func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { 100 101 let item = words[sourceIndexPath.row] 102 words.remove(at: sourceIndexPath.row) 103 words.insert(item, at: destinationIndexPath.row) 104 } 105 106 //スワイプで削除するか 107 func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { 108 if editingStyle == .delete { 109 words.remove(at: indexPath.row) 110 tableView.deleteRows(at: [indexPath], with: .right) 111 } else if editingStyle == .insert {} 112 } 113 114 override func didReceiveMemoryWarning() { 115 super.didReceiveMemoryWarning() 116 } 117 118 func numberOfSections(in tableView: UITableView) -> Int { 119 return 1 120 } 121 122 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 123 return words.count 124 } 125 126} 127 128 129

補足情報(FW/ツールのバージョンなど)

xcode11.1

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fuzzball

2019/10/28 01:38

セル番号というのは cellNumber のことですか?
hameji

2019/10/28 02:20

というか、まず UITableViewCellのcustom classを作った方がいいと思いますが、、、 そうすれば、見やすくなりますし、 cellのrowとbuttonのtagを揃えやすいと思います。 そして、cellはinsertRowsではなく、 wordsの数に合わせて描出するコードにして、 tableviewはrelaoadで表示を変えるのがいいのではないですか?
Larry

2019/10/28 03:09

fuzzballさん indexpath.rowのことです
fuzzball

2019/10/28 03:49

じゃあ speakButton.tag = indexPath.row でいいのでは?同じにしたいなら。
guest

回答3

0

deleteRows,insertRowなどのセルのアニメーションを利用したい場合や
reloadDataした場合の画面のチラツキが気になるという場合reloadData()できないので、
セルの編集(挿入、削除、並び替え)が頻繁に発生するtableViewを作る場合は、tagにcellForRowAtで番号を付与するのではなく、ボタンが押されたセルのindexPath、セクションのindexindexPathForCellrectForSectionから取得するようにすると、
tagの管理からは開放されると思います。
質問者さんがほしいtagというのがindexPath.rowならばそれがいいんじゃないでしょうか?

セルからVCへ送るのはセル自身でも、tag:Intでも、タップされたボタンでも都合のいいものを送って
VC側で送られてきたものからindexPathを取り出せばいいんじゃないの?

swift

1// カスタムセル内 2 3weak var customCellDelegate: CustomCellDelegate? 4 5@IBAction func btnDidTap(_ sender: UIButton) { 6 customCellDelegate?.doSomething(cell: self) 7} 8 9 10// delegate 11protocol CustomCellDelegate: class { 12 func doSomething(sell: UITableViewCell) 13} 14 15 16// VC 17func doSomething(cell: UITableViewCell) { 18 guard let indexPath = tableView.indexPath(for: cell) else { return } 19 20 // 取得したindexPathをつかってなんかする。 21 22}

これはボタンかな。

https://teratail.com/questions/160978

あなたがいつもやってることで、取り立ててコードをとかいうものではないですよ。

投稿2019/10/28 05:01

編集2019/10/30 08:47
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/10/30 07:54 編集

方向性がひどいと思って回答したのですが、まだ続いているようなので… 適度なアニメーションと過度なアニメーションは区別して扱うべきです。 アニメーションなしで画面遷移させる製作者がいないのと同じようなもので、 デフォルトで用意されているセルのアニメーションは適度であり、リロードしてしまうと、ユーザーが自分の操作がどのセルに反映されたか直感的にはわからなくなってしまうと私は思っています。 またこの場合、 > 安全なのはwordsの削除、reloadだと思います。 ではなく、tagを持たない方法か、tagの内容を都度編集することで、この回答は怠慢なだけだと思います。 teratailではreload、reloadと半ば呪文のように回答されていますが、とりあえずreloadDataしてみれば?っていうようなもので、reloadDataがtableViewの編集全てに推奨されるならば、 ↓こういったことがなぜ考えられているのでしょうか? https://medium.com/eureka-engineering/uicollectionview%E3%82%84uitableview%E3%81%AEreloaddata%E3%82%92%E5%91%BC%E3%81%B6%E5%BF%85%E8%A6%81%E3%81%AF%E3%81%BB%E3%81%A8%E3%82%93%E3%81%A9%E3%81%AA%E3%81%84-17a0635a54a9 質問ではinsertRows/deloteRowsとなっているので、質問者がアニメーションなんかいらないよ〜といってない限りは 不必要なリロードは積極的には進めないほうが良いかと思います。
hameji

2019/10/30 05:57 編集

その通りかもしれませんね。 気をつけます。
退会済みユーザー

退会済みユーザー

2019/10/30 06:34

下のコード、tagを使用しているけれども、編集してないからdeleteRowsでアニメーションさせると漏れなくズレるよ。 それと、APIのsortアニメーションの後にreloadしてるからreloadのチラつきが追いつくような条件なら変な動き(感じ方は人によるけど)をするtableviewになるよ
hameji

2019/10/30 07:33 編集

確かに。全く回答になってなかったですね。 やはりそもそもreloadしない場合は、 tagを編集しなおさないと、 tagを利用して読み上げるのに無理があるということですね。 普通にindexPathを利用するべきですね。
退会済みユーザー

退会済みユーザー

2019/10/30 07:43

べきかは知らない、どっちか便利な方選べばいいんじゃない
hameji

2019/10/30 08:01

そもそもずれるindexPathをうまくcustomCell内へ渡す方法が 思いつかなかったのですが、どうやるんですか? 自分の解決策は苦肉の策的ですが、回答に修正で載せておきます。
退会済みユーザー

退会済みユーザー

2019/10/30 08:22 編集

ずれる? 2番目消したら、3番目が2番目になるから、消えた2番目のセルをタップする能力がない限り、ずれは認識できないかと、 insertRows/deleteRows/sortAPI/beginUpdate/endUpdateが何のためにあるのかと、 delegateで、cellをVCに飛ばしてindexPathForCellでindexPathをとってみれば? めんどくさいなら全部reloadDataでもいいんじゃない
hameji

2019/10/30 08:24 編集

質問者が望んでるのはセルのタップではなく、、、 CustomCellに配置されている、UIButtonに読み上げるfuncを持たせる場合にですよ。 そのボタンに正しいindexPathを渡す方法を教えていただきたいのですが、、、
退会済みユーザー

退会済みユーザー

2019/10/30 08:27 編集

ボタンが乗ってるカスタムセルをデリゲートでVCに送って、indexPathForCellで取れない? tableViewにセルが表示されてる時点でセルは正しいindexPathを持ってるよ。
hameji

2019/10/30 08:28

そういうことやったことないので、コードを示していただけると後学のためになるのですが、、、
退会済みユーザー

退会済みユーザー

2019/10/30 08:30

そこら中に落ちてるけど...
hameji

2019/10/30 08:32 編集

なら簡単にしめせますよね、、、
hameji

2019/10/30 08:40

示さないようなので、自分で試行錯誤してみましたが、 // Delegate protocol CustomCellDelegate { func buttonPressedA(word: String) func buttonPressedB(cell: UITableViewCell) } // CustomCell class CustomCell: UITableViewCell { @objc func buttonTapped() { self.delegate?.buttonPressedA(word: word) self.delegate?.buttonPressedB(cell: self) } } // ViewController func buttonPressedB(cell: UITableViewCell) { let indexPath = self.tableView.indexPath(for: cell) read(word: words[indexPath.row]) } で実現はできますが、UITableViewCell自体を渡さないといけないので、 おすすめの方法ではないですね。
thyda.eiqau

2019/10/30 08:46

っていうかbuttonTappedのsenderのsuperViewをたどっていけば、いずれUITableViewCellにたどり着いて[self indexPathforCell:]で取れる気がしてきたのですが、いかがでしょうか……
退会済みユーザー

退会済みユーザー

2019/10/30 08:59 編集

タイピングできる環境にきて上に書いておきました。 そのとおりで、indexPathが取れればいいんじゃないの? tagじゃないからセルの管理をしてあれば、tagの管理を気にかける必要はないでしょ。 word てのはなんだか知らないけど、VC側でModel(配列?)からindexPathをつかって取得するんでないの? そらで let point = tTableView.convert(tappedBtn.center, from: tappedBtn) とか書くのだるいじゃん セル渡してindexPathForCellでインデックスパス取ればのほうが楽でしょ あとはあなたが最適な方法でインデックスパスを取得すればいいじゃない、だってできるでしょ自分で。あなた回答者なんでしょ、質問なら質問たててください。なら簡単にしめせますよね、、、とかどうかしてませんか?
hameji

2019/10/30 09:00 編集

tyobigorouさん、それ試行錯誤で実現できました。 thyda.eiquaさんありがとうございます。 引数をsender: UIButtonに変えて見て、下記でも取得できました。 まだ引数がUIButtonなので、UITableViewCell自体よりはいいのかな? func buttonPressedC(sender: UIButton) { let indexPath = self.tableView.indexPath(for: sender.superview!.superview! as! CustomCell) read(word: words[indexPath!.row]) } いろいろな方法がありますが、 もし、大元のデータのwordsを編集せず、tableViewの表示のみ変える場合は、 cell自体のlabelに設定されているStringを送るのが安全な気がします。
退会済みユーザー

退会済みユーザー

2019/10/30 09:05 編集

> cell自体のlabelに設定されているStringを送るのが安全な気がします。 ここまでやってそこに戻るのがようわからん。例えばStringが一意である保証をどっかでするの? ”A”が2個あったら… メンドクサ
hameji

2019/10/30 09:10 編集

今回の場合には当てはまりませんが、 wordsを削除せずに、tableViewのdeleteRowで非表示にするのみの場合、 例えば、単語帳で正解したら、非表示にする等で元データは削除しない (正解回数などの変数を設定して、読み込みを変える等も考えられますが、、、) indexPath.rowで3であっても、そのwordがwordsのindex 3に当たらない可能性ができませんか? まぁ、違う場合などは考えなくていいですね、、、
退会済みユーザー

退会済みユーザー

2019/10/30 09:30 編集

デリゲートで送ってindexPathとってみて、取得したindexPathを使って配列から取得した値?と、cellにあるラベルのtextの値かな?があってるか検証してみたらどうですか。 私は今wordとwordsとか理解していないのでこの指示であってるかわかりませんが、 reloadData()しないと不安になるんじゃないですか?、swiftUIで簡単に全部が置き換わらないくらい、tableViewを編集するメソッドは結構良く出来てるもんですよ。 単語帳? 正解したら非表示にする? 元データを削除するか、元データが存在しないデータソース変えるか?かな? 基本は配列の要素を一個消したら、セルも一個消す。配列に要素を一個追加したら、セルも一個追加する。 モデルを操作したら、対応するViewも操作する。この過程でセルの数とか間違うとエラー履いて動かなくなる。 面倒ならばreloadDataでモデルの状態にViewを強制的にあわせちゃう、でもアニメーションはしない。 例えば8このセルしか表示できない10個のセルが存在するtableViewに11個目のセルの情報をモデルにappendした際にreloadDataすると、画面がちらつくだけで、追加したセルはユーザーには認識できない。 これが問題だと思う人はinsertなりすればいいし、UI/UXガン無視ならreloadData()でいいんじゃないの?
hameji

2019/10/30 10:05

丁寧にありがとうございます。 自分の回答の誤りを指摘され、直す方向は示されてましたが、 その具体的な実装の仕方がなかったので、しつこく聞いてしまいました。 非エンジニアなので、例えばindexpathを普通に取ってくるだけだよとか 言われても、方法(method)などがそらでは浮かばないので、 teratailの方針でもあるように具体的に示していただければ助かります。 この大元の質問も初心者マークですし、その方が質問者にもためになると思います。 お付き合いいただきありがとうございました。
Larry

2019/11/02 09:30

tyobigorou様も回答ありがとうございます。 バリバリ参考にします。
guest

0

ベストアンサー

上記二つの回答でピンときているかはわかりませんが、
サンプルコードを作って見ました。

確かにアニメーションが必要なら、insertRows/deleteRowsですし、
不要であれば、reloadDataで更新をかければ解決すると思います。

都合上、コードはつなげて書いてあります。
viewcontrollerにベタ張りすれば動くと思います。

きれいに整えたいなら、コメントにも書きましたが、
別ファイルに分けることをお勧めします。

また、tableviewのeditingに関してはあまり考えずに、そのまま使ったので、
すっきりとしてないかもしれません。

個人的にはstoryboardやxibファイルなどをうまく組み合わせるのが好きです。
こういう場ではコードのみの方が、動くサンプルを渡せて便利ですけどね。

Swift

1import UIKit 2import AVFoundation 3 4class ViewController: UIViewController { 5 6 var speak:AVSpeechSynthesizer = AVSpeechSynthesizer() 7 var tableView: UITableView = UITableView() 8 var words: [String] = [] 9 var mode:Int = 0 // 0: cellのselectで読み上げ, 1: cellのボタンタップで読み上げ 10 11 override func viewDidLoad() { 12 super.viewDidLoad() 13 // modeの設定 -> UISegmentedControlなどを配置し、切り替えにしてもいいかも 14 // 好きな方で行って、不要な方は削除していただいてOK 15 mode = 0 16 // NavigationBarの設定 17 navigationItem.leftBarButtonItem = editButtonItem 18 let addButton = UIBarButtonItem(barButtonSystemItem: .add, 19 target: self, 20 action:#selector(onAddTapped)) 21 navigationItem.rightBarButtonItem = addButton 22 23 // TableViewの設定 24 tableView.frame = self.view.frame 25 tableView.delegate = self 26 tableView.dataSource = self 27 tableView.register(CustomCell.self, forCellReuseIdentifier: "customCell") 28 self.view.addSubview(tableView) 29 } 30 31 // NavigationbarのaddのAction 32 @objc func onAddTapped(sender: Any) { 33 let alert = UIAlertController(title: "Edit Name", message: "Enter Chinese",preferredStyle: .alert) 34 alert.addTextField {(ChineseTH) in ChineseTH.placeholder = "words" } 35 let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) 36 // cancelボタンを追加 37 let OkAction = UIAlertAction(title: "Add", style: .default) { (_) in 38 print("OK pressed") 39 guard let word = alert.textFields?.first?.text, word.count > 0 else { return } 40 // ↑空白入力の時もguardで除外とした 41 self.words.append(word) 42 self.tableView.reloadData() 43 } 44 alert.addAction(cancelAction) 45 alert.addAction(OkAction) 46 present(alert, animated: true, completion: nil) 47 } 48 49 // AVSpeechのfunction(わかりやすくするために別funcにした) 50 func read(word: String) { 51 let content = AVSpeechUtterance(string: word) 52 content.voice = AVSpeechSynthesisVoice(language: "zh-CN") 53 self.speak.speak(content) 54 } 55} 56 57extension ViewController: UITableViewDelegate { 58 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 59 if mode == 0 { 60 let word = words[indexPath.row] 61 read(word: word) 62 } 63 } 64 65 override func setEditing(_ editing: Bool, animated: Bool) { 66 super.setEditing(editing, animated: animated) 67 tableView.setEditing(editing, animated: animated) 68 } 69 70 func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { 71 words.remove(at: sourceIndexPath.row) 72 tableView.reloadData() 73 } 74 75 76 func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { 77 if editingStyle == .delete { 78 words.remove(at: indexPath.row) 79 tableView.reloadData() 80 //tableView.deleteRows(at: [indexPath], with: .right) 81 // アニメーションを実現したいということであれば、こちらですね。 82 } 83 } 84 85} 86 87extension ViewController: UITableViewDataSource { 88 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 89 return self.words.count 90 } 91 92 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 93 let cell = tableView.dequeueReusableCell(withIdentifier: "customCell") as! CustomCell 94 cell.label.text = self.words[indexPath.row] 95 cell.button.tag = indexPath.row 96 cell.delegate = self 97 // 上記は、CustomCellのメンバ変数delegate(CustomCellDelegate) を self(ViewController)で記述するという設定です。 98 // なので、CustomCellDelegateをすぐ下にextensionで書いてます。 99 // 要はボタンを押したというfuncをdelegateで渡すことにより、 100 // AVSpeechを各cellで作成せずに済んでますし, 101 // 直接、wordsにアクセスできてシンプルになってます。 102 return cell 103 } 104} 105 106extension ViewController: CustomCellDelegate { 107 func buttonPressed(tag: Int) { 108 if mode == 1 { 109 let word = words[tag] 110 read(word: word) 111 } 112 } 113} 114 115// MARK: -- ここから別ファイルCusotmCell.swiftなどが好ましい、再度import UIKitが必要 116 117class CustomCell: UITableViewCell { 118 119 var label: UILabel! 120 var button: UIButton! 121 var delegate: CustomCellDelegate? 122 123 // CustomCellの初期化設定1 124 override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 125 super.init(style: style, reuseIdentifier: reuseIdentifier) 126 // UILabelの設定 127 label = UILabel(frame: CGRect(x: 20, y: 10, width: 100, height: 21)) 128 label.textAlignment = .left 129 contentView.addSubview(label) 130 // UIButtonの設定 131 button = UIButton(frame: CGRect(x: self.contentView.frame.width, y: 10, width: 46, height: 30)) 132 button.backgroundColor = UIColor.blue 133 button.addTarget(self, action: #selector(buttonTapped), for: UIControl.Event.touchUpInside) 134 button.setTitle("押す", for: .normal) 135 contentView.addSubview(button) 136 } 137 138 // CustomCellの初期化設定2 139 required init?(coder: NSCoder) { 140 fatalError("init(coder:) has not been implemented") 141 } 142 143 @objc func buttonTapped(sender: UIButton) { 144 self.delegate?.buttonPressed(tag: sender.tag) 145 } 146} 147 148// MARK: -- ここから別ファイルCusotmCellDelegate.swiftなどが好ましい、import UIKitは不要 149 150protocol CustomCellDelegate { 151 func buttonPressed(tag: Int) 152}

=========================================
2019/10/30 更新

お恥ずかしながら、tyobigorouさんに指摘され気付いたんですが、
deleteRowによるindexPathとUIButtonのtagのズレ問題の解決をできてなかったので
追記します。

アニメーションが必要ということで、insertRows/deleteRowsを使う方向で、
deleteRowsするとindexPathが減り、tagはそのままとなって確実にズレますね。

reloadData()が頭にあったので、上記のサンプルではそこを考慮できてなかったです。
cellForRowAt以外でdequeueReusableCellを呼び出すのはおすすめできないので、
tagを更新するのは、reloadData()で行う以外のいい方法は思いつかないです。

tyobigorouさんも言ってますが、UIButtonのtagを使わず代わりに、
普通にindexPathを利用して行うのがいいのではないでしょうか。

tagを使わない方法としては、
以下に修正したコードを載せて見ます。
cellForRowatでのcell.button.tag = indexPath.rowは不要となります。
メインはcustomCellのボタンのfunctionとCustomCellDelegateの部分を変更しました。

Swift

1 2extension ViewController: CustomCellDelegate { 3 func buttonPressed(word: String) { 4 if mode == 1 { 5 read(word: word) 6 } 7 } 8} 9 10// ~~~~間省略 11 12 @objc func buttonTapped() { 13 if let word = self.label.text { 14 self.delegate?.buttonPressed(word: word) 15 } 16 } 17} 18 19// MARK: -- ここから別ファイルCusotmCellDelegate.swiftなどが好ましい、import UIKitは不要 20 21protocol CustomCellDelegate { 22 func buttonPressed(word: String) 23} 24

indexPathをうまく渡す方法は思いつかなかったので、
labelに表示されているstringを直接渡す方法にしてみました。

これなら、元データを変更せず(wordsでwordを削除せず)に、
表示のみ変更(deleteRowのみした)場合にもきちんと該当の単語を読み上げてくれると思います。

投稿2019/10/28 19:24

編集2019/10/30 08:09
hameji

総合スコア1380

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Larry

2019/10/29 13:56

回答誠にありがとうございます。 サンプルコードの作成に関しましても、非常に感謝しております。 extensionやCustomCellとか使ったことがないので今必死に調べて理解している最中です。 一点、僕の説明不足で勘違いをさせてしまったことかも知れませんが、 音声を再生は、セルをタップした時ではなくて、セルに設置したボタンを押した時に実行したいです。 載せていただいたコードを参考にもう少し考えてみたいと思いますが、また何かアドバイスがあれば教えていただきたいです!
hameji

2019/10/29 14:03

var mode:Int = 0 // 0: cellのselectで読み上げ, 1: cellのボタンタップで読み上げ と、最初の方に書いてあると思いますが、 メンバ変数の宣言のとことviewDidLoad()内で mode = 1としていただければ、ボタンを押すことで再生できますよ。
Larry

2019/10/29 14:18

ああ、すみません。見逃していました。。。 後一点質問ですが、セルを追加する時アニメーションを入れたいのですが そうなるとやはり tableView.insertRows(at: [indexPath], with: .right) を使うしかありませんか? hamejiさんが最初に 「そして、cellはinsertRowsではなく、 wordsの数に合わせて描出するコードにして、 tableviewはrelaoadで表示を変えるのがいいのではないですか?」 とコメントしていただいていますが、これは何か理由があったりしますか?
hameji

2019/10/29 14:34

reloadが推奨されるのは、個別のデータを削除していくと、 データが合わなくなりやすいからだと思います。 安全なのはwordsの削除、reloadだと思います。 アニメーションは作る人はかっこいいと思うかもしれませんが、 意外と目に付くだけの場合が多いですからね。 ex) パワーポイントでのプレゼン等でアニメーションの多様は避けるのが望ましいなど
Larry

2019/10/29 16:18

確かにそうかも知れませんね、、勉強になります。(^ ^) 最後にもう一つお聞きしたいのですが、 セルの表示スタイルをsubtitleにしたいのですが、 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)に cell.detailTextLabel?.text = "aiueo" を追加して super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)に変更したのですが、 一段目と二段目の文字が被ってしまい、うまく表示されないのですが、どうすればいいですか。 自分で考えろと言われるかも知れませんが、よろしければ教えていただきたいです。お願いします。
hameji

2019/10/29 17:07

コードからやるなら、自分だったら、 .subtitleを用いずに、手動でUILabelをTableViewCellに追加します。 その方が文字の大きさや微妙な位置、色や装飾など自由度が高いです。
hameji

2019/10/30 08:10

上記の答え修正しました、ご確認ください。
Larry

2019/11/02 09:31

回答ありがとうございました。なんとか解決しました。 親身に教えてくださり、非常に感謝しております。
guest

0

insertRows/deleteRowsって必要ですか?wordsの要素を編集したあとにreloadDataすればよいと思います

ちょっといま手元にmacが無くて、記憶を頼りに書いてるのでちょっと変なところがあるかもしれないし、書きなれているObjective-Cで恐縮ですが、方向性の提示ということでひとつご容赦いただければ

objc

1-(IBAction) buttonTapped(UIButton *sender) { 2 UIView *maybeCell = sender; 3 NSIndexPath *indexPath = nil; 4 while(maybeCell.superview) { 5 maybeCell = maybeCell.superview; 6 if([maybeCell isKindOfClass:UITableViewCell.class]) { 7 indexPath = [self indexPathForCell:maybeCell]; 8 break; 9 } 10 } 11 if(!indexPath) return; 12 13 /* 14 let content = AVSpeechUtterance(string: words[words.count - 1 - indexPath.row]) 15 content.voice = AVSpeechSynthesisVoice(language: "zh-CN") 16 self.speak.speak(content) 17 */ 18}

投稿2019/10/28 03:28

編集2019/10/30 08:47
thyda.eiqau

総合スコア2982

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問