Hide imageView and Label by setting some kind of flag - ios

Can you guys give me some help to hide the image and name if the message it's from the same user... I want only to show it for the first message...and if that user send more not to show anymore until another Id appear... like whatsapp does it..
currently I m having like this to show u an example
[![enter image description here][1]][1]
var isTheSameUser = false
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let selectedChat = chat[indexPath.row]
let myId = String(describing:UserDefaults.standard.value(forKey: "user_id")!)
let messageUserId = String(describing:selectedChat.owner_id)
if messageUserId == myId {
return myMessageCell(indexPath: indexPath)
}else {
return userMessageCell(indexPath: indexPath)
}
}
func myMessageCell(indexPath :IndexPath) -> UITableViewCell {
let cell = self.mainTableView.dequeueReusableCell(withIdentifier: "MyMessageTableViewCell", for: indexPath) as! MyMessageTableViewCell
let selectedChat = self.chat[indexPath.row]
let myId = String(describing:UserDefaults.standard.value(forKey: "user_id")!)
// Show only for the first message
// photo image
if !isTheSameUser {
cell.profileImageView.isHidden = false
cell.profileNameLabel.isHidden = false
} else {
cell.profileImageView.isHidden = true
cell.profileNameLabel.isHidden = true
}
if let userInfo = getUserMemberOf(userId: messageUserId) {
cell.profileImageView.imageFromURL(urlString: userInfo["photo"] as! String)
} else {
cell.profileImageView.image = #imageLiteral(resourceName: "accountIcon")
}
cell.profileNameLabel.text = "\(String(describing: cell.userProfileInfo!["name"]!))"
return cell

You need check the previous chat messsage id like for every message
if indexPath.row != 0 {
let prevItem = chat[indexPath.row - 1]
let currentItem = chat[indexPath.row]
if prevItem.owner_id! == currentItem.owner_id! {
// hide label and image
}else {
// show them
}
}
else {
// show them
}

Related

how to reduce the code in tableView cell in swift

hey i'm in the process of learning to code. I created an app that downloads JSON data - covid.
It looks like this :
enter image description here
my code in function (code below) has become terribly large.
how can I reduce this code?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Cells.covidCell, for: indexPath) as! CovidCell
if inSearchMode == true {
cell.countryLabel.text = filterCovidData[indexPath.row].country
cell.regionLabele.text = filterCovidData[indexPath.row].continent
cell.casesLabel.text = "Case: \(filterCovidData[indexPath.row].cases!)"
cell.deathLabel.text = "Death: \(filterCovidData[indexPath.row].deaths!)"
cell.activelabel.text = "Active: \(filterCovidData[indexPath.row].active!)"
cell.testsLabel.text = "Test: \(filterCovidData[indexPath.row].tests!)"
cell.todayCasesInfo.text = "\(filterCovidData[indexPath.row].todayCases!)"
let imageUrl = filterCovidData[indexPath.row].countryInfo?.flag
fetchImage(withUrlString: imageUrl!) { (image) in
DispatchQueue.main.async {
cell.countryFlag.image = image
}
}
} else {
cell.countryLabel.text = covidData[indexPath.row].country
cell.regionLabele.text = covidData[indexPath.row].continent
cell.casesLabel.text = "Case: \(covidData[indexPath.row].cases!)"
cell.deathLabel.text = "Death: \(covidData[indexPath.row].deaths!)"
cell.activelabel.text = "Active: \(covidData[indexPath.row].active!)"
cell.testsLabel.text = "Test: \(covidData[indexPath.row].tests!)"
cell.todayCasesInfo.text = "\(covidData[indexPath.row].todayCases!)"
let imageUrl = covidData[indexPath.row].countryInfo?.flag
fetchImage(withUrlString: imageUrl!) { (image) in
DispatchQueue.main.async {
cell.countryFlag.image = image
}
}
}
return cell
}
See the repetitive code.
You do the same, except the source of the populate, so, let's just retrieve the model according to your needs (inSearchMode), and then let's call the same code.
let model = inSearchMode ? filterCovidData[indexPath.row] : covidData[indexPath.row]
cell.countryLabel.text = model.country
cell.regionLabele.text = model.continent
cell.casesLabel.text = "Case: \(model.cases!)"
cell.deathLabel.text = "Death: \(model.deaths!)"
cell.activelabel.text = "Active: \(model.active!)"
cell.testsLabel.text = "Test: \(model.tests!)"
cell.todayCasesInfo.text = "\(model.todayCases!)"
let imageUrl = model.countryInfo?.flag
fetchImage(withUrlString: imageUrl!) { (image) in //I'duse a [weak self] here
DispatchQueue.main.async {
cell.countryFlag.image = image
}
}
That's be the first step.
You can have another logic at start:
let arrayToUse = inSearchMode ? filterCovidData : covidData
let model = arrayToUse[indexPath.row]
You can also add a code in CovidCell
func update(model: ModelThatsInsideCovidData) {
countryLabel.text = model.country
regionLabele.text = model.continent
casesLabel.text = "Case: \(model.cases!)"
deathLabel.text = "Death: \(model.deaths!)"
activelabel.text = "Active: \(model.active!)"
testsLabel.text = "Test: \(model.tests!)"
todayCasesInfo.text = "\(model.todayCases!)"
let imageUrl = model.countryInfo?.flag
//Here cell doesn't have that method, should it be accessible?, I'll let you decide.
fetchImage(withUrlString: imageUrl!) { (image) in
DispatchQueue.main.async {
self.countryFlag.image = image
}
}
And then, in cellForRowAt:
let model = ...
cell.update(model: model)
return cell

Dequeue Reusable Cell crashes when calling dequeued cell

I am attempting to have a table view that lists multiple things and allows a user to go through and select multiple cells with checkboxes. My code works up until a certain point, the problem is that the app crashes with the following error
Fatal error: Unexpectedly found nil while unwrapping an Optional value
whenever I call the following code
swift let currentCell = recommendToFriendTableView.cellForRow(at: selectedRow[i]) as? RecommendToFriendsTableViewCell
Here is the method where we set up the cells
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if (tableView == self.friendListTableView) {
let cell: FriendListTableViewCell = tableView.dequeueReusableCell(withIdentifier: "FriendListCell") as! FriendListTableViewCell
let rowNumber = (indexPath as NSIndexPath).row
var name = ""
if searchActive {
name = filtered[rowNumber]
}
else {
name = names[rowNumber]
}
cell.friendNameLabel.text = name
cell.friendNameLabel.backgroundColor = tableViewBgColor
cell.friendNameLabel.textColor = textColor
cell.recommendToFriendButton.layer.borderWidth = 1
cell.recommendToFriendButton.layer.borderColor = tableViewBgColor.cgColor
cell.recommendToFriendButton.layer.cornerRadius = 6
cell.recommendToFriendButton.backgroundColor = buttonBgColor
cell.backgroundColor = tableViewBgColor
//set target for buttons
cell.recommendToFriendButton.tag = rowNumber
cell.recommendToFriendButton.addTarget(self, action:#selector(recommendToFriendButtonClicked), for: UIControl.Event.touchUpInside)
return cell
}
else {
let cell: RecommendToFriendsTableViewCell = tableView.dequeueReusableCell(withIdentifier: "RecommendToFriendsCell") as! RecommendToFriendsTableViewCell
let rowNumber = (indexPath as NSIndexPath).row
// set the content view background color
cell.contentView.backgroundColor = tableViewBgColor
// set the text color
cell.nameLabel.textColor = textColor
var dict_friend = NSMutableDictionary()
if searchActive {
dict_friend = filteredFriendsArray[rowNumber]
}
else {
dict_friend = friendsArray[rowNumber]
}
let name = dict_friend["name"] as! String
cell.nameLabel.text = name
let friendUID = dict_friend["uid"] as! String
cell.friendID = friendUID
let imageAddress = dict_friend["photo"] as? String
if imageAddress != "unavailable" && imageAddress != nil && imageAddress != ""{
//Swift forces us to wrap strings as optional to use them in logic
if let imageURL = imageAddress as String? {
//Swift forces us to wrap strings as optional to use them in logic
if let image = imageURL as String? {
//We convert the string into a URL and get the image
let url = URL(string: image)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
return
}
//We create a new async thread to download and update the image
DispatchQueue.main.async {
//imageView.image = UIImage(data: data!)
cell.photoImageView.image = UIImage(data:data!)
}
}).resume()
}
} else {
cell.photoImageView!.image = UIImage(named: "placeholder-profile-male.png")
}
} else {
cell.photoImageView!.image = UIImage(named: "placeholder-profile-male.png")
}
cell.checkBoxImageView.image = cell.checkBoxImageView.image!.withRenderingMode(.alwaysTemplate)
cell.checkBoxImageView.tintColor = textColor
// Style the profile photo to show in a circle
cell.photoImageView.layer.borderWidth = 0
cell.photoImageView.layer.borderColor = tableViewBgColor.cgColor
// Set cornerRadius = a square UIImageView frame size width / 2
// In our case, UIImageView height = width = 60 points
cell.photoImageView.layer.cornerRadius = 30
cell.photoImageView.clipsToBounds = true
cell.selectionStyle = .none // to prevent cells from being "highlighted"
return cell
}
}
This is the method where we interact with them. The crash happens on a cellForRow call for a cell that is out of view (aka dequeued)
var firstFriendName: String = ""
var numberOfFriends = 0
if let selectedRow = recommendToFriendTableView.indexPathsForSelectedRows {
numberOfFriends = selectedRow.count
for i in 0..<selectedRow.count {
let currentCell = recommendToFriendTableView.cellForRow(at: selectedRow[i]) as! RecommendToFriendsTableViewCell
let friendID = currentCell.friendID
idList.append(",\(friendID)")
}
let firstSelectedCell = recommendToFriendTableView.cellForRow(at: selectedRow[0]) as! RecommendToFriendsTableViewCell
firstFriendName = firstSelectedCell.nameLabel.text!
After about a day of experimenting, I've yet to figure out the actual problem (other than the observation that it appears to be in regards to calling a dequeued cell)
Any help is appreciated.
When this line
let currentCell = recommendToFriendTableView.cellForRow(at: selectedRow[i]) as! RecommendToFriendsTableViewCell
crashes this means you access a non-visble cell so either use
if let currentCell = recommendToFriendTableView.cellForRow(at: selectedRow[i]) as? RecommendToFriendsTableViewCell { }
or better use the dataSource array of the table to get the data that you want to gran wrongly from the cell

How to update a specific cell label using MZDownloadManager even if user move to any ViewController and came back

I'm using MZDownloadManger library to download the files using url, every thing is working fine except the label update, when i start the downloading it changes to "Starting Downloading" then starts its progress like 10% 20% etc. its working fine but when i move to any other view controller its progress stops and not update the label value to "Downloaded". i have set a flag in my local data base '0' and '1', 0 means not downloaded and 1 means downloaded.
here is the code when a user select the cell and hit for download:
func keepOfflineFiles(sender: UIButton) {
if files[sender.tag].onLocal == "1"{
self.displayAlert(title: AlertTitle.alert, message: AlertMsg.alreadyDownloaded)
} else{
if self.files[sender.tag].status == "on amazon"{
let indexPath = IndexPath.init(row: sender.tag, section: 0)
let cell = self.tblFilesPro.cellForRow(at: indexPath)
if let cell = cell {
let downloadCell = cell as! filesTableViewCell
downloadCell.lblDetailsPro.text = "Starting Download. . ."
}
let pathString:String = ""
let fileName:String = self.files[sender.tag].file_id! + self.files[sender.tag].Extension!
if(fileName != ""){
let local_url = NSURL(fileURLWithPath: pathString.getDocumentsPath())
let filePath = local_url.appendingPathComponent(fileName)?.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath!) {
// FILE AVAILABLE
let indexPath = IndexPath.init(row: sender.tag, section: 0)
let cell = self.tblFilesPro.cellForRow(at: indexPath)
if let cell = cell {
let downloadCell = cell as! filesTableViewCell
downloadCell.lblDetailsPro.text = "Downloaded"
}
self.fileid = self.files[sender.tag].file_id!
self.updateFileStatusToRealm()
//self.displayAlert(title: AlertTitle.alert, message: AlertMsg.alreadyDownloaded)
} else {
// FILE NOT AVAILABLE
let completeUrl:String = Tray.downloadURLBasePath + self.files[sender.tag].fileLink!
if(self.verifyUrl(urlString: completeUrl)) {
self.fileid = self.files[sender.tag].file_id!
let index:String = String(sender.tag)
self.AppDelegateObj.downloadManager.addDownloadTask(fileName as String, fileURL: completeUrl as String, destinationPath: index as String)
}
}
}
}else{
self.displayAlert(title: AlertTitle.alert, message: AlertMsg.inArchiveProcess)
}
}
}
here are the delegates of MZDownloadManager that i called in AppDelegate
To update the progress
func downloadRequestDidUpdateProgress(_ downloadModel: MZDownloadModel, index: Int) {
let root : UINavigationController = self.window?.rootViewController as! UINavigationController
if let master = root.topViewController as? TabBarController {
if let nav = master.viewControllers?[0] as? FilesVC {
nav.refreshCellForIndex(downloadModel, index: Int(downloadModel.destinationPath)!)
}
} else {
print("Somthing went wrong while downloading this file.")
}
}
When downloading finished
func downloadRequestFinished(_ downloadModel: MZDownloadModel, index: Int) {
let root : UINavigationController = self.window!.rootViewController! as! UINavigationController
if let master = root.topViewController as? TabBarController {
if let nav = master.viewControllers![0] as? FilesVC{
nav.getDownloadingStatusOfCellForIndex(downloadModel, index: Int(downloadModel.destinationPath)!)
}
} else {
print("Somthing went wrong while finishing downloading of this file.")
}
}
Method to refresh the cell label
func refreshCellForIndex(_ downloadModel: MZDownloadModel, index: Int) {
let indexPath = IndexPath.init(row: index, section: 0)
let cell = self.tblFilesPro.cellForRow(at: indexPath)
if let cell = cell {
let downloadCell = cell as? filesTableViewCell
downloadCell?.updateCellForRowAtIndexPath(indexPath, downloadModel: downloadModel)
}
}
Method to get the cell and change value
func getDownloadingStatusOfCellForIndex(_ downloadModel: MZDownloadModel, index: Int) {
let indexPath = IndexPath.init(row: index, section: 0)
let cell = self.tblFilesPro.cellForRow(at: indexPath)
if let cell = cell {
let downloadCell = cell as? filesTableViewCell
downloadCell?.lblDetailsPro.text = "Downloaded"
self.fileid = self.files[index].file_id!
self.updateFileStatusToRealm()
}
}
here is the method which change the flag value 0 to 1 in database:
func updateFileStatusToRealm(){
let fileToUpdate = uiRealm.objects(filesDataTable.self).filter("file_id = %#", self.fileid)
let realm = try! Realm()
if let file = fileToUpdate.first {
try! realm.write {
file.onLocal = "1"
tblFilesPro.reloadData()
}
}
}

