Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/create chat room vc #29

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d200ce7
CreateChatRoomの各種ファイルを追加
ojun9 Jul 18, 2020
f151900
Storyboardに部品の配置をした
ojun9 Jul 18, 2020
f17d77a
カスタムセルを作って部品を接続した
ojun9 Jul 18, 2020
eaca76e
各cellのsetupを書いた
ojun9 Jul 18, 2020
ae1492c
CreateChatRoomViewControllerのセットアップを行った
ojun9 Jul 18, 2020
fc1e953
Searchのスペルが間違ってたのを修正
ojun9 Jul 18, 2020
9cdb665
ファイル名の最初が大文字になってたかったのを修正
ojun9 Jul 18, 2020
30e3bbf
デバックようにcellを表示した
ojun9 Jul 18, 2020
a9015c4
デバック用にimageを表示した
ojun9 Jul 18, 2020
6cd9b4a
serchUserTableviewを選択したときにデータをtableviewとcollectionviewに反映させる処理を書いた
ojun9 Jul 18, 2020
53e9783
radioImageViewに結果を反映させるようにした
ojun9 Jul 18, 2020
64d01bd
selectedUserCollectionViewのcellの表示を調整
ojun9 Jul 18, 2020
8cf5f94
selectedUserCollectionViewでバツボタン押せれた時に削除できるようにした
ojun9 Jul 18, 2020
74c38ab
キーボードが登場したときにcollectionviewを上に上げる処理を書いた
ojun9 Jul 18, 2020
7183396
キーボードを閉じる処理をcollectionviewの時はしないようにした
ojun9 Jul 18, 2020
68eec80
NavigationItemと各アイテムがタップされたときの処理を書いた
ojun9 Jul 18, 2020
8fc7cf1
searchBarSearchButtonClickedのときの処理を書いた
ojun9 Jul 18, 2020
1a5c774
バツボタン押してユーザ削除したときにtableviewに反映されない問題を修正
ojun9 Jul 18, 2020
97e2440
firestoreからwhereFieldでユーザを検索する機能を追加した
ojun9 Jul 18, 2020
b3b7879
activityIndicatorを追加して検索中にくるくるする処理を書いた
ojun9 Jul 18, 2020
a6f3baa
CollectionViewを表示/非表示するときにアニメーションを追加
ojun9 Jul 19, 2020
433dc02
presenterとviewにweakをつけた
ojun9 Jul 19, 2020
daf5d66
コメントの追加と,Rootを開発用に変更してたのを元に戻した
ojun9 Jul 19, 2020
9113277
TODOのコメントを追加
ojun9 Jul 19, 2020
64f6246
didTapCreateRoomButtonのタイポを修正
ojun9 Jul 22, 2020
b356c21
cellのviewの更新をcell内のconfigureでした
ojun9 Jul 22, 2020
6a23ac3
変数の1文字目が大文字になってるのを修正
ojun9 Jul 22, 2020
ed5a521
viewControllerから`searchedUsersArray `と`selectedUsersArray `を取り除いた
ojun9 Jul 22, 2020
a52e499
`DispatchQueue.main.async`が必要ないところを削除した
ojun9 Jul 22, 2020
d9a5050
Roomを保存する処理を書いた
ojun9 Jul 22, 2020
8f53c57
クロージャでcellを削除する処理を書いた
ojun9 Jul 23, 2020
8972caa
コードを一部整形
ojun9 Jul 23, 2020
6023a3d
cellの処理と配列の取得を変更
ojun9 Jul 24, 2020
9c71c2b
radioImageViewが見にくかったのを修正
ojun9 Jul 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions chat-iOS/Views/CreateChatRoom/CreateChatRoomViewBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// CreateChatRoomViewBuilder.swift
// chat-iOS
//
// Created by jun on 2020/07/18.
//

import UIKit

