Image not behaving accurately in data search results on TableView - ios

I have data coming from Firebase and when the data is loaded I either want the an image to be hidden or shown based on some logic in my custom cell. It works perfectly fine when the data isn't being filtered but the second I type in the search bar or change the scope bar to a different index the image doesn't behave right.
For example: Index 0 should not have the image and index 1 should. Which is how it displays when it first loads. However, when I search I know the previous index 1 (now index 0) should still have it's image but it doesn't. BUT if I click to go to the detail controller it brings me to the right page. It's like it loads all the accurate info but does the logic on the original index 0. I would love some help as I have been searching for an answer FOREVER. Thank you in advance!
tableViewCell:
class SearchTalentCell: UITableViewCell {
#IBOutlet weak var userProfileImage: UIImageView!
#IBOutlet weak var talentUserName: UILabel!
#IBOutlet weak var selectedImg: UIImageView!
#IBOutlet weak var inviteSentImg: UIImageView!
var prospectRef: FIRDatabaseReference!
//#IBOutlet weak var radioButton: UIButton!
var currentTalent: UserType!
//var delegate: SearchCellDelegate?
func setTalent(talent: UserType) {
currentTalent = talent
currentTalent.userKey = talent.userKey
}
override func awakeFromNib() {
super.awakeFromNib()
let tap = UITapGestureRecognizer(target: self, action: #selector(selectTapped))
tap.numberOfTapsRequired = 1
selectedImg.addGestureRecognizer(tap)
selectedImg.isUserInteractionEnabled = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
/*#IBAction func radioButtonTapped(_ sender: Any) {
delegate?.didTapRadioButton(userKey: currntTalent.userKey, searchSelected: currntTalent.searchSelected!.rawValue, radioButton: radioButton)
}*/
func configureCell(user: UserType, img: UIImage? = nil) {
prospectRef = Cast.REF_PRE_PRODUCTION_CASTING_POSITION.child(ProjectDetailVC.currentProject).child(FIRDataCast.prospect.rawValue).child(CastingDetailVC.positionName).child(user.userKey)
//setTalent(talent: user)
self.talentUserName.text = "\(user.firstName) \(user.lastName)"
//self.inviteSentImg.image = UIImage(named: "inviteSent")
//user.adjustSearchSelected(talent: user, radioButton: radioButton)
prospectRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let _ = snapshot.value as? NSNull {
self.inviteSentImg.isHidden = true
print("**Image hidden")
} else {
self.inviteSentImg.image = UIImage(named: "inviteSent")
print("**Image shown")
}
})
//Image Caching
if img != nil {
self.userProfileImage.image = img
} else {
if let imageURL = user.profileImage {
let ref = FIRStorage.storage().reference(forURL: imageURL)
ref.data(withMaxSize: 2 * 1024 * 1024, completion: { (data, error) in
if error != nil {
print("ZACK: Unable to download image from Firebase Storage")
} else {
print("ZACK: Image downloaded from Firebase Storage")
if let imgData = data {
if let img = UIImage(data: imgData) {
self.userProfileImage.image = img
SearchTalentVC.userProfileImageCache.setObject(img, forKey: imageURL as NSString)
}
}
}
})
}
}
}
Viewcontroller:
class SearchTalentVC: UITableViewController/*, SearchCellDelegate*/ {
var searchingRole = [Cast]()
var unfilteredTalent = [UserType]()
var filteredTalent = [UserType]()
var selectedTalent = [UserType]()
var matchingTalentUserKeys = [String]()
var isFiltered = false
var prospectRef: FIRDatabaseReference!
static var userProfileImageCache: NSCache<NSString, UIImage> = NSCache()
let searchController = UISearchController(searchResultsController: nil)
//#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.placeholder = "Search Talent"
searchController.searchBar.barStyle = .black
navigationItem.searchController = searchController
definesPresentationContext = true
searchController.searchBar.scopeButtonTitles = ["All", "Role Specific"]
searchController.searchBar.tintColor = UIColor.white
searchController.searchBar.delegate = self
searchController.searchResultsUpdater = self
getTalentProfiles()
}
func searchBarIsEmpty() -> Bool {
return searchController.searchBar.text?.isEmpty ?? true
}
func filterContentForSearchText(_ searchText: String, scope: String = "All") {
filteredTalent = unfilteredTalent.filter({ (talent : UserType) -> Bool in
let doesTalentMatch = (scope == "All") || doesUserKeyMatch(talent: talent.userKey)
if searchBarIsEmpty() {
return doesTalentMatch
} else {
let fullName = "\(talent.firstName) \(talent.lastName)"
return doesTalentMatch && fullName.lowercased().contains(searchText.lowercased())
}
})
tableView.reloadData()
}
func doesUserKeyMatch(talent: String) -> Bool {
self.filterRoleFeature()
return matchingTalentUserKeys.contains(talent)
}
func isSearching() -> Bool {
let searchBarScopeIsFiltering = searchController.searchBar.selectedScopeButtonIndex != 0
return searchController.isActive && (!searchBarIsEmpty() || searchBarScopeIsFiltering)
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if isSearching() {
return filteredTalent.count
} else {
return unfilteredTalent.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "userSearchCell"
if let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? SearchTalentCell {
var talent: UserType
if isSearching() {
print("we are searching")
talent = self.filteredTalent[indexPath.row]
print("indexPath: \(indexPath.row)")
} else {
print("we are not searching")
talent = self.unfilteredTalent[indexPath.row]
}
if let imageURL = talent.profileImage {
if let img = SearchTalentVC.userProfileImageCache.object(forKey: imageURL as NSString) {
cell.configureCell(user: talent, img: img)
} else {
cell.configureCell(user: talent)
//cell.delegate = self
}
return cell
} else {
cell.configureCell(user: talent)
//cell.delegate = self
return SearchTalentCell()
}
} else {
return SearchTalentCell()
}
}
extension SearchTalentVC: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
let searchBar = searchController.searchBar
let scope = searchBar.scopeButtonTitles![searchBar.selectedScopeButtonIndex]
filterContentForSearchText(searchController.searchBar.text!, scope: scope)
self.tableView.reloadData()
}
}
extension SearchTalentVC: UISearchBarDelegate {
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
filterContentForSearchText(searchBar.text!, scope: searchBar.scopeButtonTitles![selectedScope])
}
}

