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

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

ただいまの
回答率

87.37%

Facebookのコメント投稿画面のようにタイムライン画面から遷移後のページでコメント投稿を実装したい

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 1,366

score 12

前提・実現したいこと

Xcode11.3
Swift5
macOSMojave10.14.6

フェイスブックのコメントボタンを押した時のような、TableViewのタイムライン画面から個別の画面に遷移

TextFieldにコメントを入力後、投稿ボタンを押すと、遷移前のタイムライン画面でタップしたセルと同じポストデータにコメント文が追加され、遷移後のページにあるUILabelかUITextViewにコメントが投稿される

別のユーザーがコメント投稿すると下に増えていく

タイムライン画面ではUItableViewを使い、それぞれのセルはxibファイルを使い、データベースはFirebaseを使ってます。
遷移先にはUIView,UIButton,UILabel(UITextView)があります。

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

上記
「遷移前のタイムライン画面でタップしたセルと同じポストデータにコメント文が追加され、遷移後のページにあるUILabelかUITextViewにコメントが投稿される」
の実装がわからずあれこれやってもindexPath.rowを渡す・受け取るの部分ができません。

該当のソースコード

●タイムライン画面

import UIKit
import Firebase

class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var tableView: UITableView!

    var postArray: [PostData] = []

    // DatabaseのobserveEventの登録状態を表す
    var observing = false

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.delegate = self
        tableView.dataSource = self

        let nib = UINib(nibName: "PostTableViewCell", bundle: nil)
        tableView.register(nib, forCellReuseIdentifier: "Cell")

        // テーブル行の高さをAutoLayoutで自動調整する
        tableView.rowHeight = UITableView.automaticDimension
        // テーブル行の高さの概算値を設定しておく
        // 高さ概算値 = 「縦横比1:1のUIImageViewの高さ(=画面幅)」+「いいねボタン、キャプションラベル、その他余白の高さの合計概算(=100pt)」
        tableView.estimatedRowHeight = UIScreen.main.bounds.width + 100
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("DEBUG_PRINT: viewWillAppear")

        if Auth.auth().currentUser != nil {
            if self.observing == false {
                // 要素が追加されたらpostArrayに追加してTableViewを再表示する
                let postsRef = Database.database().reference().child(Const.PostPath)
                postsRef.observe(.childAdded, with: { snapshot in
                    print("DEBUG_PRINT: .childAddedイベントが発生しました。")

                    // PostDataクラスを生成して受け取ったデータを設定する
                    if let uid = Auth.auth().currentUser?.uid {
                        let postData = PostData(snapshot: snapshot, myId: uid)
                        self.postArray.insert(postData, at: 0)

                        // TableViewを再表示する
                        self.tableView.reloadData()
                    }
                })
                // 要素が変更されたら該当のデータをpostArrayから一度削除した後に新しいデータを追加してTableViewを再表示する
                postsRef.observe(.childChanged, with: { snapshot in
                    print("DEBUG_PRINT: .childChangedイベントが発生しました。")

                    if let uid = Auth.auth().currentUser?.uid {
                        // PostDataクラスを生成して受け取ったデータを設定する
                        let postData = PostData(snapshot: snapshot, myId: uid)

                        // 保持している配列からidが同じものを探す
                        var index: Int = 0
                        for post in self.postArray {
                            if post.id == postData.id {
                                index = self.postArray.firstIndex(of: post)!
                                break
                            }
                        }

                        // 差し替えるため一度削除する
                        self.postArray.remove(at: index)

                        // 削除したところに更新済みのデータを追加する
                        self.postArray.insert(postData, at: index)

                        // TableViewを再表示する
                        self.tableView.reloadData()
                    }
                })

                // DatabaseのobserveEventが上記コードにより登録されたため
                // trueとする
                observing = true
            }
        } else {
            if observing == true {
                // ログアウトを検出したら、一旦テーブルをクリアしてオブザーバーを削除する。
                // テーブルをクリアする
                postArray = []
                tableView.reloadData()
                // オブザーバーを削除する
                let postsRef = Database.database().reference().child(Const.PostPath)
                postsRef.removeAllObservers()

                // DatabaseのobserveEventが上記コードにより解除されたため
                // falseとする
                observing = false
            }
        }
    }
    //セルの数を決めるメソッド
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return postArray.count
    }
    //セルを構築する際に呼ばれるメソッド
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // セルを取得してデータを設定する
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! PostTableViewCell
        cell.setPostData(postArray[indexPath.row])

        // セル内のボタンのアクションをソースコードで設定する
        cell.likeButton.addTarget(self, action:#selector(handleButton(_:forEvent:)), for: .touchUpInside)

        return cell
    }

    //セルをタップしたら...のメソッド
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        // タップされたセルの行番号を出力
        print("\(indexPath.row)番目の行が選択されました。")

        let postData = postArray[indexPath.row]

        //記事画面へ遷移(セグエ)
          let nextVC = self.storyboard?.instantiateViewController(withIdentifier: "Article") as! ArticleViewController
        //completionでSetPostDataに渡す
            self.present(nextVC, animated: true, completion: {
                nextVC.SetPostData(postData)
            })
        performSegue(withIdentifier: "Article", sender: postArray[indexPath.row])
        //セルの選択を解除
        tableView.deselectRow(at: indexPath, animated: true)

    }    
}