struct CreateChatRoomViewBuilder {
static func create() -> UIViewController {
guard let createChatRoomViewController = CreateChatRoomViewController.loadFromStoryboard() as? CreateChatRoomViewController else {
fatalError("fatal: Failed to initialize the CreateChatRoomViewController")
}
let model = CreateChatRoomModel()
let presenter = CreateChatRoomViewPresenter(model: model)
createChatRoomViewController.inject(with: presenter)
return createChatRoomViewController
}
}
239 changes: 239 additions & 0 deletions chat-iOS/Views/CreateChatRoom/CreateChatRoomViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
//
// File.swift
// chat-iOS
//
// Created by jun on 2020/07/18.
//

import UIKit

final class CreateChatRoomViewController: UIViewController {
private var presenter: CreateChatRoomViewPresenterProtocol!

@IBOutlet weak var userNameSearchBar: UISearchBar!
@IBOutlet weak var serchUserTableview: UITableView!
@IBOutlet weak var selectedUserCollectionView: UICollectionView!

@IBOutlet weak var selectedUserCollectionViewBottomsConstraints: NSLayoutConstraint!

var activityIndicator = UIActivityIndicatorView()

private let searchedUsersCellID = "SearchUserTableviewCell"
private let selectedUsersCellID = "SelectedUserCollectionViewCell"

override func viewDidLoad() {
super.viewDidLoad()

self.setupNavigationItem()
self.setupUserSearchBar()
self.setupSerchUserTableview()
self.setupSelectedUserCollectionView()
self.setupActivityIndicator()
self.setupNotificationCenter()
}

private func setupNavigationItem() {
self.navigationItem.title = "Choose friends"
let stopItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(tapStopCreateRoomButton))
self.navigationItem.leftBarButtonItem = stopItem
let saveItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(tapCreateRoomButton))
self.navigationItem.rightBarButtonItem = saveItem
}

private func setupUserSearchBar() {
self.userNameSearchBar.placeholder = "Search by name"
self.userNameSearchBar.delegate = self
}

private func setupSerchUserTableview() {
self.serchUserTableview.rowHeight = 75
self.serchUserTableview.delegate = self
self.serchUserTableview.dataSource = self
self.serchUserTableview.tableFooterView = UIView()
}

private func setupSelectedUserCollectionView() {
self.selectedUserCollectionView.isHidden = true
self.selectedUserCollectionView.collectionViewLayout.invalidateLayout()
self.selectedUserCollectionView.delegate = self
self.selectedUserCollectionView.dataSource = self
}

private func setupActivityIndicator() {
self.activityIndicator.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
self.activityIndicator.center = self.view.center
self.activityIndicator.hidesWhenStopped = true
self.view.addSubview(self.activityIndicator)
}

func setupNotificationCenter() {
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShowNotification(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHideNotification(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}

/// キーボードが登場した時の処理
@objc func keyboardWillShowNotification(notification: NSNotification) {
guard let userInfo = notification.userInfo else { return }
guard let keyboard = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }

self.selectedUserCollectionViewBottomsConstraints.constant = -keyboard.cgRectValue.height + self.view.safeAreaInsets.bottom
UIView.animate(withDuration: 1.0, animations: { self.view.layoutIfNeeded() })
}

/// キーボードが隠れた時の処理
@objc func keyboardWillHideNotification(notification: NSNotification) {
guard notification.userInfo != nil else { return }

self.selectedUserCollectionViewBottomsConstraints.constant = 0
UIView.animate(withDuration: 1.0, animations: { self.view.layoutIfNeeded() })
}

@objc func tapStopCreateRoomButton() {
self.presenter.didTapStopCreateRoomButton()
}

@objc func tapCreateRoomButton() {
self.presenter.didTapCreateRoomutton()
}

/// `selectedUserCollectionView`にあるバツボタンタップされたときに呼ばれる関数。
/// - Parameter button: ボタンの`tag`でどのindexを消すかがわかる
@objc func tapSelectedUserCollectionViewCellDeleteUserButton(_ button: UIButton) {
self.presenter.didTapSelectedUserCollectionViewCellDeleteUserButton(index: button.tag)
}

func inject(with presenter: CreateChatRoomViewPresenterProtocol) {
self.presenter = presenter
self.presenter.view = self
}
}

