chat messenger UITableViewCell, loading message - ios

i am trying to load messages into my messenger using firebase, however, it isnt showing. I set my cells background to light gray and i can see it's working as the number of messages in database and cells in light gray matched.
the code i used is
func loadMsg() {
let toId = user!.id!
let fromId = Auth.auth().currentUser!.uid
let ref = Database.database().reference().child("privateMessages").child(fromId).child(toId)
ref.observe(.value) { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [DataSnapshot] {
self.messages.removeAll()
for data in snapshot {
let newMsg = Message(dictionary: data.value as! [String: AnyObject])
self.messages.append(newMsg)
}
}
DispatchQueue.main.async {self.tableView.reloadData()}
}
}
my firebase database looks like:
and my simulator looks like
as of my storyboard:
any reason why my text aren't showing???
*UPDATED BELOW
i tried to configure my cells, and now it seems to be going to the right direction but I am having crash on my sentView where it crashes due to
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an
Optional value
my code for my tableview cell is
import UIKit
import Firebase
import FirebaseDatabase
class ChatMessageCell: UITableViewCell {
var message: Message!
#IBOutlet weak var receivedMsgLabel: UILabel!
#IBOutlet weak var sentMsgLabel: UILabel!
#IBOutlet weak var sentView: UIView!
#IBOutlet weak var receivedView: UIView!
var currentUser = Auth.auth().currentUser!.uid
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func configCell(message: Message) {
self.message = message
if message.fromId == currentUser {
sentView.isHidden = false
sentMsgLabel.text = message.textMessages
receivedMsgLabel.text = ""
receivedMsgLabel.isHidden = true
} else {
sentView.isHidden = true
sentMsgLabel.text = ""
receivedMsgLabel.text = message.textMessages
receivedMsgLabel.isHidden = false
}
}
}
as for my table is
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: ChatMessageCell = self.tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! ChatMessageCell
let message = messages[indexPath.row]
cell.configCell(message: message)
cell.backgroundColor = UIColor.lightGray
return cell
}
am i going in the right direction?? sorry not sure where to post my codes, therefore posting it in answer section.

"The unbalanced calls to begin/end appearance transitions"
Crash occurs when you try and display a content before the cell is finished displaying.
Try by creating new custom cell xib and try.
register like this.
self.tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "TableViewCell")
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:TableViewCell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
let message = messages[indexPath.row]
cell.configCell(message: message)
cell.backgroundColor = UIColor.lightGray
return cell
}

Related

How to utilize a custom tableview cell nib in iOS with a swipe table view controller as the cell's default controller

I am a beginner and I'm having some issues with an iOS app I'm creating. I am utilizing the SwipeCellKit package to have swipeable cells for my tableViews. I would also like to use a custom cell to display birthdays. I created a custom tableView cell and nib. The issue that I'm running into is properly coding the nib into my birthday tableView controller so it will display the information. Below is a picture of my code. I'd really appreciate if someone could point me in the right direction.
import UIKit
import RealmSwift
import UserNotifications
class BirthdayTableViewController: SwipeTableViewController {
#IBOutlet weak var name: UILabel!
#IBOutlet weak var birthdayLabel: UILabel!
#IBOutlet weak var age: UILabel!
let realm = try! Realm()
var birthdays: Results<Birthday>?
let dateFormatter = DateFormatter()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(BirthdayTableViewCell.nib(), forCellReuseIdentifier: BirthdayTableViewCell.identifier)
tableView.rowHeight = 100
tableView.separatorStyle = .none
}
override func viewWillAppear(_ animated: Bool) {
loadBirthdays()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return birthdays?.count ?? 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = super.tableView(tableView, cellForRowAt: indexPath)
guard let birthdayCell = (tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! BirthdayTableViewCell) else {fatalError()}
let birthday = birthdays?[indexPath.row]
let firstName = birthday?.firstName ?? ""
let lastName = birthday?.lastName ?? ""
name?.text = firstName + " " + lastName
if let date = birthday?.birthdate as Date? {
birthdayLabel?.text = dateFormatter.string(from: date)
} else {
birthdayLabel.text = " "
}
return cell
}
[Beginning of Code][1]
[TableView Methods][2]
[1]: https://i.stack.imgur.com/fZspG.png
[2]: https://i.stack.imgur.com/9IlD1.png
The app crashes due to casting a result of
tableView.dequeueReusableCell(withIdentifier:for:)
with force unwrap as! which returns non optional object.
To solve the error, just change it to as?
There is another thing which can lead to an error as well, you typed direct identifier of a cell instead using the identifier BirthdayTableViewCell.identifier
guard let birthdayCell = (tableView.dequeueReusableCell(withIdentifier: BirthdayTableViewCell.identifier, for: indexPath) as? BirthdayTableViewCell) else {fatalError()}

