UITableViewCell content disappears after scrolling - ios

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.

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

Unable to add dynamic data in expandable tableview iOS swift?

I'm new to iOS development I'm trying to implement expandable table with dynamic data which comes from server. I'm using https://github.com/amratab/ThreeLevelAccordian this expandable table view.
In this library they added statically like below code.
cells.append(TLAHeaderItem(value: "Bathroom" as AnyObject, imageURL: "bathroom_grey_32.png"))
cells.append(TLACell(value: "Shower" as AnyObject))
cells.append(TLASubItem(value: "Shower pores should be cleaned effectively by brushing." as AnyObject))
cells.append(TLACell(value: "Tap" as AnyObject))
cells.append(TLASubItem(value: "Taps must be washed with soap and all the salt removed." as AnyObject))
cells.append(TLACell(value: "Toilet" as AnyObject, imageURL: "toilet_grey_32.png"))
cells.append(TLASubItem(value: "Should be made stains and germs free." as AnyObject))
cells.append(TLAHeaderItem(value: "Bedroom" as AnyObject, imageURL: "bedroom_grey_32.png"))
cells.append(TLACell(value: "Bed" as AnyObject))
cells.append(TLASubItem(value: "Remove all the dust." as AnyObject))
cells.append(TLACell(value: "Dressing" as AnyObject))
cells.append(TLAHeaderItem(value: "Kitchen" as AnyObject, imageURL: "kitchen_grey_32.png"))
cells.append(TLACell(value: "Utensils" as AnyObject))
cells.append(TLASubItem(value: "There are many type of utensils like tongs, rolling pin, pan, non stick pans. Wash them all." as AnyObject))
cells.append(TLACell(value: "Sink" as AnyObject))
cells.append(TLASubItem(value: "Clean the sink" as AnyObject))
in cellforrowindexpath they are using like this.
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = self.cells[(indexPath as NSIndexPath).row]
let value = item.value as? String
if let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) {
cell.textLabel?.text = value
let label = cell.textLabel!
cell.imageView?.image = nil
if let headerImage = item.imageURL, let image = UIImage(named: headerImage) {
cell.imageView?.image = image
}
if let accessoryView = accessory(for: indexPath, and: .expand) {
cell.accessoryView = accessoryView
} else {
cell.accessoryType = UITableViewCellAccessoryType.none
cell.accessoryView = nil
}
if let _ = item as? TLAHeaderItem {
if let headerFont = headerCellFont {
cell.textLabel?.font = headerFont
}
if let headerCellBackgroundColor = self.headerCellBackgrondColor {
cell.backgroundColor = headerCellBackgroundColor
}
if let headerCellTextColor = self.headerCellTextColor {
cell.textLabel?.textColor = headerCellTextColor
}
} else if (item as? TLASubItem != nil) {
if isMultiline {
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.numberOfLines = 0
label.sizeToFit()
}
cell.accessoryView = nil
cell.accessoryType = UITableViewCellAccessoryType.none
if let subItemCellBackgrondColor = self.subItemCellBackgrondColor {
cell.backgroundColor = subItemCellBackgrondColor
}
if let subItemCellTextColor = self.subItemCellTextColor {
cell.textLabel?.textColor = subItemCellTextColor
}
if let subItemCellFont = self.subItemCellFont {
cell.textLabel?.font = subItemCellFont
}
} else {
if let itemCellBackgrondColor = self.itemCellBackgrondColor {
cell.backgroundColor = itemCellBackgrondColor
}
if let itemCellTextColor = self.itemCellTextColor {
cell.textLabel?.textColor = itemCellTextColor
}
if let itemCellFont = self.itemCellFont {
cell.textLabel?.font = itemCellFont
}
}
return cell
}
return UITableViewCell()
}
instead of adding statically i want to add dynamically array of data. to the cell.
Example:
cell.textLabel?.text = self.tableViewData[indexPath.row]

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.

Having issue in reuseable cell in UITableView cell

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.

Refactor cellForRowIndexPath in UITableView Swift