extension CreateChatRoomViewController: CreateChatRoomViewPresenterOutput {
func reloadSerchUserTableview() {
DispatchQueue.main.async { self.serchUserTableview.reloadData() }
}

func reloadSelectedUserCollectionView() {
DispatchQueue.main.async { self.selectedUserCollectionView.reloadData() }
guard self.selectedUserCollectionView.isHidden else { return }

self.selectedUserCollectionView.alpha = 0
self.selectedUserCollectionView.isHidden = false
UIView.animate( withDuration: 0.15, animations: { self.selectedUserCollectionView.alpha = 1 }, completion: nil)
}

func hiddenSelectedUsersCollectionView() {
UIView.animate(
withDuration: 0.25,
animations: { self.selectedUserCollectionView.alpha = 0 },
completion: { _ in
self.selectedUserCollectionView.isHidden = true
self.selectedUserCollectionView.alpha = 1
}
)
}

func dismissCreateChatRoomVC() {
DispatchQueue.main.async { self.dismiss(animated: true, completion: nil) }
}

func clearSearchUserTableView() {
DispatchQueue.main.async { self.serchUserTableview.reloadData() }
}

func startActivityIndicator() {
DispatchQueue.main.async { self.activityIndicator.startAnimating() }
}

func stopActivityIndicator() {
DispatchQueue.main.async { self.activityIndicator.stopAnimating() }
}
}

extension CreateChatRoomViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.presenter.numberOfSearchedUsers
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = self.serchUserTableview.dequeueReusableCell(withIdentifier: self.searchedUsersCellID, for: indexPath)
as? SearchUserTableviewCell else { return UITableViewCell() }

let selectedUsersArray: [User] = self.presenter.getSelectedUsersArray()
let searchedUsersArray: [User] = self.presenter.getSearchedUsersArray()
let isSelected = !selectedUsersArray.filter({ $0.id == searchedUsersArray[indexPath.item].id ?? ""}).isEmpty
cell.configure(with: searchedUsersArray[indexPath.item], isSelected: isSelected)

//TODO:Firestoreから取得した後で表示し直すこと
if #available(iOS 13.0, *) {
cell.profileImageView.image = UIImage(systemName: "bolt.circle.fill")
} else {
// Fallback on earlier versions
}

return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let searchedUsersArray: [User] = self.presenter.getSearchedUsersArray()
self.serchUserTableview.deselectRow(at: indexPath, animated: true)
self.presenter.didSelectedSerchUserTableview(selectedUser: searchedUsersArray[indexPath.item])
}

}

extension CreateChatRoomViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.presenter.numberOfSelectedUsers
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: selectedUsersCellID, for: indexPath) as! SelectedUserCollectionViewCell
let selectedUsersArray: [User] = self.presenter.getSelectedUsersArray()

cell.userNameLabel.text = selectedUsersArray[indexPath.item].displayName

cell.deleteUserButton.tag = indexPath.item
cell.deleteUserButton.addTarget(self, action: #selector(tapSelectedUserCollectionViewCellDeleteUserButton(_:)), for: .touchUpInside)

//TODO:Firestoreから取得した後で表示し直すこと
if #available(iOS 13.0, *) {
cell.profileImageView.image = UIImage(systemName: "bolt.circle.fill")
} else {
// Fallback on earlier versions
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tagを使わずにdelegateかクロージャの方が良いかなと思うのですがどうでしょうか?
参考 https://fluffy.es/handling-button-tap-inside-uitableviewcell-without-using-tag/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これは結局SearchUserTableviewCell内に
cell番号自体を保存する必要がある(サイト内ではユーチューバの名前がクロージャーで送られてる)かつ,ボタンで使うのはcellで固有のString等の情報でなく単にcellの番号のみの取得なので,今回の処理ではtagを使った実装にしたのですがどうでしょうか?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

タグを使わない方が良い(例えばsectionがいっこだけじゃない時にややこしくなる)と思っていて
その上で今回はtagを使ってindexPath.itemを渡しているので
それならクロージャを使って
cellのクラス内でbuttonがtapされた時に
deleteUserActionが実行されるようにして

cell.deleteUserAction = { [weak self] in 
   self?.presenter.didTapSelectedUserCollectionViewCellDeleteUserButton(index: indexPath.item)
}

こんな感じでindexPath.itemを渡した方が良いかなと思いました

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど。理解しました!了解です
クロージャで実装します!


return cell
}

