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

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

ただいまの
回答率

90.34%

  • Swift

    7653questions

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

  • Firebase

    670questions

    Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

表示するデータが一つでも画面を更新するためにUITableViewを使用する必要があるのか

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 345

 前の画面のUICollectionViewでタップされた写真のデータを画面遷移先で取得し、その写真に関する情報を表示させる。

こんにちは、質問失礼致します。
現在Firebaseを使用してSNSアプリを作成しており、ユーザーによって投稿されたデータを元に写真をUICollectionViewで表示し、そのセルがタップされた時にその写真に関するデータを取得したいと考えています。
そこで質問なのですが、UICollectionViewでタップされた後の遷移先の画面でInstagramのいいねボタンの機能を実装すると考えると遷移先の画面で表示するデータは一つなのですがUITableViewを使用した方がいいのでしょうか?
この遷移先の画面を普通のUIViewControllerで実装したところ、いいねボタンをタップした時にボタンをタップしたユーザーの情報が格納される配列に格納し、すでにタップずみなら配列から削除をしていたのですが、ボタンがタップされた時に画面が更新されないので2度3度いいねが出来てしまったりという様なバグが発生してしまいました。なのでUITableViewにUIを表示してボタンがタップされる度に tableView.reloadData で画面を更新すれば解決すると考えたのですが他にいい方法はありますでしょうか?
またUITableViewを使用して表示をした方がいいという意見であれば、どの部分で画面遷移前の画面からデータを取得して遷移後の画面でデータを表示するかの部分を教えていただけるとありがたいです。

 コード

import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase

class SharePhotoUpViewController: UIViewController {

  @IBOutlet weak var profielNameLabel: UILabel!
  @IBOutlet weak var imageView: UIImageView!
  @IBOutlet weak var fightButton: UIButton!
  @IBOutlet weak var commentButton: UIButton!
  @IBOutlet weak var dateLabel: UILabel!
  @IBOutlet weak var moreCountLabel: UILabel!
  @IBOutlet weak var captionTextView: UITextView!
  // 変数photoInformationに前の画面でUICollectionViewでタップされた画像の情報を取得する
  var photoInformation: PostData!
  let formatter = DateFormatter()
  var label = UILabel()


    override func viewDidLoad() {
        super.viewDidLoad()

      label.font = UIFont.systemFont(ofSize: 20)
      label.text = photoInformation?.name

      imageView.image = photoInformation.image
      profielNameLabel.text = photoInformation?.name
      formatter.dateFormat = "yyyy/MM/dd HH:mm"
      let dateString = formatter.string(from: photoInformation.date!)
      dateLabel.text = dateString
      captionTextView.text = "\(label.text!) \(photoInformation.caption!)"

      if photoInformation.isLiked {
        let buttonImage = UIImage(named: "FireWhite48.png")
        self.fightButton.setImage(buttonImage, for: .normal)
      } else {
        let buttonImage = UIImage(named: "FireRed48.png")
        self.fightButton.setImage(buttonImage, for: .normal)
      }

        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

  override func viewWillAppear(_ animated: Bool) {
  self.reloadInputViews()
  }


  @IBAction func handleFightButton(_ sender: Any) {
    print("DEBUG_PRINT: moreボタンがタップされました")
    let postData = photoInformation!

    // Firebaseに保存するデータの準備
    if let uid = Auth.auth().currentUser?.uid {
      if postData.isLiked {   // isLikedには初期値でfalseが設定ずみ
        // すでにfightをしていた場合はいいねを解除をするためにIDを削除する
        // 変数indexにすでにいいねをしていた場合に-1をするために-1を初期化の時点で格納する
        var index = -1
        // for文でloopさせながらpostDataのlikesの中の値を取り出す
        for likeID in postData.likes {
          // もしlikesの中の値が格納されているlikeIDの中に現在ログインしているuidがあった場合
          if likeID == uid {
            index = postData.likes.index(of: likeID)!
            break
          }
        }
        postData.likes.remove(at: index)
      } else {
        // もしmoreをしていなかった場合、appendメソッドでlikesの配列の中に地震のuidを追加する
        postData.likes.append(uid)
      }
      // 増えたlikesをFirebaseに保存する
      // childメソッドでConst.PostPathを指定して、childメソッドで投稿情報のidを指定する
      let postRef = Database.database().reference().child(Const.PostPath).child(postData.id!)
      let likes = ["likes": postData.likes]
      postRef.updateChildValues(likes)

    }
  }

  override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // もしコメントボタンをタップした時はshareButtonSegueで画面遷移をする
    if segue.identifier == "shareButtonSegue" {
      let shareCommentViewController: ShareCommentViewController = segue.destination as! ShareCommentViewController
      print("DEBUG_PRINT: handleCommentActionがタップされました")
      // 画面遷移時に表示されているデータをshareCommentViewControllerの変数に格納する
      shareCommentViewController.photoInformation = photoInformation
    }
  }

}

 試したこと

