Having issue in reuseable cell in UITableView cell - ios

I have two kinds of design in single UITableViewCell. Here is the design which I want and getting at the time of loading viewController
But after scrolling I'm getting following result.
I think this problem is caused by reusability of UITableViewCell. here is my code of cellForRowAtIndexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "notificationCell", for: indexPath) as! NotificationTableViewCell
cell.selectionStyle = .none
cell.btnClose.tag = indexPath.row
let noti_flag = (arrayNotificationList.object(at: indexPath.row) as! NSDictionary).object(forKey: "noti_flag") //as! String
let noti_flag_string = NSString(format: "%#", noti_flag as! CVarArg) as String
cell.lbl_From.text = (arrayNotificationList.object(at: indexPath.row) as! NSDictionary).object(forKey: "caller_id") as? String ?? ""
if noti_flag_string == "0" {
cell.lblTime.isHidden = true
cell.imgThumbIcon.isHidden = true
cell.lbl_remaining.isHidden = true
cell.lbl_mm_text.text = "Missed call"
cell.lbl_mm_text.font = cell.lbl_mm_text.font.withSize(23)
}
else{
var startAttributedText = (arrayNotificationList.object(at: indexPath.row) as! NSDictionary).object(forKey: "rebound_start_time") as! String
var endAttributedText = (arrayNotificationList.object(at: indexPath.row) as! NSDictionary).object(forKey: "rebound_end_time") as! String
startAttributedText = Model.shared.convertLocalTimeToServer(timeString: startAttributedText,isTimeFromServer: true)
endAttributedText = Model.shared.convertLocalTimeToServer(timeString: endAttributedText,isTimeFromServer: true)
let dateString:String = startAttributedText + " - " + endAttributedText
cell.lblTime.attributedText = convertStringToAttr(dateString: dateString)
cell.lbl_remaining.text = (arrayNotificationList.object(at: indexPath.row) as! NSDictionary).object(forKey: "remaining_time") as? String ?? ""
cell.lbl_mm_text.text = (arrayNotificationList.object(at: indexPath.row) as! NSDictionary).object(forKey: "mm_text") as? String ?? ""
//cell.lbl_From.text = (arrayNotificationList.object(at: indexPath.row) as! NSDictionary).object(forKey: "caller_id") as? String ?? ""
let mm_type = (arrayNotificationList.object(at: indexPath.row) as! NSDictionary).object(forKey: "mm_type") as! String//"img"
switch mm_type {
case "img":
cell.imgThumbIcon.image = UIImage(named: "thumb_camera")
case "vid":
cell.imgThumbIcon.image = UIImage(named: "thumb_video")
case "aud":
cell.imgThumbIcon.image = UIImage(named: "thumb_audio")
case "str":
cell.imgThumbIcon.image = UIImage(named: "thumb_sticker")
case "txt":
cell.imgThumbIcon.image = UIImage(named: "thumb_text")
case "brd":
cell.imgThumbIcon.image = UIImage(named: "thumb_brand")
default:
cell.imgThumbIcon.image = UIImage(named: "thumb_camera")
}
}
return cell
}
So please help me to solve this issue. Thanks in Advance.

The problem is that when you scrolled, the
cell.lblTime.isHidden = true
line hid the label in the cell. When it was reused, it was still hidden so the middle label enlarged to fill up the remaining space.
The solution is to either,
A. Create another cell subclass which cleans up the code considerably and it would no longer be necessary to show or hide labels.
B. Make sure to set
cell.lblTime.isHidden = false
in the else clause.
I hope this helps.

Related

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

get data from firebase children