Trouble reusing TableViewCells in Swift

I have some trouble reusing cells in swift. I want the code below to only execute for the cells where post.altC.isEmpty actually is true. The problem is that it makes botBtnsStackView.isHidden = true for all cells, even though altC is not empty in all. What am I doing wrong?
The code below is from my PostCell file(just a part of the configureCell code at the bottom, but it's this part that is going wrong):
if post.altC.isEmpty == true {
botBtnsStackView.isHidden = true
} else {
altCLabel.text = post.altC["text"] as? String
if let votes = post.altC["votes"]{
self.altCVotesLbl.text = "\(votes)"
}
}
cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexpath: IndexPath) -> UITableViewCell {
let post = posts[indexpath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexpath) as? PostCell{
cell.configureCell(post: post)
return cell
} else {
return PostCell()
}
}
ConfigureCell from PostCell file:
func configureCell(post: Post) {
self.post = post
//ALT A
if post.altA.isEmpty == false {
altALabel.text = post.altA["text"] as? String
if let votes = post.altA["votes"]{
self.altAVotesLbl.text = "\(votes)"
}
} else {
print("No data found in Alt A")
}
//ALT B
if post.altB.isEmpty == false {
altBLabel.text = post.altB["text"] as? String
if let votes = post.altB["votes"]{
self.altBVotesLbl.text = "\(votes)"
}
} else {
print("No data found in Alt B")
}
//ALTD
if post.altD.isEmpty == false {
altDLabel.text = post.altD["text"] as? String
if let votes = post.altD["votes"]{
self.altDVotesLbl.text = "\(votes)"
}
} else {
altDView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
altDVotesView.isHidden = true
altDLabelView.isHidden = true
}
//ALT C
if post.altC.isEmpty == true {
print("No data found in Alt C")
//altCView.isHidden = true
botBtnsStackView.isHidden = true
} else {
altCLabel.text = post.altC["text"] as? String
if let votes = post.altC["votes"]{
self.altCVotesLbl.text = "\(votes)"
}
}
Cells are reused. So anything you do in an if statement you need to undo in the else.
So your snipped needs to be changed to:
if post.altC.isEmpty == true {
botBtnsStackView.isHidden = true
} else {
botBtnsStackView.isHidden = false
altCLabel.text = post.altC["text"] as? String
if let votes = post.altC["votes"]{
self.altCVotesLbl.text = "\(votes)"
}
}
Your others need to be update as well. For example, for "ALT A":
if post.altA.isEmpty == false {
altALabel.text = post.altA["text"] as? String
if let votes = post.altA["votes"]{
self.altAVotesLbl.text = "\(votes)"
}
} else {
altALabel.text = ""
self.altAVotesLbl.text = ""
print("No data found in Alt A")
}
I'm guessing a bit here but this gives you an idea. Adjust this to suit your actual needs. The important thing to remember is that whatever you set for one condition, you must reset for other conditions.
Unrelated but you should rewrite your cellForRowAt as:
func tableView(_ tableView: UITableView, cellForRowAt indexpath: IndexPath) -> UITableViewCell {
let post = posts[indexpath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexpath) as! PostCell
cell.configureCell(post: post)
return cell
}
This is a case where a force-cast is appropriate. You want your app to crash early on in development if you have setup your cell identifier and cell type incorrectly. Once setup properly and working, it can't crash at runtime unless you do something to break it.

Swift: Table View is only returning one cell

I'm attempting to load a table view with two different prototype cells. profileCell should only load once and at the top of the table view. dogCell should count an array of dog objects named dogs downloaded from firebase. Currently, only the first cell is displaying correctly.
I think the numberOfRowsInSection method isn't accurately counting the dog objects in the dogs array. When I put a breakpoint on return dogs.count + 1 and po dogs.count the debugger keeps outputting 0.
When I use return dogs.count the table view loads but with only the profile cell. If I use return dogs.count + 1(to account for the profile cell at the top) an exception is thrown when constructing dogCell: "fatal error: Index out of range"
Perhaps I need to change the way my tableview is reloading data?
Here's my code:
class DogTableViewController: UITableViewController {
var user = User()
let profileCell = ProfileTableViewCell()
var dogs = [Dog]()
override func viewDidLoad() {
super.viewDidLoad()
let userDogRef = Database.database().reference().child("users").child(user.uid!).child("dogs")
let userProfileImageView = UIImageView()
userProfileImageView.translatesAutoresizingMaskIntoConstraints = false
userProfileImageView.widthAnchor.constraint(equalToConstant: 40).isActive = true
userProfileImageView.heightAnchor.constraint(equalToConstant: 40).isActive = true
userProfileImageView.layer.cornerRadius = 20
userProfileImageView.clipsToBounds = true
userProfileImageView.contentMode = .scaleAspectFill
userProfileImageView.image = UIImage(named: "AppIcon")
navigationItem.titleView = userProfileImageView
//MARK: Download dogs from firebase
userDogRef.observe(.childAdded, with: { (snapshot) in
if snapshot.value == nil {
print("no new dog found")
} else {
print("new dog found")
let snapshotValue = snapshot.value as! Dictionary<String, String>
let dogID = snapshotValue["dogID"]!
let dogRef = Database.database().reference().child("dogs").child(dogID)
dogRef.observeSingleEvent(of: .value, with: { (snap) in
print("Found dog data!")
let value = snap.value as? NSDictionary
let newDog = Dog()
newDog.name = value?["name"] as? String ?? ""
newDog.breed = value?["breed"] as? String ?? ""
newDog.creator = value?["creator"] as? String ?? ""
newDog.score = Int(value?["score"] as? String ?? "")
newDog.imageURL = value?["imageURL"] as? String ?? ""
newDog.dogID = snapshot.key
URLSession.shared.dataTask(with: URL(string: newDog.imageURL!)!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
return
}
newDog.picture = UIImage(data: data!)!
self.dogs.append(newDog)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}).resume()
})
}
})
tableView.estimatedRowHeight = 454
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dogs.count + 1
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let profileCell = tableView.dequeueReusableCell(withIdentifier: "profileCell", for: indexPath) as! ProfileTableViewCell
profileCell.nameLabel.text = user.name
profileCell.totalReputationLabel.text = String(describing: user.reputation!)
profileCell.usernameLabel.text = user.username
return profileCell
} else {
let dogCell = tableView.dequeueReusableCell(withIdentifier: "dogCell", for: indexPath) as! DogTableViewCell
dogCell.dogBreedLabel.text = dogs[indexPath.row].breed
dogCell.dogNameLabel.text = dogs[indexPath.row].name
dogCell.dogScoreLabel.text = String(describing: dogs[indexPath.row].score)
dogCell.dogImageView.image = dogs[indexPath.row].picture
dogCell.dogCreatorButton.titleLabel?.text = dogs[indexPath.row].creator
dogCell.dogVotesLabel.text = "0"
return dogCell
}
}
}
I actually found a solution shortly after writing this question, but I think it might be helpful for others to read.
Because the first indexPath.row is dedicated to a profile cell, I should not have been using the indexPath.row to navigate my dogs array. Instead I should have been using indexPath.row - 1 to get the correct dogs index.
Here's the section I updated:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let profileCell = tableView.dequeueReusableCell(withIdentifier: "profileCell", for: indexPath) as! ProfileTableViewCell
profileCell.nameLabel.text = user.name
profileCell.totalReputationLabel.text = String(describing: user.reputation!)
profileCell.usernameLabel.text = user.username
return profileCell
} else {
let dogCell = tableView.dequeueReusableCell(withIdentifier: "dogCell", for: indexPath) as! DogTableViewCell
dogCell.dogBreedLabel.text = dogs[indexPath.row - 1].breed
dogCell.dogNameLabel.text = dogs[indexPath.row - 1].name
dogCell.dogScoreLabel.text = String(describing: dogs[indexPath.row - 1].score)
dogCell.dogImageView.image = dogs[indexPath.row - 1].picture
dogCell.dogCreatorButton.titleLabel?.text = dogs[indexPath.row - 1].creator
dogCell.dogVotesLabel.text = "0"
return dogCell
}
}

Resources