Why is this not displaying information in a UITableView?

I would really appreciate any help on this. I'm very new to coding, and have no luck implementing this feature so far. I'm looking to populate a UITableViewCell with information gathered from Firestore, namely: title, username and content. I've been able to print the 'title' array successfully, but have not been able to actually populate this into the cells.
This is the HomeViewController, where my UITableView is:
class HomeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var logoutButton: UIButton!
var postArray: [String] = []
var documents: [DocumentSnapshot] = []
let db = Firestore.firestore()
let currentUserID = Auth.auth().currentUser?.uid
// Find the UserIDs of people following
// Where Field for those UserIDs in "Posts"
override func viewDidLoad() {
super.viewDidLoad()
getFollowingPosts()
configureTableView()
}
func getFollowingPosts() {
let searchForFollowing = db.collection("users").document(currentUserID!).collection("Following")
searchForFollowing.getDocuments { (snapshot, error) in
for documents in snapshot!.documents {
let followedUID = documents.get("uid")
print(followedUID!)
self.db.collection("posts").whereField("uid", isEqualTo: followedUID!).getDocuments { (querySnapshot, error) in
for documents in querySnapshot!.documents {
let uid = documents.get("uid") as! String
let title = documents.get("Title") as! String
let ProfilePictureURL = documents.get("ProfilePictureURL") as! String
let username = documents.get("username") as! String
let content = documents.get("Content") as! String
self.postArray.append(title)
print(self.postArray)
}
self.tableView.reloadData()
}
}
}
}
func configureTableView() {
tableView.delegate = self
tableView.dataSource = self
tableView.register(PostTableViewCell.self, forCellReuseIdentifier: "PostCell")
// remove separators for empty cells
tableView.tableFooterView = UIView()
// remove separators from cells
tableView.separatorStyle = .none
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
postArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as! PostTableViewCell
let post = postArray[indexPath.row]
return cell
}
}
This is my PostTableViewCell:
class PostTableViewCell: UITableViewCell {
#IBOutlet weak var usernameLabel: UILabel!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var contentLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
addSubview(usernameLabel)
addSubview(titleLabel)
addSubview(contentLabel)
}
}
If anyone could help, this would be massively appreciated. Like I said, I've been struggling a lot with this one.
You don't seem to be setting the data onto anything in the cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as! PostTableViewCell
let post = postArray[indexPath.row]
cell.titleLabel.text = post
return cell
}
Also, modify the register method if you're using nib
func configureTableView() {
//...
tableView.register(UINib(nibName: "PostCell", bundle: nil), forCellReuseIdentifier: "PostCell")
//...
}
Note: Make sure that the nib file has nib's identifier set as "PostCell".

UITableViewCell data not showing up in UITableViewController

I am having trouble debugging why my UITableview cell data isn't showing up in the UITableview. The UITableview currently displays blank when the user navigates to it. Data is correctly going into the cellForRowAt and into the function that sets the cell data.
Setting the cell data
class EventInboxTableViewCell: UITableViewCell {
#IBOutlet weak var eventNameLabel: UILabel!
#IBOutlet weak var eventCoverImageView: UIImageView!
#IBOutlet weak var eventStartLabel: UILabel!
#IBOutlet weak var eventEndLabel: UILabel!
var eventStartString = String()
var eventEndString = String()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setEvent(_ event:Event) {
eventNameLabel?.text = event.eventName
if event.eventStart != nil {
let eventStartTS = event.eventStart
let eventStartDate = eventStartTS?.dateValue()
self.eventStartString = AppWideService.dateToStringShort(date: eventStartDate!)
}
if event.eventEnd != nil {
let eventEndTS = event.eventEnd
let eventEndDate = eventEndTS?.dateValue()
self.eventEndString = AppWideService.dateToStringShort(date: eventEndDate!)
}
print("Event inbox event \(eventStartString)")
print("Event inbox event \(eventEndString)")
eventStartLabel?.text = self.eventStartString
eventEndLabel?.text = self.eventEndString
guard let urlString = event.eventCoverUrl as? String else { return }
let url = URL(string: urlString)
guard url != nil else {
//Couldn't create url object
return
}
eventCoverImageView?.sd_setImage(with: url) { (image, error, cacheType, url) in
self.eventCoverImageView?.image = image
}}}
For some reason when I remove the ? from setting the label text it says the values like eventName or eventStartString etc are nil, but I have print statements that ensure they are not.
UITableView Datasource
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return retrievedEvents.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "EventInboxTableViewCell", for: indexPath) as! EventInboxTableViewCell
let event = retrievedEvents[indexPath.row]
cell.setEvent(event)
return cell
}
Registered the cell in viewDidLoad
tableView.register(EventInboxTableViewCell.self, forCellReuseIdentifier: "EventInboxTableViewCell")
The problem is the way the table view controller was being used.
If you design a View Controller (of any type) in Storyboard, and you want to use it, you cannot simply say:
let vc = EventInboxTableViewController()
you have to instantiate it from the storyboard:
if let vc = storyboard?.instantiateViewController(withIdentifier: "EventInboxTableViewController") as? EventInboxTableViewController {
navigationController?.pushViewControllerFromLeft(controller: vc)
}
So, in Storyboard, assign your custom class to your UITableViewController, and make sure to fill in the Storyboard ID field (with the string you are using in code as the Identifier).