I have two custom cells in one table view.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Configure the cell...
if (indexPath.row == 0) {
let cell = tableView.dequeueReusableCell(withIdentifier: "Main", for: indexPath) as! PostTableViewCell
//Configure the cell
cell.PostView.layer.cornerRadius = 5
cell.PostView.layer.masksToBounds = false
cell.PostView.layer.shadowColor = UIColor.black.withAlphaComponent(0.4).cgColor
cell.PostView.layer.shadowOffset = CGSize(width: 0, height: 0)
cell.PostView.layer.shadowOpacity = 0.9
let post = Comments[indexPath.row] as! [String: AnyObject]
let commentname = post["author"] as? String
sendAuthor = post["author"] as? String
cell.CommentersName.setTitle(commentname, for: .normal)
if let seconds = post["pub_time"] as? Double {
let timeStampDate = NSDate(timeIntervalSince1970: seconds/1000)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM d, yyyy"
let formating = timeStampDate as Date
cell.CommentTime.text = dateFormatter.string(from: formating)
}
cell.comment.text = post["content"] as? String
textViewDidChange(cell.comment)
cell.comment.frame.size.width = 344
cell.comment.sizeToFit()
cell.comment.clipsToBounds = true
cell.REply.frame.origin.y = cell.comment.frame.maxY + 10
cell.PostView.frame.size.height = cell.comment.frame.maxY + 50
TableView.rowHeight = cell.PostView.frame.size.height + 20
cell.LikesNumber.text = post["num_likes"] as? String
replyId = post["id"] as? String
cell.checkfornightmode()
return cell
}
else{
let cell = tableView.dequeueReusableCell(withIdentifier: "Reply", for: indexPath) as! RepliesTableViewCell
cell.ReplyCustomCell.layer.cornerRadius = 5
cell.ReplyCustomCell.layer.masksToBounds = false
cell.ReplyCustomCell.layer.shadowColor = UIColor.black.withAlphaComponent(0.4).cgColor
cell.ReplyCustomCell.layer.shadowOffset = CGSize(width: 0, height: 0)
cell.ReplyCustomCell.layer.shadowOpacity = 0.9
let post = Comments[indexPath.row] as! [String: AnyObject]
let posttest = post["id"] as? String
let replyRef = Database.database().reference().child("main").child("posts").child(postID!).child("comments").child(posttest!).child("comments")
replyRef.observeSingleEvent(of: .value, with: { (snapshot:DataSnapshot) in
if let postsDictionary = snapshot .value as? [String: AnyObject] {
for testingkey in postsDictionary.keys {
Database.database().reference().child("main").child("posts").child(self.postID!).child("comments").child(posttest!).child("comments").child(testingkey).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let content : String? = value?["content"] as? String ?? ""
cell.ReplyText.text = content!
})
}
}
})
TableView.rowHeight = 150.0
return cell
}
}
The first cell with the identifier main is supposed to print out all the intial comments for a certain post. The second cell with the identifier is supposed to print out the comments to main comments. Based on this code, I am only getting the last comment of the main post and the last comment to the main posts.
This is what the json looks like
First thing is to separate the firebase call into a separate function and in that function populate a dictionary with posts as key and comments as value
for example like this
var postComments: [String: [String]] = [:] // post as key and string array as comments
In firebase database call populate this with snapshot.
After the data call but within the firebase Database call back use this function to reload data
DispatchQueue.main.async{
tableView.reloadData()
}
Set the cell with postComments array.

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
}
}

how to make active only one radio button for all the sections?

here i had divided into sections in it each section there are rows depending on count so for every row the radio button will be there and i need to make active only one radio button for all of the sections but i am unable to implement it and the image for sections will be shown here and my code is
#IBAction func paymentRadioAction(_ sender: KGRadioButton) {
let chekIndex = self.checkIsPaymentRadioSelect.index(of: sender.tag)
if sender.isSelected {
} else{
if(chekIndex == nil){
self.checkIsPaymentRadioSelect.removeAll(keepingCapacity: false)
self.checkIsPaymentRadioSelect.append(sender.tag)
self.shippingTableView.reloadData()
}
}
}
here is the code for cell for row at indexpath
let cell = tableView.dequeueReusableCell(withIdentifier: "shippingCell", for: indexPath) as! shippingMethodTableViewCell
var key = self.keys[indexPath.section]
print(key)
var a :[Any] = arrayss[key] as! [Any]
var dictionary = a[indexPath.row] as! [String:Any]
let name = dictionary["name"]
let price = dictionary ["price"]
cell.methodNameLabel.text = name as! String
cell.priceLabel.text = price as! String
let checkIndex = self.checkIsPaymentRadioSelect.index(of: indexPath.row)
if(checkIndex != nil){
cell.radioButton.isSelected = true
}else{
cell.radioButton.isSelected = false
}
return cell
}
Here you don't have to use an array to keep track of the selected radio button
Make chekIndex as global variable of type IndexPath as var chekIndex:IndexPath?
and modify the code as below
#IBAction func paymentRadioAction(_ sender: KGRadioButton) {
let center = sender.center;
let centralPOint = sender.superview?.convert(sender.center, to:self.shippingTableView )
let indexPath = self.shippingTableView.indexPathForRow(at: centralPoint)
if sender.isSelected {
} else{
chekIndex = indexPath
self.shippingTableView.reloadData()
}
}
let cell = tableView.dequeueReusableCell(withIdentifier: "shippingCell", for: indexPath) as! shippingMethodTableViewCell
var key = self.keys[indexPath.section]
print(key)
var a :[Any] = arrayss[key] as! [Any]
var dictionary = a[indexPath.row] as! [String:Any]
let name = dictionary["name"]
let price = dictionary ["price"]
cell.methodNameLabel.text = name as! String
cell.priceLabel.text = price as! String
if checkIndex == indexPath {
cell.radioButton.isSelected = true
} else {
cell.radioButton.isSelected = false
}
return cell
}

