Unable to add dynamic data in expandable tableview iOS swift? - ios

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]

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

Swift Accessing Data Structure Inside Cell Setup

I am appending a Firebase Data snapshot to a NSObject of items being a "customer", "employee", and "business". Set up like this:
var customerData = [CustomerData]()
var employeeData = [EmployeeData]()
var businessData = [BusinessData]()
func getCustomerData() {
Database.database().reference().child("user_profiles").observe(.childAdded, with: { snapshot in
self.customerData.append(CustomerData(snapshot: snapshot))
})
}
func getEmployeeData() {
Database.database().reference().child("employees").observe(.childAdded, with: { snapshot in
self.employeeData.append(EmployeeData(snapshot: snapshot))
})
}
func getBusinessData() {
Database.database().reference().child("Businesses").observe(.childAdded, with: { snapshot in
self.businessData.append(BusinessData(snapshot: snapshot))
})
}
Data structure is the same for customer, employees, and business as below
import UIKit
import Firebase
class CustomerData: NSObject {
var customerName: String?
var customerPicture: String?
var customerUID: String?
init(snapshot: DataSnapshot) {
if let dictionary = snapshot.value as? [String: AnyObject] {
customerName = dictionary["name"] as? String
customerUID = dictionary["uid"] as? String
customerPicture = dictionary["profPicString"] as? String
}
}
}
I just want to access this snapshot data inside the cell to keep my message details up-to-date, like the profile picture and name. Below is my cell set up:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ChatMessageCell
cell.chatLogController = self
let customer = customerData
let employee = employeeData
let business = businessData
let message = messages[indexPath.row]
cell.message = message
cell.customer = customer
cell.employee = employee
cell.business = business
setupChatMessageCell(cell,message,customer,employee,business)
if let text = message.text {
cell.textView.text = text
cell.bubbleWidthAnchor?.constant = estimateSizeOfText(text).width + 32
cell.textView.isHidden = false
} else if message.imageUrl != nil {
cell.bubbleWidthAnchor?.constant = 200
cell.textView.isHidden = true
}
cell.playButton.isHidden = message.videoUrl == nil
return cell
}
private func setupChatMessageCell(_ cell: ChatMessageCell, _ message: GroupMessage, _ customer: CustomerData, _ employee: EmployeeData, _ business: BusinessData) {
if message.fromId == customer.customerUID {
//outgoing messages
cell.bubbleView.backgroundColor = ChatMessageCell.blueColor
cell.textView.textColor = .white
cell.bubbleLeftAnchor?.isActive = false
cell.bubbleRightAnchor?.isActive = true
cell.profileImageView.isHidden = true
cell.nameLabel.textColor = .gray
cell.nameRightAnchor?.isActive = true
cell.nameLeftAnchor?.isActive = false
cell.nameLabel.text = message.name?.description
//cell.nameLabel.text = message.customerName
} else if message.fromId == employee.employeeUID {
//incoming messagese
let customerImage = employee.employeePicture
cell.profileImageView.loadImageUsingCacheWithUrlString(customerImage!)
cell.profileImageView.isHidden = false
cell.bubbleView.backgroundColor = UIColor(red: 240, green: 240, blue: 240)
cell.textView.textColor = .black
cell.bubbleLeftAnchor?.isActive = true
cell.bubbleRightAnchor?.isActive = false
cell.profileImageView.isHidden = false
cell.nameRightAnchor?.isActive = false
cell.nameLeftAnchor?.isActive = true
cell.nameLabel.textColor = .black
cell.nameLabel.text = message.name?.description
} else if message.fromId == business.businessUID {
let customerImage = business.businessPicture
cell.profileImageView.loadImageUsingCacheWithUrlString(customerImage!)
cell.profileImageView.isHidden = false
cell.bubbleView.backgroundColor = UIColor(red: 240, green: 240, blue: 240)
cell.textView.textColor = .black
cell.bubbleLeftAnchor?.isActive = true
cell.bubbleRightAnchor?.isActive = false
cell.profileImageView.isHidden = false
cell.nameRightAnchor?.isActive = false
cell.nameLeftAnchor?.isActive = true
cell.nameLabel.textColor = .black
cell.nameLabel.text = message.name?.description
}
if let imageUrl = message.imageUrl {
cell.messageImageView.loadImageUsingCacheWithUrlString(imageUrl)
cell.messageImageView.isHidden = false
cell.bubbleView.backgroundColor = .clear
} else {
cell.messageImageView.isHidden = true
}
}
The problem is that I don't think accessing it as a "[index.path]" is the correct way just how I am doing with "messages". How can I access these data structures within the cell setup so I can keep my users information always up-to-date? I am getting errors like "Cannot assign value of type '[CustomerData]' to type 'CustomerData?'" so what is the proper way to access these data structures inside the cell?
The problem is that I don't think accessing it as a "[index.path]" is the correct way just how I am doing with "messages".
This is not true. Passing row or item property of IndexPath as index of element in data source array is correct way how to get certain element.
But, you're using UICollectionView, so you should use item property instead of row even if functionality is the same
UITableView
let item = dataSourceArray[indexPath.row]
UICollectionView
let item = dataSourceArray[indexPath.item]
But you should never pass certain cell as parameter for some other method where you're setting it.
Instead in your collection view cell subclass create method for setting cell's views etc.
class ChatMessageCell: UICollectionViewCell {
...
var message: Message!
...
func setupCell() {
... // here you can work with cell's properites e.g. message, ...
}
}
... and then call it in cellForItemAt
Inside this method you should change things connected with content. "Cosmetic" stuff like changing color of views, etc. you can set inside overridden UICollectionViewCell's method prepareForReuse()
override func prepareForReuse() {
super.prepareForReuse()
...
}

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

