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

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

ただいまの
回答率

87.35%

関数内に設定したイベントリスナの処理について

解決済

回答 1

投稿 編集

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

score 111

イベントリスナについて、お聞きしたいことがあります。

import UIKit
import ChameleonFramework
import Firebase

class ChatViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UITextFieldDelegate {



  @IBOutlet weak var tableView: UITableView!
  @IBOutlet weak var messageTextField: UITextField!

  @IBOutlet weak var sendButton: UIButton!

  //スクリーンサイズ
  let screenSize = UIScreen.main.bounds.size

  var chatArray = [Message]()

  override func viewDidLoad() {
        super.viewDidLoad()

    tableView.delegate = self
    tableView.dataSource = self
    messageTextField.delegate = self

    //カスタムセルを作成する前に、テーブルビューに伝える必要がある
    tableView.register(UINib(nibName:"CustomCell",bundle:nil),forCellReuseIdentifier:"Cell")

    //可変
    tableView.rowHeight = UITableView.automaticDimension
    tableView.estimatedRowHeight = 75

    //キーボード
    NotificationCenter.default.addObserver(self, selector: #selector(ChatViewController.keyboardWillShow(_ :)), name: UIResponder.keyboardWillShowNotification, object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(ChatViewController.keyboardWillHide(_ :)), name: UIResponder.keyboardWillHideNotification, object: nil)

    //Firebaseからデータをfetch(取得)
    fetchChatData()
    tableView.separatorStyle = .none 

  }

  @objc func keyboardWillShow(_ notification:NSNotification){

    let keyboardHeight = ((notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as Any) as AnyObject).cgRectValue.height

    messageTextField.frame.origin.y = screenSize.height - keyboardHeight - messageTextField.frame.height


  }


  @objc func keyboardWillHide(_ notification:NSNotification){

    messageTextField.frame.origin.y = screenSize.height - messageTextField.frame.height

    guard let rect = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue,
      let duration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else{return}
    UIView.animate(withDuration: duration) {

      //移動アニメーション?                  ↓view自体が上がってしまっている為、元の位置に戻す為の指定
      let transform = CGAffineTransform(translationX: 0, y: 0)

      self.view.transform = transform

    }
  }

  //タッチされた場合
  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    messageTextField.resignFirstResponder()

  }

  //Returnキーが押された場合
  func textFieldShouldReturn(_ textField: UITextField) -> Bool {

    textField.resignFirstResponder()
    return true

  }


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

    //メッセージの数
    return chatArray.count

  }

  func numberOfSections(in tableView: UITableView) -> Int {
    return 1
  }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    //このメソッドを呼び出す前に、registerとして登録する必要がある
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! CustomCell

    cell.messageLabel.text = chatArray[indexPath.row].message

    cell.userNameLabel.text = chatArray[indexPath.row].sender
    cell.iconImageView.image = UIImage(named:"dogAvatarImage")


    if cell.userNameLabel.text == Auth.auth().currentUser?.email as! String{

      cell.messageLabel.backgroundColor = UIColor.flatGreen()
      cell.messageLabel.layer.cornerRadius = 20
      cell.messageLabel.layer.masksToBounds = true

    }else{

      cell.messageLabel.backgroundColor = UIColor.flatBlue()
      cell.messageLabel.layer.cornerRadius = 20
      cell.messageLabel.layer.masksToBounds = true
    }

    return cell

  }

  func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

    return 100
  }

  @IBAction func sendAction(_ sender: Any) {

    messageTextField.endEditing(true)
    messageTextField.isEnabled = false
    //ボタン無効
    sendButton.isEnabled = false

    if messageTextField.text!.count > 15{

      print("15文字以上です!!。")

      return

    }
    let chatDB = Database.database().reference().child("chats")

    //キーバリュー型で内容を送信(Dictionary型)

    let messageInfo = ["sender":Auth.auth().currentUser?.email,"message":messageTextField.text!]

    //chatDBに入れる
    chatDB.childByAutoId().setValue(messageInfo) { (error, result) in

      if error != nil{

        print(error as Any)

      }else{

        print("送信完了")
//        self.sendButton.isEnabled = true
//        self.messageTextField.isEnabled = true
//        self.messageTextField.text = ""

      }

    }

  }

  //データを引っ張ってくる
  func fetchChatData(){

    //どこからデータを引っ張ってくるのか
    let fetchDataRef = Database.database().reference().child("chats")

    //新しく更新があった時だけ取得
    fetchDataRef.observe(.childAdded) { (snapShot) in
      let snapShotData = snapShot.value as AnyObject
      let text = snapShotData.value(forKey: "message")
      let sender = snapShotData.value(forKey: "sender")

      let message = Message()
      message.message = text as! String
      message.sender = sender as! String
      self.chatArray.append(message)
      self.tableView.reloadData()

    }

   }



}