●遷移後のコメント投稿画面

import UIKit
import Firebase
import SVProgressHUD

class ArticleViewController: UIViewController,UITextFieldDelegate {

    @IBOutlet weak var ArticleImage: UIImageView!
    @IBOutlet weak var ArticleLabel: UILabel!
    @IBOutlet weak var ArticleButton: UIButton!
    @IBOutlet weak var ArticleLilkeCount: UILabel!
    @IBOutlet weak var CommentLabel: UILabel!
    @IBOutlet weak var CommentTextField: UITextField!
    //postarrayの配列
    var postArray: [PostData] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        self.CommentTextField.delegate = self
        CommentTextField.returnKeyType = .done    
    }

    func SetPostData(_ postData: PostData) {
        //イメージ画像
        self.ArticleImage.image = postData.image
        //キャプションのテキスト
        self.ArticleLabel.text = "\(postData.name!) : \(postData.caption!)"
        //いいねの数
        let likeNumber = postData.likes.count
        ArticleLilkeCount.text = "\(likeNumber)"

        //いいねボタン
        if postData.isLiked {
            let buttonImage = UIImage(named: "like_exist")
            self.ArticleButton.setImage(buttonImage, for: .normal)
        } else {
            let buttonImage = UIImage(named: "like_none")
            self.ArticleButton.setImage(buttonImage, for: .normal)
        }
        //allCommentは最初は空である
        var allComment = ""

        //postData.commentsの中から要素をひとつずつ取り出すのを繰り返す、というのがcomment
        for comment in postData.comments{
        //comment + comment = allCommentである
        allComment += comment
        //commentLabelに表示するのはallComment(commentを足していったもの)である
        self.CommentLabel.text = allComment
        }
    }

    //キーボードを閉じる
    @IBAction func textField(_ sender: Any) {
        CommentTextField.text = (sender as AnyObject).text
    }
    //コメント投稿ボタン
    @IBAction func CommentButton(_ sender: UIButton,_ postData: PostData,indexPath: IndexPath) {

        let indexPath = postArrey[indexPath.row],
        //現在の使用者の表示名がnilじゃなかったら、displayNameに代入し、
        if let displayName = Auth.auth().currentUser?.displayName,
        //cell.textField.textをtextと定義する
        let commentText = self.CommentTextField.text  {

        //であれば、cell(PostTableViewCell)のtextFieldをプリントする
        print(self.CommentTextField.text as Any)

        //であれば、postData.commentsにtextをappend(追加)する
        postData.comments.append("\(displayName): \(commentText)\n")

        }

        // 増えたcommentsをFirebaseに保存する
        let postRef = Database.database().reference().child(Const.PostPath).child(postData.id!)
        //comments辞書
        let commentDictionary = ["comments": postData.comments]
        //Firebaseに辞書を保存する
        postRef.updateChildValues(commentDictionary)
    }
}

試したこと

遷移前ページのdidSelectRowAt内で

performSegue(withIdentifier: "Article", sender: postArray[indexPath.row])

でセルの行番号を送ろうとしていますが、合っているのかがわかりません。

遷移後ページのアクションボタン内で

@IBAction func CommentButton(_ sender: UIButton,_ postData: PostData,indexPath: IndexPath) {

        let indexPath = postArrey[indexPath.row],
        //現在の使用者の表示名がnilじゃなかったら、displayNameに代入し、
        if let displayName = Auth.auth().currentUser?.displayName,
        //cell.textField.textをtextと定義する
        let commentText = self.CommentTextField.text  {

        //であれば、cell(PostTableViewCell)のtextFieldをプリントする
        print(self.CommentTextField.text as Any)

        //であれば、postData.commentsにtextをappend(追加)する
        postData.comments.append("\(displayName): \(commentText)\n")

        }


遷移前でのセル行番号を取得する方法がわかりません。
色々調べてもたどり着けませんでした。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

check解決した方法

+1

遷移元のViewContoroller

//セルをタップしたら...のメソッド
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        // タップされたセルの行番号を出力
        print("\(indexPath.row)番目の行が選択されました。")

        let postData = postArray[indexPath.row]

        postDataToSend = postData
            performSegue(withIdentifier: "Article", sender: tableView)
            //セルの選択を解除
            tableView.deselectRow(at: indexPath, animated: true)

        }

        var postDataToSend: PostData?

        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if segue.identifier == "Article" {
                let nextVC = segue.destination as! ArticleViewController
                if let postData = postDataToSend {
                    nextVC.setPostData(postData)
                }
            }     
    }