If you are hidding image in If part then you should put logic of showing image also in else block. See if this solve your problem.
if let _ = snapshot.value as? NSNull {
self.inviteSentImg.isHidden = true
print("**Image hidden")
} else {
self.inviteSentImg.isHidden = false
self.inviteSentImg.image = UIImage(named: "inviteSent")
print("**Image shown")
}

Related

Delete tableView cell and remove data from firebase

I'm working on study project of social network. Stuck on the stage of deleting user comments from Firebase database. To delete a specific comment I need to know the comment Id, but I do not understand how to access it. I'm really appreciate any help on this!
Example of Firebase database:
CommentViewController:
class CommentViewController: UIViewController {
#IBOutlet weak var sendButton: UIButton!
#IBOutlet weak var commentTextField: UITextField!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var constraintToBottom: NSLayoutConstraint!
var postId: String!
var comments = [Comment]()
var users = [User]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
title = "Comment"
tableView.estimatedRowHeight = 77
tableView.rowHeight = UITableView.automaticDimension
empty()
handleTextField()
loadComments()
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
#objc func keyboardWillShow(_ notification: NSNotification) {
let keyboardFrame = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as AnyObject).cgRectValue
UIView.animate(withDuration: 0.3) {
self.constraintToBottom.constant = keyboardFrame!.height
self.view.layoutIfNeeded()
}
}
#objc func keyboardWillHide(_ notification: NSNotification) {
UIView.animate(withDuration: 0.3) {
self.constraintToBottom.constant = 0
self.view.layoutIfNeeded()
}
}
var comment: Comment?
func loadComments() {
Api.Post_Comment.REF_POST_COMMENTS.child(self.postId).observe(.childAdded, with: {
snapshot in
Api.Comment.observeComments(withPostId: snapshot.key, completion: {
comment in
self.fetchUser(uid: comment.uid!, completed: {
self.comments.append(comment)
self.tableView.reloadData()
})
})
})
}
func fetchUser(uid: String, completed: #escaping() -> Void ) {
Api.User.observeUser(withId: uid, completion: {
user in
self.users.append(user)
completed()
})
}
func handleTextField() {
commentTextField.addTarget(self, action: #selector(self.textFieldDidChange), for: UIControl.Event.editingChanged)
}
#objc func textFieldDidChange() {
if let commentText = commentTextField.text, !commentText.isEmpty {
sendButton.setTitleColor(UIColor.black, for: UIControl.State.normal)
sendButton.isEnabled = true
return
}
sendButton.setTitleColor(UIColor.lightGray, for: UIControl.State.normal)
sendButton.isEnabled = false
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.isHidden = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.tabBarController?.tabBar.isHidden = false
}
#IBAction func sendButton_TouchUpInside(_ sender: Any) {
let commentsReference = Api.Comment.REF_COMMENTS
let newCommentId = commentsReference.childByAutoId().key!
let newCommentReference = commentsReference.child(newCommentId)
guard let currentUser = Api.User.CURRENT_USER else {
return
}
let currentUserId = currentUser.uid
newCommentReference.setValue(["uid": currentUserId, "commentText": commentTextField.text!], withCompletionBlock: {
(error, ref) in
if error != nil {
ProgressHUD.showError(error!.localizedDescription)
return
}
let postCommentRef = Api.Post_Comment.REF_POST_COMMENTS.child(self.postId).child(newCommentId)
postCommentRef.setValue(true, withCompletionBlock: { (error, ref) in
if error != nil {
ProgressHUD.showError(error!.localizedDescription)
return
}
})
self.empty()
self.view.endEditing(true)
})
}
func empty() {
self.commentTextField.text = ""
sendButton.setTitleColor(UIColor.lightGray, for: UIControl.State.normal)
sendButton.isEnabled = false
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Comment_ProfileSegue" {
let profileVC = segue.destination as! ProfileUserViewController
let userId = sender as! String
profileVC.userId = userId
}
}
extension CommentViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return comments.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CommentCell", for: indexPath) as! CommentTableViewCell
let comment = comments[indexPath.row]
let user = users[indexPath.row]
cell.comment = comment
cell.user = user
cell.delegate = self
return cell
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
}
}
CommentTableViewCell:
class CommentTableViewCell: UITableViewCell {
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var commentLabel: UILabel!
var delegate: CommentTableViewCellDelegate?
var comment: Comment? {
didSet {
updateView()
}
}
var user: User? {
didSet {
setupUserInfo()
}
}
func updateView() {
commentLabel.text = comment?.commentText
}
func setupUserInfo() {
nameLabel.text = user?.username
if let photoUrlString = user?.profileImageUrl {
let photoUrl = URL(string: photoUrlString)
profileImageView.sd_setImage(with: photoUrl, placeholderImage: UIImage(named: "photo_placeholder"))
}
}
override func awakeFromNib() {
super.awakeFromNib()
nameLabel.text = ""
commentLabel.text = ""
let tapGestureForNameLabel = UITapGestureRecognizer(target: self, action: #selector(self.nameLabel_TouchUpInside))
nameLabel.addGestureRecognizer(tapGestureForNameLabel)
nameLabel.isUserInteractionEnabled = true
}
#objc func nameLabel_TouchUpInside() {
if let id = user?.id {
delegate?.goToProfileUserVC(userId: id)
}
}
override func prepareForReuse() {
super.prepareForReuse()
profileImageView.image = UIImage(named: "placeholderImg")
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
Comment Api
class CommentApi {
var REF_COMMENTS = Database.database().reference().child("comments")
func observeComments(withPostId id: String, completion: #escaping (Comment) -> Void) {
REF_COMMENTS.child(id).observeSingleEvent(of: .value, with: {
snapshot in
if let dict = snapshot.value as? [String: Any] {
let newComment = Comment.transformComment(dict: dict, key: snapshot.key)
completion(newComment)
}
})
}
func observeComment(withId id: String, completion: #escaping (Comment) -> Void) {
REF_COMMENTS.child(id).observeSingleEvent(of: DataEventType.value, with: {
snapshot in
if let dict = snapshot.value as? [String: Any] {
let comment = Comment.transformComment(dict: dict, key: snapshot.key)
completion(comment)
}
})
}
Comment Model:
class Comment {
var commentText: String?
var uid: String?
var id: String?}
extension Comment {
static func transformComment(dict: [String: Any], key: String) -> Comment {
let comment = Comment()
comment.id = key
comment.commentText = dict["commentText"] as? String
comment.uid = dict["uid"] as? String
return comment
}
Speaking at a high level, your tableView is backed by a dataSource, typically an array, which is the source for the content displayed in the tableView.
var userCommentArray = [UserComment]()
you should be loading data from Firebase and storing that data in the array as UserComment objects
class UserComment {
var firebaseKey = ""
var commentText = ""
var uid = ""
}
the firebase_key property is the key to the node in Firebase, shown as -MH_xxxx in the screenshot and then the commentText and uid are is the child data of that node.
The indexes of the elements in the array match what's being shown in the tableView, so row0 matches the array index 0, row 1 matches the array index 1 etc.
When the user deletes row 1, you know that's index 1 in the array. read the object, get it's firebaseKey and then delete it from firebase, updating the array accordingly and then reloading your UI.
See my answer to your Other Question for details on that process.

Accessing struct object array in no of rows in section returning null

I am trying to download data and put it in struct objects and trying to load data in table view .I am downloading it in to array and append it to struct object.when I am taking return array.count in no of rows in section its working when I use return objectArray[section].funcName.count its not working values are getting late to download also
import UIKit
import Alamofire
class GalleryVC: UIViewController,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var galleryTable: UITableView!
var imageUrlArray:[String] = [String]()
var imageCount:[String] = [String]()
var funName1:[String] = [String]()
var gaimage1:String = ""
var gacount1:String = ""
var funname1:String = ""
struct Objects {
var imageName : [String]!
var imageCount : [String]!
var funcName:[String]!
}
var objectArray = [Objects]()
var objectArrayFilter = [Objects]()
var inSearchMode = false
override func viewDidLoad() {
super.viewDidLoad()
downloadGalleryList()
galleryTable.delegate = self
galleryTable.dataSource = self
searchBar.delegate = self
self.hideKeyboardWhenTappedAround()
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
print(objectArray[section].funcName.count)
return objectArray[section].funcName.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier:"gallerycell", for: indexPath) as? GalleryListCell{
if inSearchMode{
cell.galleryImage.sd_setImage(with: URL(string: objectArrayFilter[indexPath.section].imageName[indexPath.row]), placeholderImage: UIImage(named: "1862205"))
cell.galleryphotono.text = objectArrayFilter[indexPath.section].imageCount[indexPath.row]+" photos"
cell.galleryFunction.text = objectArrayFilter[indexPath.section].funcName[indexPath.row]
return cell
}
cell.galleryImage.sd_setImage(with: URL(string: objectArray[indexPath.section].imageName[indexPath.row]), placeholderImage: UIImage(named: "1862205"))
cell.galleryphotono.text = objectArray[indexPath.section].imageCount[indexPath.row]+" photos"
cell.galleryFunction.text = objectArray[indexPath.section].funcName[indexPath.row]
return cell
}
else{
return UITableViewCell()
}
}
func downloadGalleryList(){
let bmiChapterUrl = URL(string:Gallery_List)!
Alamofire.request(bmiChapterUrl).responseJSON{ response in
let result = response.result
print(response)
print(result)
if let dict = result.value as? Dictionary<String,AnyObject>{
if let bmi = dict["result"] as? [Dictionary<String,AnyObject>]
{
for obj in bmi {
if let gaimage = obj["image"] as? String
{
print(gaimage)
self.gaimage1 = gaimage
self.imageUrlArray.append(gaimage)
}
if let gacount = obj["count"] as? String
{
self.gacount1 = gacount
print(gacount)
self.imageCount.append(gacount)
}
if let funname = obj["event"] as? String
{
print(funname)
self.funname1 = funname
self.funName1.append(funname)
}
}
}
}
print(self.imageUrlArray,self.imageCount,self.funName1
)
self.objectArray.append(Objects(imageName: self.imageUrlArray, imageCount:self.imageCount,funcName: self.funName1))
print(self.objectArray)
self.galleryTable.reloadData()
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
inSearchMode = false
view.endEditing(true)
galleryTable.reloadData()
} else {
inSearchMode = true
objectArrayFilter = objectArray.filter { $0.imageName.contains(where: { $0.contains(searchBar.text!) }) }
print(objectArrayFilter)
galleryTable.reloadData()
}
}
}

How to pull users from database and list them in a table view using firebase?

I'm using firebase to make an iOS app. I want to retrieve all the users on my database and display their name and profile picture in a table view. Here is my code for my TableViewCell:
import UIKit
import FirebaseDatabase
import FirebaseAuth
import SDWebImage
class HomeTableViewCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var profileImageView: UIImageView!
#IBOutlet weak var likeImageView: UIImageView!
#IBOutlet weak var messageImageView: UIImageView!
#IBOutlet weak var likeCountButton: UIButton!
var homeVC: HomeViewController?
var postReference: DatabaseReference!
var post: UserFile?{
didSet {
updateView()
}
}
var user: UserFile? {
didSet {
updateUserInfo()
}
}
override func awakeFromNib() {
super.awakeFromNib()
nameLabel.text = ""
let berryTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleLikeTap))
likeImageView.addGestureRecognizer(berryTapGesture)
likeImageView.isUserInteractionEnabled = true
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func updateView() {
if let photoURL = post?.picURL {
profileImageView.sd_setImage(with: URL(string: photoURL))
}
API.Post.REF_POSTS.child(post!.id!).observeSingleEvent(of: .value, with: { postSnapshot in
if let postDictionary = postSnapshot.value as? [String:Any] {
let post = UserFile.transformPost(postDictionary: postDictionary, key: postSnapshot.key)
self.updateLike(post: post)
}
})
API.Post.REF_POSTS.child(post!.id!).observe(.childChanged, with: { snapshot in
if let value = snapshot.value as? Int {
self.likeCountButton.setTitle("\(value) berries", for: .normal)
}
})
}
func updateLike(post: UserFile) {
let imageName = post.berries == nil || !post.isBerried! ? "berry" : "berrySelected"
likeImageView.image = UIImage(named: imageName)
// display a message for berries
guard let count = post.berryCount else {
return
}
if count != 0 {
likeCountButton.setTitle("\(count) berries", for: .normal)
} else if post.berryCount == 0 {
likeCountButton.setTitle("Be the first to Like this", for: .normal)
}
}
func incrementberries(forReference ref: DatabaseReference) {
ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in
if var post = currentData.value as? [String : AnyObject], let uid = Auth.auth().currentUser?.uid {
var berries: Dictionary<String, Bool>
berries = post["berries"] as? [String : Bool] ?? [:]
var likeCount = post["berryCount"] as? Int ?? 0
if let _ = berries[uid] {
// Unlike the post and remove self from stars
likeCount -= 1
berries.removeValue(forKey: uid)
} else {
// Like the post and add self to stars
likeCount += 1
berries[uid] = true
}
post["berryCount"] = likeCount as AnyObject?
post["berries"] = berries as AnyObject?
currentData.value = post
return TransactionResult.success(withValue: currentData)
}
return TransactionResult.success(withValue: currentData)
}) { (error, committed, snapshot) in
if let error = error {
print(error.localizedDescription)
}
if let postDictionary = snapshot?.value as? [String:Any] {
let post = UserFile.transformPost(postDictionary: postDictionary, key: snapshot!.key)
self.updateLike(post: post)
}
}
}
func handleLikeTap() {
postReference = API.Post.REF_POSTS.child(post!.id!)
incrementberries(forReference: postReference)
}
override func prepareForReuse() {
super.prepareForReuse()
profileImageView.image = UIImage(named: "industribune-default-no-profile-pic")
}
func updateUserInfo() {
nameLabel.text = user?.username
if let photoURL = user?.profileImageURL {
profileImageView.sd_setImage(with: URL(string: photoURL), placeholderImage: UIImage(named: "industribune-default-no-profile-pic"))
}
}
}
I am displaying this cell on my HomeViewController:
import UIKit
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage
import Firebase
class HomeViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var activityIndicatorView: UIActivityIndicatorView!
var posts = [UserFile]()
var users = [UserFile]()
override func viewDidLoad() {
super.viewDidLoad()
// for performance set an estimated row height
tableView.estimatedRowHeight = 1
// but also request to dynamically adjust to content using AutoLayout
tableView.rowHeight = UITableViewAutomaticDimension
//tableView.delegate = self
tableView.dataSource = self
loadPosts()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadPosts() {
activityIndicatorView.startAnimating()
API.User.observePosts { (newPost) in
guard let userID = newPost.uid else { return }
self.fetchUser(uid: userID, completed: {
// append the new Post and Reload after the user
// has been cached
self.posts.append(newPost)
self.activityIndicatorView.stopAnimating()
self.tableView.reloadData()
})
}
}
func fetchUser(uid: String, completed: #escaping () -> Void) {
API.User.observeUser(withID: uid) { user in
self.users.append(user)
completed()
}
}
}
extension HomeViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
cell.post = posts[indexPath.row]
cell.user = users[indexPath.row]
cell.homeVC = self
return cell
}
}
I have a lot of craziness going on in my project so let me know if you have any questions and what I'm doing wrong. If it's too complicated to understand I'm ready to erase everything and start over too.
And I do honestly think that I followed all the guidelines to ask a question so don't like shut this question down or something.
That's a lot of code. Try this super reduced example. For this, the users node only stores the name as a child node but it could also have an image, email, address, etc.
Example users node
users
uid_0:
name: "Bert"
uid_1:
name: "Ernie"
and some code
var usersArray = [ [String: Any] ]() //an array of dictionaries.
class ViewController: UIViewController {
//set up firebase references here
override func viewDidLoad() {
super.viewDidLoad()
let usersRef = self.ref.child("users")
usersRef.observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children {
let snap = child as! DataSnapshot
let userDict = snap.value as! [String: Any]
self.usersArray.append(userDict)
}
self.tableView.reloadData()
})
and the tableView delegate methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.usersArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTableViewCell", for: indexPath) as! HomeTableViewCell
let userDict = self.usersArray[indexPath.row]
cell.text = userDict["name"] as! String
//cell.imge = userDict["image"] etc etc
return cell
}
Now... that all being said. This is the perfect use for an array of UserClass objects instead of the dictionaries.
Here's a starting point....
class UserClass {
var name = ""
var image = ""
func init(snap: DataSnapshot) {
//populate the vars from the snapshot
}
}
var userClassArray = [UserClass]()
Don't copy and paste this as there are probably typos but it should point you in the right direction.