遷移先のUIViewControllerにUITableViewを配置し、UITableViewCellのxibファイルを作成し、 register  メソッドで呼び出し、 ViewDidLoad メソッドでxibファイルのインスタンスを使ってUI部品に値を格納したが全てのOutlet部品にnilが入ってしまいました。

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

Xcode Version 9.3.1

よろしくお願い致します。

 追加コード

 override func viewWillDisappear(_ animated: Bool) {
    let postData = photoInformation!

    if let uid = Auth.auth().currentUser?.uid {
      if buttonStatus != true {
        print("DEBUG_PRINT: buttonStatusがtrueじゃなかったら")
        // 変数indexにすでにいいねをしていた場合に-1をするために-1を初期化の時点で格納する
        var index = -1
        // for文でloopさせながらpostDataのlikesの中の値を取り出す
        for likeID in postData.likes {
          if likeID == uid {
            print("DEBUG_PRINT: もし配列の中に自身のuidが存在していたら削除する")
            index = postData.likes.index(of: likeID)!
            postData.likes.remove(at: index)
            break
          } else {
            print("DEBUG_PRINT: 配列の中に自身のuidが存在していなかったので何もしない")   // ここの処理がうまくいかない
          }
        }

      } else {
        print("DEBUG_PRINT: buttonStatusがtrueだったら")
        for likeID in postData.likes {
          if likeID == uid {
            print("DEBUG_PRINT: もしも既に配列の中にuidが存在していたら")
            return
          } else {
            print("DEBUG_PRINT: 配列の中に自身のuidが存在しないので配列に追加する")
            postData.likes.append(uid)
          }
        }
      }
      // 増えたlikesをFirebaseに保存する
      // childメソッドでConst.PostPathを指定して、childメソッドで投稿情報のidを指定する
      let postRef = Database.database().reference().child(Const.PostPath).child(postData.id!)
      let likes = ["likes": postData.likes]
      postRef.updateChildValues(likes)
    }
  }
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • tyobigorou

    2018/06/05 08:03 編集

    photoInformation.isLikedはそのユーザーがいいねボタンを押したか押していないかをBoolで格納しているのでしょうか?
    なぜ、viewWillDisappear? 。

    キャンセル

回答 2

checkベストアンサー

+1

分かりづらかったようなので、書き直します。

使うのは普通のUIViewControllerでいいと思います。

postData.isLikedが各々のユーザー固有のものと仮定して。

「いいね?」ボタンを押したときに、「いいね」を押したかどうかを記憶しておく仕組みがなかったので
それを足してあげれば、要望どおりになると思います。

 @IBAction func handleFightButton(_ sender: Any) {
   let postData = photoInformation!

    // Firebaseに保存するデータの準備
    if let uid = Auth.auth().currentUser?.uid {

       // 「いいね」済の場合
       if postData.isLiked == true {
            // 配列から該当IDを削除する
            // uidがが格納されている場所(uidIndex)を特定する
            if let uidIndex = postData.likes.index(of: uid) { 
                // 「いいね」済配列にuidが存在するので、その順番(uidIndex)をもとにuidを削除する              
                postData.likes.remove(at: uidIndex) 
            }
            postData.isLiked = false  // 「いいね」してない状態になったと記憶させる

            // ボタンの見た目を変更する
            let buttonImage = UIImage(named: "FireRed48.png")
            self.fightButton.setImage(buttonImage, for: .normal)

        // 「いいね」してない場合
        } else {
            postData.likes.append(uid) // 「いいね」済配列にuidを追加する
            postData.isLiked = true  // 「いいね」した状態になったと記憶させる

       // ボタンの見た目を変更する
            let buttonImage = UIImage(named: "FireWhite48.png")
            self.fightButton.setImage(buttonImage, for: .normal)
        }

      // 増えたlikesをFirebaseに保存する
      // childメソッドでConst.PostPathを指定して、childメソッドで投稿情報のidを指定する

      let postRef = Database.database().reference().child(Const.PostPath).child(postData.id!)
      let likes = ["likes": postData.likes] 
      /* ここで、postData.isLikedもfireBaseに保存する必要がある */
      postRef.updateChildValues(likes)

    }
  }