I have a rather long cellForRowAtIndexPath function. I am using parse as my backend and have a lot going on. I want to extract a lot of these conditions and put them in their own functions. Especially the PFUser query, but unfortunately I don't know whats the best way to go about it since I don't know how I can access the elements of each cell in those functions I want to write.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("PostCells", forIndexPath: indexPath) as! NewsFeedTableCellTableViewCell
// Configure the cell...
// A drive is a post
let drive: PFObject = self.timelineData[indexPath.row] as PFObject
var driverId = drive.objectForKey("driver")!.objectId!
var currentUserObjectId = PFUser.currentUser()!.objectId
if(driverId != currentUserObjectId){
cell.requestButton.layer.borderWidth = 1
cell.requestButton.titleLabel!.font = UIFont.systemFontOfSize(11)
cell.requestButton.tintColor = UIColor.orangeColor()
cell.requestButton.layer.borderColor = UIColor.orangeColor().CGColor
cell.requestButton.setTitle("REQUEST", forState: UIControlState.Normal)
}
else {
cell.requestButton.layer.borderWidth = 1
cell.requestButton.titleLabel!.font = UIFont.systemFontOfSize(11)
cell.requestButton.tintColor = UIColor.grayColor()
cell.requestButton.layer.borderColor = UIColor.lightGrayColor().CGColor
cell.requestButton.setTitle("REQUEST", forState: UIControlState.Normal)
cell.requestButton.enabled = false
}
// Setting up the attributes of the cell for the news feed
cell.driveTitleTextField.text = drive.objectForKey("title") as! String
cell.wayTextField.text = drive.objectForKey("way") as! String
var departureDate = NSDate()
departureDate = drive.objectForKey("departureDate") as! NSDate
var dateFormat = NSDateFormatter()
dateFormat.dateFormat = "M/dd hh:mm a"
cell.departureDateTextField.text = dateFormat.stringFromDate(departureDate)
if((drive.objectForKey("way")!.isEqualToString("Two Way")))
{
var returnDate = NSDate()
returnDate = drive.objectForKey("returnDate") as! NSDate
cell.returningDateTextField.text = dateFormat.stringFromDate(returnDate)
}
else if((drive.objectForKey("way")!.isEqualToString("One Way")))
{
cell.returningDateTextField.enabled = false
cell.returningDateTextField.userInteractionEnabled = false
cell.returningDateTextField.hidden = true
cell.returningLabel.hidden = true
}
var seatNumber = NSNumber()
seatNumber = drive.objectForKey("seatNumber") as! NSInteger
var numberFormat = NSNumberFormatter()
numberFormat.stringFromNumber(seatNumber)
cell.seatNumberTextField.text = numberFormat.stringFromNumber(seatNumber)
// this is a PFUser query so we can get the users image and name and email from the User class
var findDrive = PFUser.query()
var objectId: AnyObject? = drive.objectForKey("driver")!.objectId!
findDrive?.whereKey("objectId", equalTo: objectId!)
findDrive?.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]?, error:NSError?)->Void in
if (error == nil){
if let actualObjects = objects {
let possibleUser = (actualObjects as NSArray).lastObject as? PFUser
if let user = possibleUser {
cell.userProfileNameLabel.text = user["fullName"] as? String
cell.userEmailLabel.text = user["username"] as? String
//Profile Image
cell.profileImage.alpha = 0
if let profileImage = user["profilePicture"] as? PFFile {
profileImage.getDataInBackgroundWithBlock{
(imageData:NSData? , error:NSError?)-> Void in
if(error == nil) {
if imageData != nil{
let image:UIImage = UIImage (data: imageData!)!
cell.profileImage.image = image
}
}
}
}
UIView.animateWithDuration(0.5, animations: {
cell.driveTitleTextField.alpha = 1
cell.wayTextField.alpha = 1
cell.profileImage.alpha = 1
cell.userProfileNameLabel.alpha = 1
cell.userEmailLabel.alpha = 1
cell.seatNumberTextField.alpha = 1
cell.returningDateTextField.alpha = 1
cell.departureDateTextField.alpha = 1
})
}
}
}
}
return cell
}
EDIT 1
I came up with a way to refactor my code that I would like critiqued!
1. I extracted a lot of the cells configurations and put them into to functions, one for the button on the cell and the other for all the data from parse.
func configureDataTableViewCell(cell:NewsFeedTableCellTableViewCell, drive: PFObject)
{
cell.driveTitleTextField.text = drive.objectForKey("title") as! String
cell.wayTextField.text = drive.objectForKey("way") as! String
cell.userEmailLabel.text = drive.objectForKey("username") as? String
cell.userProfileNameLabel.text = drive.objectForKey("name") as? String
var departureDate = NSDate()
departureDate = drive.objectForKey("departureDate") as! NSDate
var dateFormat = NSDateFormatter()
dateFormat.dateFormat = "M/dd hh:mm a"
cell.departureDateTextField.text = dateFormat.stringFromDate(departureDate)
if((drive.objectForKey("way")!.isEqualToString("Two Way")))
{
var returnDate = NSDate()
returnDate = drive.objectForKey("returnDate") as! NSDate
cell.returningDateTextField.text = dateFormat.stringFromDate(returnDate)
}
else if((drive.objectForKey("way")!.isEqualToString("One Way")))
{
cell.returningDateTextField.enabled = false
cell.returningDateTextField.userInteractionEnabled = false
cell.returningDateTextField.hidden = true
cell.returningLabel.hidden = true
}
var seatNumber = NSNumber()
seatNumber = drive.objectForKey("seatNumber") as! NSInteger
var numberFormat = NSNumberFormatter()
numberFormat.stringFromNumber(seatNumber)
cell.seatNumberTextField.text = numberFormat.stringFromNumber(seatNumber)
}
func configureButtonTableViewCell(cell:NewsFeedTableCellTableViewCell, userID: String)
{
var currentUserObjectId = PFUser.currentUser()!.objectId
if(userID != currentUserObjectId){
cell.requestButton.layer.borderWidth = 1
cell.requestButton.titleLabel!.font = UIFont.systemFontOfSize(11)
cell.requestButton.tintColor = UIColor.orangeColor()
cell.requestButton.layer.borderColor = UIColor.orangeColor().CGColor
cell.requestButton.setTitle("REQUEST", forState: UIControlState.Normal)
println("orange")
}
else {
cell.requestButton.layer.borderWidth = 1
cell.requestButton.titleLabel!.font = UIFont.systemFontOfSize(11)
cell.requestButton.tintColor = UIColor.grayColor()
cell.requestButton.layer.borderColor = UIColor.lightGrayColor().CGColor
cell.requestButton.setTitle("REQUEST", forState: UIControlState.Normal)
cell.requestButton.enabled = false
println("gray")
}
}
2. I then passed in the functions from step 1 and into my cellForRowIndexPath
// A drive is a post
let drive: PFObject = self.timelineData[indexPath.row] as PFObject
var driverId : String = drive.objectForKey("driver")!.objectId!!
configureButtonTableViewCell(cell, userID: driverId)
configureDataTableViewCell(cell, drive: drive)
3. I stored all my PFUser data into my object when its saved instead of querying the user class. So I get the PFUser.currentUser() username, full name, and profile picture when they save a post.
My load data has been modified. I store all the profile pictures in there own array.
func loadData(){
var findItemData:PFQuery = PFQuery(className:"Posts")
findItemData.addDescendingOrder("createdAt")
findItemData.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]? , error:NSError?) -> Void in
if error == nil
{
self.timelineData.removeAll(keepCapacity: false)
self.profilePictures.removeAll(keepCapacity: false)
self.timelineData = objects as! [PFObject]
for object in objects! {
self.profilePictures.append(object.objectForKey("profilePicture") as! PFFile)
}
self.newsFeedTableView.reloadData()
}
}
}
And finally, here is my updated cellForRowIndexPath
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("PostCells", forIndexPath: indexPath) as! NewsFeedTableCellTableViewCell
// Configure the cell...
// A drive is a post
let drive: PFObject = self.timelineData[indexPath.row] as PFObject
var driverId : String = drive.objectForKey("driver")!.objectId!!
configureButtonTableViewCell(cell, userID: driverId)
configureDataTableViewCell(cell, drive: drive)
println(PFUser.currentUser()?.objectForKey("username"))
if let profileImage = drive["profilePicture"] as? PFFile {
profileImage.getDataInBackgroundWithBlock{
(imageData:NSData? , error:NSError?)-> Void in
if(error == nil) {
if imageData != nil{
let image:UIImage = UIImage (data: imageData!)!
cell.profileImage.image = image
}
}
}
}
return cell
}
Let me know what you guys think, I want to do make my code much more readable, fast, and memory efficient.
You shouldn't be doing any heavy model stuff inside cellForRow.
What you're currently trying to do will greatly slow down your UI.
In most cases you will want your model objects setup, and ready to go before you even get to cellForRow.
This means performing your Parse queries somewhere like in viewDidLoad, keep those results in an array, and when it comes time to do so, apply them to your cells in cellForRow. This way, when a user scrolls, a new query won't be dispatched for every new cell that comes into view. It will already be available.
In addition to this, should you need to make any changes to these items once they have been fetched, you can do so, and have them remain unchanged even when the user is scrolling.
Refactor so you have some data type or group of instance variables to serve as a view model. Avoid making asynchronous calls that mutate the cell in cellForRowAtIndexPath. Instead have your data access method mutate or recreate the view model and at the end of your callback, dispatch_async to the main queue. Give it a closure that tells your table view to reloadData and whatever else you need to do for views to show the new data.
Here's a little pseudocode to describe what I mean:
func loadData() {
parseQueryWithCallback() { data in
self.viewModel = doWhateverTransformsAreNeeded(data)
dispatch_async(dispatch_get_main_queue(), self.tableView.reloadData)
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) {
let cell = dequeue(...)
cell.thingOne = self.viewModel.things[indexPath.row].thingOne
cell.thingTwo = self.viewModel.things[indexPath.row].thingTwo
return cell
}

Resources