遷移先のViewController

class ArticleViewController: UIViewController,UITextFieldDelegate {

    @IBOutlet weak var articleImage: UIImageView!
    @IBOutlet weak var articleLabel: UILabel!
    @IBOutlet weak var articleButton: UIButton!
    @IBOutlet weak var articleLilkeCount: UILabel!
    @IBOutlet weak var commentTextView: UITextView!
    @IBOutlet weak var commentTextField: UITextField!
//前画面からデータを受け取るための変数
    var postDataReceived: PostData?

    func setPostData(_ postData: PostData) {
        postDataReceived = postData
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        guard let postData = postDataReceived else {
            return
        }
        //イメージ画像
        self.articleImage.image = postData.image
        //キャプションのテキスト
        self.articleLabel.text = "\(postData.caption!)"
        //いいねの数
        let likeNumber = postData.likes.count
        articleLilkeCount.text = "\(likeNumber)"

        //いいねボタン
        if postData.isLiked {
            let buttonImage = UIImage(named: "like_exist")
            self.articleButton.setImage(buttonImage, for: .normal)
        } else {
            let buttonImage = UIImage(named: "like_none")
            self.articleButton.setImage(buttonImage, for: .normal)
        }
        //allCommentは最初は空である
        var allComment = ""

        //postData.commentsの中から要素をひとつずつ取り出すのを繰り返す、というのがcomment
        for comment in postData.comments{
            //comment + comment = allCommentである
            allComment += comment
            //commentLabelに表示するのはallComment(commentを足していったもの)である
            self.commentTextView.text = allComment
        }
//コメント投稿ボタン
    @IBAction func commentButton(_ sender: UIButton) {
        // 前画面から受け取ったデータを取り出す
        guard let postData = postDataReceived else {
            return
        }

        //cell.textField.textがnilじゃなかったら、commentTextとする
        if let commentText = self.commentTextField.text  {

            //であれば、cell(PostTableViewCell)のtextFieldをプリントする
            print(self.commentTextField.text as Any)

            //であれば、postData.commentsにcommentTextをappend(追加)する
            postData.comments.append("\(commentText)\n")
        }

        // 増えたcommentsをFirebaseに保存する
        let postRef = Database.database().reference().child(Const.PostPath).child(postData.id!)
        //comments辞書
        let commentDictionary = ["comments": postData.comments]
        //Firebaseに辞書を保存する
        postRef.updateChildValues(commentDictionary)

        //allCommentは最初は空である
        var allComment = ""

        //postData.commentsの中から要素をひとつずつ取り出すのを繰り返す、というのがcomment
        for comment in postData.comments{
            //comment + comment = allCommentである
            allComment += comment
            //commentLabelに表示するのはallComment(commentを足していったもの)である
            self.commentTextView.text = allComment
        }
    }

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

コメントボタンは、tableViewcellの中にあるボタンでそれを押し、indexを渡したいといった認識でお間違いなければ、protocolを使ってみてはいかがでしょうか?

【tableViewCell.swift】の一番上 import UIKitの上に

protocol UserTableViewCellDelegate {
    func didTapCommentButton(indexPath: IndexPath)
}

下に

@IBAction func commentButton(){
        delegate!.didTapCommentButton(indexPath: indexPath)
   }


を宣言して、
HomeViewControllerにtableViewCellDelegateを書く

class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate ,tableViewCellDelegate{


HomeViewController内に func didTapCommnetButtonを定義する

func didTapCommentButton(indexPath: IndexPath){
   //ここで行いたい処理をかく
}

これでcellのボタンからindexPath諸々の情報を紐付けることができると思います
間違っていたらすいません!

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/01/13 14:44

    わかりやすく丁寧なご説明ありがとうございます。ちょうど解決いたしましたので、大丈夫となりました。せっかくご回答いただいたのに、申し訳ございません。今後の参考にいたします。

    キャンセル

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

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

関連した質問

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

  • トップ
  • Swiftに関する質問
  • Facebookのコメント投稿画面のようにタイムライン画面から遷移後のページでコメント投稿を実装したい