Swift: Cannot put my value into label (optional issue)

I have my value from Firebase but Swift doesn't want to put it in my label.
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
To summarize, my database look like that:
Firebase database
I've created a standard model call ServiceModel:
import Foundation
class ServiceModel {
var name: String?
var category: String?
var pricing: String?
init(name: String?, category: String?, pricing: String?){
self.name = name
self.category = category
self.pricing = pricing
}
}
I want to display this values into a TableView, so I've created a custom cell like this (very standard too):
import UIKit
class SubscriptionTableViewCell: UITableViewCell {
#IBOutlet weak var imageService: UIImageView!
#IBOutlet weak var labelName: UILabel!
#IBOutlet weak var labelCategory: UILabel!
#IBOutlet weak var labelPricing: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
And now, here is the controller of my view:
import UIKit
import FirebaseDatabase
class SecondViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
var refServices:DatabaseReference!
#IBOutlet weak var ListSub: UITableView!
var serviceList = [ServiceModel]()
var databaseHandle:DatabaseHandle?
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return serviceList.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SubCell", for: indexPath) as! SubscriptionTableViewCell
let service: ServiceModel
service = serviceList[indexPath.row]
//cell.imageService.image = UIImage(named: service.name! + ".png")
cell.labelName?.text = service.name //ERROR HERE
cell.labelCategory?.text = service.category
cell.labelPricing?.text = service.pricing
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
ListSub.delegate = self
ListSub.dataSource = self
refServices = Database.database().reference().child("Categories");
refServices.observe(DataEventType.value, with: { (snapshot) in
if snapshot.childrenCount > 0 {
self.serviceList.removeAll()
for services in snapshot.children.allObjects as! [DataSnapshot] {
let serviceObject = services.value as? [String: AnyObject]
let serviceName = serviceObject?["Name"]
let serviceCategory = serviceObject?["Category"]
let servicePricing = serviceObject?["Pricing"]
let service = ServiceModel(name: serviceName as! String?, category: serviceCategory as! String?, pricing: servicePricing as! String?)
self.serviceList.append(service)
}
self.ListSub.reloadData()
}
})
}
When I launch this view, I have the error mentioned earlier.
When I debug, I see that I have the right values in service.name, service.category and service.pricing
It seems that I don't correctly handle Optional values, but I cannot see what is wrong.
Thanks for your help.
Potential lines to be crashed in case of optional unwrapping is this line
refServices = Database.database().reference().child("Categories");
refServices.observe(DataEventType.value, with: { (snapshot) in
Try to pur breakpoint and check if refServices is initialised properly or make ti to be optional not using !
Hope this help
Ps. please remove ; out of your Swift code :P
Use this code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SubCell", for: indexPath) as! SubscriptionTableViewCell
let service = serviceList[indexPath.row]
// If you sure that you have to display all the info use this code
if let name = service.name, let category = service.category, let price = service.pricing {
cell.labelName.text = name
// set other data also here....
}
// If you know any value may be empty or not exists then use this code.
if let name = service.name {
cell.labelName.text = name
}
if let category = service.category {
cell.labelCategory.text = service
}
if let pricing = service.pricing {
cell.labelPricing.text = pricing
}
return cell
}
Did you register your custom UITableViewCell with your tableView? Put this line into the init() function of your ViewController:
ListSub.register(SubscriptionTableViewCell.classForCoder(), forCellReuseIdentifier: "SubCell")
If you debug this function, what did you see for your service
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SubCell", for: indexPath) as! SubscriptionTableViewCell
let service: ServiceModel
//Put a breakpoint here
service = serviceList[indexPath.row]
//Put a breakpoint here
//cell.imageService.image = UIImage(named: service.name! + ".png")
cell.labelName?.text = service.name
cell.labelCategory?.text = service.category
cell.labelPricing?.text = service.pricing
return cell
}