How to filter an array of JSON objects to be used in a table view?

I'm getting JSON data from an API and parsing that data in objects, which are then simply stored in an array of objects. The objects themselves contain data about articles from a newspaper. However, I need to filter that data. Some of the objects I'm getting from my JSON actually have no article content because they are pictures and not articles (i.e. some of the "nodes" from the API's JSON have content that I don't want to see in my table view).
In my JSON-parsing function, I've tried to make it so that the parsed object will only get added to the array of parsed objects if the character count of the "articleContent" variable is above 40. Here is what it looked like.
if issueElement.articleContent.characters.count > 40 {
self.currentIssueObjects.addObject(issueElement)
}
However, this simply does not work. I get the typical "unexpectedly found nil while unwrapping an Optional value" error message (I don't get a specific line for the error). How can I make this work ? I'm essentially trying to prevent the array from having objects with empty articleContent, because then that screws up my table view (empty cells, duplicates, etc...).
Here is my cellForRowAtIndexPath code, and my JSON-parsing code:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell {
let row = indexPath.row
guard let cell = tableView.dequeueReusableCellWithIdentifier(CurrentIssueArticlesTableCellIdentifier, forIndexPath: indexPath) as? CurrentIssueArticlesTableViewCell else {
print ("error: currentIssueTableView cell is not of class CurrentIssueArticlesTableViewCell, we will use EditorialsTableViewCell instead")
return tableView.dequeueReusableCellWithIdentifier(CurrentIssueArticlesTableCellIdentifier, forIndexPath: indexPath) as! EditorialsTableViewCell
}
let currentIssueObject = currentIssueObjects.objectAtIndex(indexPath.row) as! IssueElement
let title = currentIssueObject.title ?? ""
let timeStampDateObject = NSDate(timeIntervalSince1970: NSTimeInterval(currentIssueObject.timeStamp))
let timeStampDateString = dateFormatter.stringFromDate(timeStampDateObject) ?? "Date unknown"
if let author = currentIssueObject.author {
cell.currentIssueArticlesAuthorLabel!.font = UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
cell.currentIssueArticlesAuthorLabel!.text = author
}
let issueNumber = currentIssueObject.issueNumber ?? ""
let volumeNumber = currentIssueObject.volumeNumber ?? ""
let articleContent = currentIssueObject.articleContent ?? ""
let nodeID = currentIssueObject.nodeID ?? 0
cell.currentIssueArticlesHeadlineLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cell.currentIssueArticlesHeadlineLabel.text = title
cell.currentIssueArticlesPublishDateLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)
cell.currentIssueArticlesPublishDateLabel.text = timeStampDateString
if row == 0 {
cell.userInteractionEnabled = false
let imageURL = (currentIssueObjects.objectAtIndex(row) as! IssueElement).imageURL
cell.currentIssueArticlesHeadlineLabel.textColor = UIColor.clearColor()
cell.currentIssueArticlesAuthorLabel.textColor = UIColor.clearColor()
cell.currentIssueArticlesPublishDateLabel.textColor = UIColor.clearColor()
cell.request?.cancel()
if let image = self.imageCache.objectForKey(imageURL!) as? UIImage {
cell.currentIssueArticlesBackgroundImageView.image = image
} else {
cell.currentIssueArticlesBackgroundImageView.image = UIImage(named: "reveal Image")
cell.request = Alamofire.request(.GET, imageURL!).responseImage() { response in
if response.result.error == nil && response.result.value != nil {
self.imageCache.setObject(response.result.value!, forKey: response.request!.URLString)
cell.currentIssueArticlesBackgroundImageView.image = response.result.value
} else {
}
}
}
cell.currentIssueArticlesBackgroundImageView.hidden = false
}
else {
cell.currentIssueArticlesBackgroundImageView.hidden = true
}
return cell
}
JSON-parsing code:
func populateCurrentIssue() {
if populatingCurrentIssue {
return
}
populatingCurrentIssue = true
self.cellLoadingIndicator.backgroundColor = goldenWordsYellow
self.cellLoadingIndicator.startAnimating()
Alamofire.request(GWNetworking.Router.Issue).responseJSON() { response in
if let JSON = response.result.value {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
var nodeIDArray : [Int]
if (JSON .isKindOfClass(NSDictionary)) {
for node in JSON as! Dictionary<String, AnyObject> {
let nodeIDValue = node.0
var lastItem : Int = 0
self.nodeIDArray.addObject(nodeIDValue)
if let issueElement : IssueElement = IssueElement(title: "Could not retrieve title", nodeID: 0, timeStamp: 0, imageURL: "init", author: "Author not found", issueNumber: "Issue # error", volumeNumber: "Volume # error", articleContent: "Could not retrieve article content", coverImageInteger: "init", coverImage: UIImage()) {
issueElement.title = node.1["title"] as! String
issueElement.nodeID = Int(nodeIDValue)!
let timeStampString = node.1["revision_timestamp"] as! String
issueElement.timeStamp = Int(timeStampString)!
issueElement.imageURL = String(node.1["image_url"])
if let author = node.1["author"] as? String {
issueElement.author = author
}
if let issueNumber = node.1["issue_int"] as? String {
issueElement.issueNumber = issueNumber
}
if let volumeNumber = node.1["volume_int"] as? String {
issueElement.volumeNumber = volumeNumber
}
if let articleContent = node.1["html_content"] as? String {
issueElement.articleContent = articleContent
}
issueElement.coverImageInteger = String(node.1["cover_image"]) // addition specific to the Current Issue View Controller
lastItem = self.currentIssueObjects.count
print(issueElement.nodeID)
if issueElement.articleContent.characters.count > 40 {
self.currentIssueObjects.addObject(issueElement)
print(issueElement.nodeID)
}
// Sorting with decreasing timestamp from top to bottom.
let timestampSortDescriptor = NSSortDescriptor(key: "timeStamp", ascending: false)
self.currentIssueObjects.sortUsingDescriptors([timestampSortDescriptor])
// Placing the object with coverImage
let coverImageSortDescriptor = NSSortDescriptor(key: "coverImageInteger", ascending: false)
self.currentIssueObjects.sortUsingDescriptors([coverImageSortDescriptor])
let indexPaths = (lastItem..<self.currentIssueObjects.count).map {
NSIndexPath(forItem: $0, inSection: 0) }
}
}
}
dispatch_async(dispatch_get_main_queue()) {
self.currentIssueTableView.reloadData()
self.cellLoadingIndicator.stopAnimating()
self.cellLoadingIndicator.hidesWhenStopped = true
}
}
}
self.populatingCurrentIssue = false
}
}

Resources