UITableViewCell content disappears after scrolling

I have a UITableView which gets its data from Parse. It is basically like a feed that has message posts, UIMapView and UIImages. Some posts have only text however, so I've made up 4 different cell types. For all the different combinations of posts.
Text Alone
Image with Text
Image and Map with Text
Map with Text
After that, in my cellForRowAtIndexPath function, I retrieve all the messages from Parse.
This is my function:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
let cell:MessageTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as? MessageTableViewCell
let cell2:FullTableViewCell! = tableView.dequeueReusableCellWithIdentifier("AllCell", forIndexPath: indexPath) as? FullTableViewCell
let cell3:ImageTableViewCell! = tableView.dequeueReusableCellWithIdentifier("ImageCell", forIndexPath: indexPath) as? ImageTableViewCell
let cell4:MapTableViewCell! = tableView.dequeueReusableCellWithIdentifier("MapCell", forIndexPath: indexPath) as? MapTableViewCell
var typeOfCell = ""
if(self.feedData.count > indexPath.row){
if let message:PFObject = (self.feedData[indexPath.row] as? PFObject)!{
long = message.objectForKey("LocationLongitude") as! Double
lat = message.objectForKey("LocationLatitude") as! Double
if let userImageFile = message["Photos"]{
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let imageData = imageData
let image = UIImage(data:imageData!)
if(self.long != 0 || self.lat != 0){
//ALL INCLUDED
cell2.imgView.image = image
cell2.txtMessage.text = message.objectForKey("Message") as? String
cell2.txtMessage.font = UIFont(name: "Baskerville", size: 18)
let dataFormatter:NSDateFormatter = NSDateFormatter()
dataFormatter.dateFormat = "H:mm - MM-dd-yyyy"
cell2.lblDate.text = dataFormatter.stringFromDate(message.createdAt!)
typeOfCell = "FullCell"
}else{
//ONLY IMAGE
cell3.imgView.image = image
cell3.txtMessage.text = message.objectForKey("Message") as? String
cell3.txtMessage.font = UIFont(name: "Baskerville", size: 18)
let dataFormatter:NSDateFormatter = NSDateFormatter()
dataFormatter.dateFormat = "H:mm - MM-dd-yyyy"
cell3.lblDate.text = dataFormatter.stringFromDate(message.createdAt!)
//typeOfCell = "ImageCell"
}
}
}
}else{
if(self.long != 0 || self.lat != 0){
//MAPVIEW
cell4.txtMessage.text = message.objectForKey("Message") as? String
cell4.txtMessage.font = UIFont(name: "Baskerville", size: 18)
let dataFormatter:NSDateFormatter = NSDateFormatter()
dataFormatter.dateFormat = "H:mm - MM-dd-yyyy"
cell4.lblDate.text = dataFormatter.stringFromDate(message.createdAt!)
typeOfCell = "MapCell"
}else{
//ONLY TEXT
cell.txtMessage.text = message.objectForKey("Message") as? String
cell.txtMessage.font = UIFont(name: "Baskerville", size: 18)
let dataFormatter:NSDateFormatter = NSDateFormatter()
dataFormatter.dateFormat = "H:mm - MM-dd-yyyy"
cell.lblDate.text = dataFormatter.stringFromDate(message.createdAt!)
typeOfCell = "Cell"
}
}
}
}
if(typeOfCell == "FullCell"){
self.tableView.rowHeight = 861.00
print("FullCell")
return cell2
}else if(typeOfCell == "Cell"){
print("Cell")
self.tableView.rowHeight = 133.00
return cell
}else if(typeOfCell == "MapCell"){
print("MapCell")
self.tableView.rowHeight = 269.00
return cell4
}else{
print("ImageCell")
self.tableView.rowHeight = 725.00
return cell3
}
}
So far so good. When I run my app however, Only the first cell displays an image (if it has one) and the rest do not. Also, when I try to scroll in my TableView, all my images disappear. So basically I can only view two types of cells. Either type 1 or type 4 in the previously mentioned cell types. I've been stuck on this issue for hours and I can't seem to figure it out. Any help is greatly appreciated.
While using tableViews You should use Else conditions for contents of cell as it reuses cell so when you scroll up down it reuses cell. That causes the problem.

Resources