How do I add a UIButton into my UITableViewCell in Swift 3?

I have an existing UITableView that displays data and is working fine.
However I now want to add an info button into this UITableViewCell.
I added the UIButton directly into the TableViewCell in storyboard. I then tried to declare this button as an outlet but I got the error
"Outlets cannot be connected to repeating content."
I read around the subject and decided to create a new subclass called "
import UIKit
class PersonalStatsTableViewCell: UITableViewCell {
#IBOutlet weak var personalStatsInfoButton: UIButton!
var selectedCellTitle: String?
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
As you can see I have declared the UIButton personalStatsInfoButton in this sub-class.
With more reading around the subject I believe I need to add something like:
personalStatsInfoButton.tag = indexPath.row
personalStatsInfoButton.addTarget(self, action: "infoButtonClicked", forControlEvents: UIControlEvents.TouchUpInside)
and then have a function:
function infoButtonClicked(sender:UIButton){
let infoCell = sender.tag
print (infoCell)
}
My issue is I don't know whether I need to take all my existing tableView code and transfer it into the the new sub-class PersonalStatsTableViewCell or just the parts that deal with the info button.
Below is my existing VC code that initially deals with the TableView prior to adding in this new button.
import UIKit
class ShowCommunityViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var membersTableView: UITableView!
#IBOutlet weak var communityName: UILabel!
var communityIsCalled: String?
var comIds = [String]()
var communityId: Int?
var selectedCellTitle: String?
var cellId: Int?
var communityPlayerIds = [String]()
var communityPlayers = [String?]()
override func viewDidLoad() {
super.viewDidLoad()
communityName.text = (communityIsCalled)
self.membersTableView.delegate = self
self.membersTableView.dataSource = self
membersTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.communityPlayers.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonalStatsTableViewCell", for: indexPath as IndexPath)
cell.textLabel?.text = self.communityPlayers[indexPath.row]
cell.textLabel?.font = UIFont(name: "Avenir", size: 12)
cell.textLabel?.textColor = UIColor.white // set to any colour
cell.layer.backgroundColor = UIColor.clear.cgColor
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectedCellTitle = self.communityPlayers[indexPath.row]
cellId = indexPath.row
}
override func viewDidAppear(_ animated: Bool) {
let myUrl = URL(string: "http://www.???.uk/???/specificCommunity.php?");
var request = URLRequest(url:myUrl!);
request.httpMethod = "POST";
let postString = "id=\(comIds[communityId!])";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
DispatchQueue.main.async
{
if error != nil {
print("error=\(error)")
return
}
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject]
if let arr = json?["players"] as? [[String:String]] {
self.communityPlayerIds = arr.flatMap { $0["id"]!}
self.communityPlayers = arr.flatMap { $0["user_name"]!}
self.membersTableView.reloadData()
print ("names: ",self.communityPlayers)
}
} catch{
print(error)
}
}
}
task.resume()
}
}
You don't need to put any code in your class PersonalStatsTableViewCell you can manage all the things from ShowCommunityViewController what you need to done is in your cellForRowAt method add this
cell.personalStatsInfoButton.tag = indexPath.row
cell.personalStatsInfoButton.addTarget(self, action: #selector(infoButtonClicked(sender:), forControlEvents: UIControlEvents.TouchUpInside)
and add this function
function infoButtonClicked(sender:UIButton){
let infoCell = sender.tag
print (infoCell)
}
Your code and what you are thinking is correct, you just need to change the following line.
Apart from what Arun B has said, you need to make sure xcode knows what kind of class cell will belong to.
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonalStatsTableViewCell", for: indexPath as IndexPath)
should be
let cell = tableView.dequeueReusableCell(withIdentifier: "PersonalStatsTableViewCell", for: indexPath as IndexPath) as! PersonalStatsTableViewCell
This happens if the custom class is not set up properly. Make sure that PersonalStatsTableViewCell is set as the Custom class of the UITableViewCell in your storyboard.

Resources