データの取得処理はfetchChatDataに記載していますが、イベントリスナというのはデータの更新があった場合は、どこかの関数内であっても(どこに記載されていようとも)そこの処理部分だけ(今回はfetchDataRef.observe)呼び出されそこの処理を行う、のでしょうか?
Firebase公式ドキュメントにはデータが変更されると、そのたびに再トリガーされます。とあったのですが、関数の中にある関数(fetchDataRef.observe)だけ呼び出された場合、呼び出された後の処理はどうなるのかなど、少々疑問でした為、質問させて貰いました。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

0

データの取得処理はfunc fetchdata()に記載しています

func fetchChatData() のtypoですかね?

イベントリスナというのはデータの更新があった場合は、どこかの関数内であっても(どこに記載されていようとも)そこの処理部分だけ(今回はfetchDataRef.observe)呼び出されそこの処理を行う

結論を言うと Yes ですが、日本語が正確ではない気がしますね。

fetchDataRef.observe(.childAdded) { (snapShot) in { ... } } は、イベントリスナを登録する処理です。イベントをリスンさせる処理と言ってもいいです。この場合、 .childAdded イベントをリスンして、イベント時に取得したデータを snapShot という引数にしてBlock (Closure) に渡しています。この登録処理はviewWillAppearに直接書いてもいいし、AppDelegateに書いておいて、ViewController側ではNotificationCenterとかで受けても大丈夫です。ただし、無秩序にどこでも宣言できるわけではなく、FIRAppの初期設定が終わっていないといけない、などの制約はあります。
余談ですが snapshot という一つの単語 (snap shotではない) なのにSが大文字なのが気になりますね……

イベントをリスンさせるというのは、「おい田中、昼休みに焼きそばパン買ってこいよ」という指示を出すようなもので、田中に声をかけたのが通学路上であっても下駄箱であっても教室の中であっても関係なく、昼休みのチャイムがなるイベントが発火すると田中は焼きそばパンを買いに行きます。ただし、田中が生まれる前に指示をしても期待通りには動かないなど、無秩序にどこでも宣言できるというわけではありません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/12/20 18:32 編集

    ご回答ありがとうございます。
    たまに、「イベントリスナ」や「リッスン」と出てきて混乱してしまう部分があったのですが、「リッスン=登録」と読み替えて差し支えなかったですね。
    イベント自体は「.childAdded」がイベントの種類となり、「.observe」はイベントを登録する処理であった訳ですね。
    自由に記載しても良いといっても初期設定などの制約が存在することを、念頭に入れておきたいと思います。

    キャンセル

  • 2019/12/20 18:36

    > 「リッスン=登録」
    いいえ、違います。「Eventが起こったことを検出すること」がListenであり、「Listenしてくれるやつ」がListenerです。「Listenerを登録する」処理がFirebaseではobserveというメソッドになっています。

    キャンセル

  • 2019/12/20 18:45 編集

    詳細なご説明助かります。
    また、分からなくなったら頂いたコメントを見返したいと思います。

    キャンセル

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

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

関連した質問

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