func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 85, height: 90)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 10)
}
}

extension CreateChatRoomViewController: UISearchBarDelegate {
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
guard scrollView == self.serchUserTableview else { return }
guard self.userNameSearchBar.isFirstResponder else { return }
self.userNameSearchBar.resignFirstResponder()
}

/// 検索ボタンがタップされたときに呼ばれる関数
/// - Parameter searchBar: サーチバー
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
guard let searchBarText = searchBar.text else { return }
guard !searchBarText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
guard self.userNameSearchBar.isFirstResponder else { return }
self.userNameSearchBar.resignFirstResponder()

self.presenter.didSearchBarSearchButtonClicked(searchText: searchBarText)
}
}
98 changes: 98 additions & 0 deletions chat-iOS/Views/CreateChatRoom/CreateChatRoomViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//
// CreateChatRoomViewModel.swift
// chat-iOS
//
// Created by jun on 2020/07/18.
//

import Firebase

protocol CreateChatRoomModelProtocol {
var presenter: CreateChatRoomModelOutput! { get set }
var selectedUsersArray: [User] { get set }
var searchedUsersArray: [User] { get set }

func isContaintsUser(user: User) -> Bool
func searchUser(searchText: String)
func removeSelectedUserFromSelectedUserArray(user: User)
func appendUserToSelectedUserArray(user: User)
func removeSelectedUsersArray(index: Int) -> [User]
func createChatRoom()
}

protocol CreateChatRoomModelOutput: class {
func successRemoveSelectedUser()
func successAppendUser()

func successCreateChatRoom()

func successSearchUser()
}

final class CreateChatRoomModel: CreateChatRoomModelProtocol {
weak var presenter: CreateChatRoomModelOutput!
private var firestore: Firestore!
var selectedUsersArray: [User] = Array()
var searchedUsersArray: [User] = Array()

init() {
self.firestore = Firestore.firestore()
let settings = FirestoreSettings()
self.firestore.settings = settings
}

/// ユーザをFirestoreから検索する関数
/// - Parameter searchText: 検索するユーザ名
func searchUser(searchText: String) {
self.firestore.collection("message/v1/users").whereField("displayName", isEqualTo: searchText).getDocuments { [weak self] (documentSnapshot, error) in
if let error = error {
print("Error: \(error.localizedDescription)")
return
}

guard let documents = documentSnapshot?.documents else {
print("The document doesn't exist.")
return
}

let searchedUsers = documents.compactMap { queryDocumentSnapshot -> User? in
return try? queryDocumentSnapshot.data(as: User.self)
}

self?.searchedUsersArray = searchedUsers
self?.presenter.successSearchUser()
}
}

/// ユーザが既に選択されているかを返す
/// - Parameter user: 検索するユーザ
/// - Returns: 検索結果
func isContaintsUser(user: User) -> Bool {
if self.selectedUsersArray.filter({ $0.id == user.id ?? "" }).isEmpty { return false }
return true
}

func removeSelectedUserFromSelectedUserArray(user: User) {
self.selectedUsersArray = self.selectedUsersArray.filter({ $0.id != user.id })

self.presenter.successRemoveSelectedUser()
}

func appendUserToSelectedUserArray(user: User) {
self.selectedUsersArray.append(user)
self.presenter.successAppendUser()
}

/// `selectedUsersArray`からあるインデックスを削除する
/// - Parameter index: 削除する配列番号
/// - Returns: 削除した後の`selectedUsersArray`
func removeSelectedUsersArray(index: Int) -> [User] {
self.selectedUsersArray.remove(at: index)
return self.selectedUsersArray
}

//TODO:- Firesotreに保存する
func createChatRoom() {
self.presenter.successCreateChatRoom()
}
}
Loading