SearchBar does not stay in place (table headerview) - swift

I have made a searchbar in my table header view. I actually would not mind having it in my navigation bar, by setting it to: navigationItem.titleView = searchController.searchBar.. The problem is though that when the searchBar is active, the navigation bar is dismissed - including the searchBar, So you cannot see what you are searching for?
How can I change that? Here is my complete searchBar code:
import UIKit
import FirebaseDatabase
import FirebaseAuth
import FBSDKCoreKit
import FirebaseStorage
class SearchTableViewController: UITableViewController, UISearchResultsUpdating {
#IBOutlet var searchUsersTableView: UITableView!
let searchController = UISearchController(searchResultsController: nil)
var usersArray = [NSDictionary?]()
var filteredUsers = [NSDictionary?]()
let databaseRef = FIRDatabase.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
searchController.searchResultsUpdater = self
searchController.dimsBackgroundDuringPresentation = false
definesPresentationContext = true
//tableView.tableHeaderView = searchController.searchBar
navigationItem.titleView = searchController.searchBar
searchController.searchBar.placeholder = "Søg"
searchController.searchBar.setValue("Annuller", forKey:"_cancelButtonText")
let cancelButtonAttributes: NSDictionary = [NSForegroundColorAttributeName: UIColor.whiteColor()]
UIBarButtonItem.appearance().setTitleTextAttributes(cancelButtonAttributes as? [String : AnyObject], forState: UIControlState.Normal)
databaseRef.child("UserInformation").queryOrderedByChild("userName").observeEventType(.ChildAdded, withBlock: { (snapshot) in
self.usersArray.append(snapshot.value as? NSDictionary)
self.searchUsersTableView.insertRowsAtIndexPaths([NSIndexPath(forRow: self.usersArray.count-1, inSection: 0)], withRowAnimation: .Automatic)
}) { (error) in
print(error.localizedDescription)
}
}
override func viewWillAppear(animated: Bool) {
if let user = FIRAuth.auth()?.currentUser {
let userId = user.uid
FIRDatabase.database().reference().child("Users").child(userId).child("NotificationBadge").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let badgesNumber = value?["numberOfBadges"] as! Int?
if badgesNumber != nil {
self.tabBarController?.tabBar.items?[3].badgeValue = String(badgesNumber!)
} else {
self.tabBarController?.tabBar.items?[3].badgeValue = nil
}
// ...
}) { (error) in
print(error.localizedDescription)
}
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showProfile" {
if let nextVC = segue.destinationViewController as? theProfileTableViewController {
nextVC.viaSegue = sender! as! String
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if searchController.active && searchController.searchBar.text != "" {
return filteredUsers.count
}
return usersArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:SearchTableViewCell = tableView.dequeueReusableCellWithIdentifier("searchCell", forIndexPath: indexPath) as! SearchTableViewCell
let user: NSDictionary?
if searchController.active && searchController.searchBar.text != "" {
user = filteredUsers[indexPath.row]
} else {
user = self.usersArray[indexPath.row]
}
cell.nameLabel.text = user?["userName"] as? String
let profileUserIDPic = user?["usersUID"] as? String
let storage = FIRStorage.storage()
// Refer to your own Firebase storage
let storageRef = storage.referenceForURL("gs://bigr-1d864.appspot.com")
let profilePicRef = storageRef.child(profileUserIDPic!+"/profile_pic.jpg")
// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
profilePicRef.dataWithMaxSize(1 * 300 * 300) { (data, error) -> Void in
if (error != nil) {
// Uh-oh, an error occurred!
print("Unable to download image")
cell.profilePic.image = UIImage(named: "profile.png")
cell.profilePic.layer.cornerRadius = cell.profilePic.frame.size.width/2
cell.profilePic.clipsToBounds = true
} else {
// Data for "images/island.jpg" is returned
// ... let islandImage: UIImage! = UIImage(data: data!)
if (data != nil){
cell.profilePic.image = UIImage(data: data!)
cell.profilePic.layer.cornerRadius = cell.profilePic.frame.size.width/2
cell.profilePic.clipsToBounds = true
}
}
}
return cell
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
// update something
filterContent(self.searchController.searchBar.text!)
}
func filterContent(searchText: String) {
self.filteredUsers = self.usersArray.filter{ user in
let username = user!["userName"] as? String
return (username?.lowercaseString.containsString(searchText.lowercaseString))!
}
self.tableView.reloadData()
}
}
When going to the search page it looks like this (because I added the insets
When searching for a user it looks like this
When having click a user and returned to search it looks like this

Dealing with switch in Tableview at swift 2.2

I have one screen like the following picture:
I uploaded list with student name by using custom cell as you are seeing and I want when click on save button save the status of student in array , I initialized array with 0 for all student at the first time and when the status of switch is enabled then this value at the clicked cell index converted to 1 but I couldn't make that when the click action happened on switch this is only now happening when click on the cell ( row ) how I can do the same thing when only change the status of switch to update the array without click on complete row at table
Code of main view :
import UIKit
class teacherAttendanceVC: UIViewController , UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var studentlistTable: UITableView!
#IBOutlet weak var loadIndicator: UIActivityIndicatorView!
var username:String?
var classID: String?
var branchID: String?
var normal_id = [String]()
var student_name = [String]()
var student_attendance = [String]()
//Sent Data
var n_id = ""
var stu_name = ""
#IBAction func backButton(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil )
}
override func viewDidLoad() {
super.viewDidLoad()
studentlistTable.delegate = self
studentlistTable.dataSource = self
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
username = prefs.objectForKey("user")as! String
classID = prefs.objectForKey("ClassID")as! String
branchID = prefs.objectForKey("BranchID")as! String
self.loadIndicator.startAnimating()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
self.loadList()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.loadIndicator.stopAnimating()
self.studentlistTable.reloadData()
})
});
}
override func viewDidAppear(animated: Bool) {
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return normal_id.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//This method to define each cell at Table View
let cell = self.studentlistTable.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! teacherAttendanceCell
cell.studentNameLabel.text = student_name[indexPath.row]
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Get Cell Label
let currentCell = studentlistTable.cellForRowAtIndexPath(indexPath) as! teacherAttendanceCell!
student_attendance[indexPath.row] = currentCell.status
}
#IBAction func saveButton(sender: AnyObject) {
print(student_attendance) // this only to ensure from the final array before sending to server
}
func loadList()
{
var normallink = "myurl"
normallink = normallink + "?classid=" + self.classID! + "&branchid=" + self.branchID!
print(normallink)
var studentParentURL:NSURL = NSURL (string: normallink)!
let data = NSData(contentsOfURL: studentParentURL)!
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
if let alldata = json["data"] as? [[String: AnyObject]] {
for onedata in alldata {
if let no_id = onedata["id"] as? String {
normal_id.append(no_id)
}
if let s_name = onedata["studentName"] as? String {
student_name.append(s_name)
}
}
}
} catch {
print("Error Serializing JSON: \(error)")
}
if(normal_id.count != 0)
{
for i in 1...self.normal_id.count
{
self.student_attendance.append("0")
}
}
print(normal_id.count)
print(student_name.count)
}
}
Cell Code :
class teacherAttendanceCell: UITableViewCell {
#IBOutlet weak var studentNameLabel: UILabel!
#IBOutlet weak var attendSwitch: UISwitch!
var status:String = ""
override func awakeFromNib() {
super.awakeFromNib()
if(attendSwitch.on)
{
status = "1"
print("ON")
}
else{
status = "0"
print("OFF")
}
attendSwitch.addTarget(self, action: "stateChanged:", forControlEvents: UIControlEvents.ValueChanged)
}
func stateChanged(switchState: UISwitch) {
if switchState.on {
status = "1"
print("ON")
} else {
status = "0"
print("OFF")
}
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBAction func attendSwitchChanged(sender: AnyObject) {
}
}
Updated:
Main View Controller:
import UIKit
class teacherAttendanceVC: UIViewController , UITableViewDataSource,UITableViewDelegate,CellInfoDelegate {
#IBOutlet weak var studentlistTable: UITableView!
#IBOutlet weak var loadIndicator: UIActivityIndicatorView!
var username:String?
var classID: String?
var branchID: String?
var normal_id = [String]()
var student_name = [String]()
var student_attendance = [String]()
//Sent Data
var n_id = ""
var stu_name = ""
#IBAction func backButton(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil )
}
override func viewDidLoad() {
super.viewDidLoad()
studentlistTable.delegate = self
studentlistTable.dataSource = self
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
username = prefs.objectForKey("user")as! String
classID = prefs.objectForKey("ClassID")as! String
branchID = prefs.objectForKey("BranchID")as! String
self.loadIndicator.startAnimating()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
self.loadList()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.loadIndicator.stopAnimating()
self.studentlistTable.reloadData()
})
});
}
override func viewDidAppear(animated: Bool) {
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return normal_id.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//This method to define each cell at Table View
let cell = self.studentlistTable.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! teacherAttendanceCell
cell.delegate = self
cell.studentNameLabel.text = student_name[indexPath.row]
student_attendance[indexPath.row] = cell.status
//print(student_attendance.count)
//let currentCell = studentlistTable.cellForRowAtIndexPath(indexPath) as! teacherAttendanceCell!
// student_attendance.append(cell.status)
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// Get Cell Label
// let currentCell = studentlistTable.cellForRowAtIndexPath(indexPath) as! teacherAttendanceCell!
// student_attendance[indexPath.row] = currentCell.status
//print("OK Status here!" + String(student_attendance.count))
}
#IBAction func saveButton(sender: AnyObject) {
print(student_attendance)
}
func loadList()
{
var normallink = "mylinkhere"
normallink = normallink + "?classid=" + self.classID! + "&branchid=" + self.branchID!
print(normallink)
var studentParentURL:NSURL = NSURL (string: normallink)!
let data = NSData(contentsOfURL: studentParentURL)!
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
if let alldata = json["data"] as? [[String: AnyObject]] {
for onedata in alldata {
if let no_id = onedata["id"] as? String {
normal_id.append(no_id)
}
if let s_name = onedata["studentName"] as? String {
student_name.append(s_name)
}
}
}
} catch {
print("Error Serializing JSON: \(error)")
}
if(normal_id.count != 0)
{
for i in 1...self.normal_id.count
{
self.student_attendance.append("0")
}
}
print(normal_id.count)
print(student_name.count)
}
func processThatNumber(theStatus: String) {
print("out : \(theStatus)")
}
}
protocol CellInfoDelegate {
func processThatNumber(theStatus: String)
}
Cell View Controller:
import UIKit
class teacherAttendanceCell: UITableViewCell{
#IBOutlet weak var studentNameLabel: UILabel!
#IBOutlet weak var attendSwitch: UISwitch!
var status:String = ""
var delegate: CellInfoDelegate?
override func awakeFromNib() {
super.awakeFromNib()
if(attendSwitch.on)
{
status = "1"
print("ON")
}
else{
status = "0"
print("OFF")
}
attendSwitch.addTarget(self, action: "stateChanged:", forControlEvents: UIControlEvents.ValueChanged)
}
func stateChanged(switchState: UISwitch) {
if switchState.on {
status = "1"
print("ON")
} else {
status = "0"
print("OFF")
}
if let delegate = self.delegate {
delegate.processThatNumber(self.status)
}
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
#IBAction func attendSwitchChanged(sender: AnyObject) {
}
}
There are few ways to do this: using closure or delegate, but I preferred to use delegate.
Create a delegate for your teacherAttendanceCell cell like in this answer https://stackoverflow.com/a/25792213/2739795
Make you teacherAttendanceVC conforms the delegate
Each time when cellForRowAtIndexPath calls set cell.delegate = self. Also return the cell into your delegate method
Call method from delegate insidestateChanged
And when delegate method calls you can get an index if switched cell
tableView.indexPathForCell(cellFromParam)

Resources