で動きますか?
viewWillDisappearの中の処理はいらないと思います。
違和感があるようでしたら、viewWillDisappearの中に処理を部分的に移してください。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/06/05 11:20

    すみません、先ほどの質問に対するご回答にコメントさせていただく前に分かりやすく解説していただいて本当にありがとうございます。
    ボタンをタップした時にif文で分岐し、それに対応するコード(自身のuidを削除、または追加)するコードを書いてからボタンをタップしたかの値のBool値を格納しなおせばよかったのですね。そこに気付かずに問題の解決まで時間を撮らせてしまった事に関してお詫びいたします。おかげさまで狙っていた機能を実装することが出来ました。本当に長々とお付き合いいただきありがとうございました。

    キャンセル

  • 2018/06/05 14:03

    なぜ、viewWillDisappear?

    キャンセル

  • 2018/06/05 14:07

    結果的にtyobigoroさんのアドバイスをいただいた通り、ボタンのActionメソッドで処理を記述しました。最初の段階ではボタンをタップする度にappendメソッドで追加をしてしまっていたので、ボタンのActionメソッドの中ではボタンの表示の切り替えだけを行い、viewWillDisappearメソッドの中でFirebaseに追加、削除をすれば使ったときの違和感はあまり感じないと思い、viewWillDisappearメソッドのなかで記述をしていました。
    最終的にはご教授していただいたお陰でボタンをタップしたときのActionメソッドのなかで処理を行うことが出来ました、本当にお世話になりました。

    キャンセル

  • 2018/06/05 16:23 編集

    見覚えのある口調の文章と、糠に釘感がでているので、書いておきます。

    回答者は、どの質問に回答するかを質問者の氏名を見て選べるものだと思いますので、
    もし、お一人で複数アカウントをご使用でしたら使用をご遠慮いただけないでしょうか?

    私の勘違いでしたら、見当違いの指摘をしてもうしわけありませんでした。

    キャンセル

  • 2018/06/07 18:31

    私はつい最近このサービスを利用し始めたのでこのアカウントしか利用していません。
    お気になさらないでください。
    ご回答ありがとうございました。

    キャンセル

  • 2018/06/07 18:39

    間違った指摘のようで。気分を害すようなことを書いてしまい申し訳ありませんでした。

    キャンセル

+1

ただのViewControllerにも
self.reloadInputViews() というのがあるので、 それできっとreloadできると思います。 更新したいタイミングでそれを読み込んでみてください。

→これでは解決しませんでした。

サンプルを走らせていて思ったのですが、ボタンを押すのでしたら、
そのタイミングで直接更新したいのもの値を変え、ボタンを無効にするか、

いいねのON, OFF機能をずっと保持するなら、
保存の値をtrue, falseで保持すれば、
trueが2回にはなること(保存の値が重なること)は
条件で避けられるかと思います。
いかがでしょうか?

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/06/03 04:50 編集

    すみません、色々考えご回答にコメントさせていただこうと考えたのですが、最初にコードをみていただく方がアドバイスしていただける際にし易いと思い、質問に追加で現在の画面が更新できず何回も自身のidを配列に追加してしまうなどのバグが存在する時点でのコードをアップさせていただきます。
    ご回答よろしくお願いいたします。

    キャンセル

  • 2018/06/03 05:08

    hamejiさんに回答していただいたのを初心者の私が理解すると、Bool型の変数をクラスのプロパティとして宣言し、ボタンがタップされる度にその変数に値を保存するというようなことでしょうか?

    キャンセル

  • 2018/06/05 15:05

    アドバイスいただいたことがやっと理解でき、問題が解決できました。
    本当にお世話になりました。

    キャンセル

  • 2018/06/05 17:11

    いえいえ、もう一つの答えの方がちゃんとしてましたので、自分の出番では無いなと静観しておりました。

    コードも載せずに申し訳なかったです。

    キャンセル

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

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

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

  • Swift

    7653questions

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

  • Firebase

    670questions

    Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。