Setting text labels to the difference in time intervals - ios

I'm trying to set a text label to the time difference between two objects; the current time and a saved time in Parse. My issue is with the if/else statement at the end. The labels are all assigned with either the datesHours or datesMinutes dictionaries whereas the datesHours dictionary should be assigned only when hours is greater than 1- otherwise, the text label should show the number of minutes.
What am I doing wrong here?
var datesHours = [Int: String]()
var datesMinutes = [Int: String]()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCell") as! TableViewCell
var query = PFQuery(className: "Items")
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects {
for (index, title) in enumerate(objects) {
let itemDate = (title as! PFObject)["date"] as! NSDate
var timeDifference = NSDate().timeIntervalSinceDate(itemDate)
var time = Int(timeDifference)
var minutes = ((time / 60) % 60)
var hours = (time / 3600)
self.datesHours[index] = String(hours)
self.datesMinutes[index] = String(minutes)
//println(self.datesMinutes)
//println(self.datesHours)
for (index, hour) in self.datesHours {
if hour == "\(0)" {
cell.timeLabel.text = self.datesMinutes[indexPath.row]! + "m"
} else {
cell.timeLabel.text = self.datesHours[indexPath.row]! + "hr"
}
}
}
}
}
}

If your data is correct, and you have a set of dictionaries that need to be displayed on your cell. Try this:
cell.textLabel.text = [[mArray objectAtIndex:indexPath.row] objectForKey:#"HOUR OR MINUTE KEY"];

Related

Multiple collectionviews in one Viewcontroller causes Index out of range error

I try to use three collectionviews in one Viewcontroller. I parse the data like the following method shows:
At the bottom i add the data depending on the position to the right list (this part works)
func getEventData(eventIDs: [String], plz: String, positiona: Int){
for eventId in eventIDs {
let ref = Database.database().reference().child("Events").child(plz).child(eventId)
ref.observe(.value, with: { snapshot in
let item = snapshot.value as? [String: AnyObject]
let eventName = item?["name"] as! String
let date = item?["date"] as! String
let lat = item?["lat"] as! String
let lng = item?["lng"] as! String
let infos = item?["additionalInfos"] as! String
let position = item?["position"] as! String
let ts = item?["ts"] as! Int
let createdBy = item?["createdBy"] as! String
let timestamp = NSDate().timeIntervalSince1970
if (ts > Int(timestamp)) {
let eo = EventObject(eventID: eventId, eventName:
eventName, info: infos, createdBy: createdBy, date: date, lat: lat, lng: lng, position: position, ts: ts)
if positiona == 0{
self.acceptedEvents.append(eo)
self.acceptedEventscv.reloadData()
}else if positiona == 1{
self.myEvents.append(eo)
self.myEventscv.reloadData()
}else if positiona == 2{
self.storedEvents.append(eo)
self.storedEventscv.reloadData()
}
}
})
}
}
In my NumbersofItemsInSection method i did the following which works as well:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView == self.acceptedEventscv{
return acceptedEvents.count
}else if collectionView == self.storedEventscv{
return storedEvents.count
}else if collectionView == self.myEventscv{
return myEvents.count
}else{
return 0
}
}
and in my CellForRowAtItem method i tried the following
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView == self.acceptedEventscv{
let cell =
collectionView.dequeueReusableCell(withReuseIdentifier:
"acceptedEventsCell", for: indexPath) as!
acceptedEventsCollectionViewCell
let eo = acceptedEvents[indexPath.row]
cell.eventName.text = eo.eventName
let items = eo.date!.components(separatedBy: " ")//Here replase
space with your value and result is Array.
//Direct line of code
//let items = "This is my String".components(separatedBy: " ")
let date = items[0]
let time = items[1]
cell.date.text = date
cell.time.text = time
getUserNameAge(label: cell.usernameAge)
return cell
}else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "savedEventsCell", for: indexPath) as! savedEventsCollectionViewCell
let eo = acceptedEvents[indexPath.row]
cell.eventName.text = eo.eventName
let items = eo.date!.components(separatedBy: " ")//Here replase space with your value and result is Array.
//Direct line of code
//let items = "This is my String".components(separatedBy: " ")
let date = items[0]
let time = items[1]
cell.date.text = date
cell.time.text = time
getUserNameAge(label: cell.usernameAge)
return cell
}
}
The problem is if i have more items in the seccond CollectionView, i always get this error:
Thread 1: Fatal error: Index out of range
at this line of code:
let eo = acceptedEvents[indexPath.row]
cellForItemAt also must be like
if collectionView == self.acceptedEventscv{
let item = acceptedEvents[indexPath.row]
----
return cell
}else if collectionView == self.storedEventscv{
let item = storedEvents[indexPath.row]
----
return cell
}else {
let item = myEvents[indexPath.row]
----
return cell
}
what happens now in your case is that you access the same array acceptedEvents in both cases where the returned count in numberOfItemsInSection may be different

add the values according to appending the values

How to add the values according to appending the values in swift3.
this is my datasource:-
class QM_ChartDataSourceModel: NSObject {
var dataListArray:Array<QM_CartModel>? = []
init(array :Array<[String:Any]>?) {
super.init()
var newArray:Array<[String:Any]> = []
if array == nil{
newArray = self.getJsonDataStored22()
}
else{
newArray = array!
}
var datalist:Array<QM_CartModel> = []
for dict in newArray{
let model = QM_CartModel(dictionary: dict)
datalist.append(model!)
}
self.dataListArray = datalist
}
}
typealias dummyDataSource22 = QM_ChartDataSourceModel
extension dummyDataSource22{
func getJsonDataStored22() ->Array<Dictionary<String,String>>{
let jsonArray = [["id":"311","name":"Dosa Fest","price":"QR 40","quantity":"3"],["id":"312","name":"Organic Vegan Fest","price":"QR 40","quantity":"2"],["id":"313","name":"Food Of Life Time","price":"QR 56","quantity":"7"],["id":"314","name":"Tea Time","price":"QR 88","quantity":"1"],["id":"315","name":"Dosa Fest","price":"QR 13","quantity":"6"],["id":"316","name":"Organic Vegan Fest","price":"QR 4","quantity":"8"],["id":"317","name":"Food Of Life Time","price":"QR 90","quantity":"3"],["id":"318","name":"Tea Time","price":"QR 66","quantity":"2"],["id":"319","name":"Dosa Fest","price":"QR 81","quantity":"6"],["id":"320","name":"Organic Vegan Fest","price":"QR 49","quantity":"2"]] as Array<Dictionary<String,String>>
return jsonArray
}
}
in tableviewcell:
func setEventData(carts:QM_CartModel)
{
self.name.text = carts.cartname
self.price.text = carts.cartsum
self.itemQuantityStepper.value = 1
setItemQuantity(quantity)
print(self.price.text)
let value = carts.cartsum
let x: Int? = Int(value!)
print(x)
let add = x!
print(add)
let theIntegerValue1 :Int = add
let theStringValue1 :String = String(theIntegerValue1)
self.price.text = theStringValue1
}
my model:-
class QM_CartModel: NSObject {
var cartname :String!
var cartprice:String!
var cartquantity:String!
var carttotalprice:String!
var carttotal:String!
var cartQR:String!
var cartvalue:String!
var cartsum:String?
var cartid:String!
init?(dictionary :JSONDictionary) {
guard let name = dictionary["name"] as? String else {
return
}
if let quantity = dictionary["quantity"] as? String{
let price = dictionary["price"] as? String
let id = dictionary["id"] as? String
self.cartid = id
self.cartprice = price
self.cartquantity = quantity
let fullNameArr = price?.components(separatedBy: " ")
let QR = fullNameArr?[0]
let value = fullNameArr?[1]
self.cartQR = QR
self.cartvalue = value
let x: Int? = Int(value!)
print(x)
let y:Int? = Int(quantity)
print(y)
let sum = x! * y!
print(sum)
let sum1 = String(describing: sum)
cartsum = sum1
}
my viewmodel:-
class QM_ChartViewModel: NSObject {
var datasourceModel:QM_ChartDataSourceModel
var insertedArray:QM_CartModel?
var filteredListArray:Array<QM_CartModel>? = []
var totalListArray:Array<QM_CartModel>? = []
init(withdatasource newDatasourceModel: QM_ChartDataSourceModel) {
datasourceModel = newDatasourceModel
print(datasourceModel.dataListArray)
}
func datafordisplay(atindex indexPath: IndexPath) -> QM_CartModel{
return datasourceModel.dataListArray![indexPath.row]
}
func numberOfRowsInSection(section:Int) -> Int {
return datasourceModel.dataListArray!.count
}
func delete(atIndex indexPath: IndexPath) {
datasourceModel.dataListArray!.remove(at: indexPath.row)
}
func search(idsearch :String?) {
filteredListArray = datasourceModel.dataListArray?.filter{($0.cartid?.range(of: idsearch!, options: .caseInsensitive) != nil)}
print(filteredListArray)
}
func searchindex(objectatindex index: Int) -> QM_CartModel {
return self.filteredListArray![index]
}
func total()
{ totalListArray = datasourceModel.dataListArray?.filter{($0.cartsum != nil)}
print(totalListArray)
}
func add(){
datasourceModel.dataListArray?.append(insertedArray!)
print(datasourceModel.dataListArray)
print(insertedArray?.offerAddName)
print(insertedArray?.offerprice)
self.datasourceModel.dataListArray = datasourceModel.dataListArray
print(insertedArray?.cartsum)
}
func addquantity(quantity:Int)
{
// datasourceModel.dataListArray?.filter{ $0.cartid! == cartaddedid! }.first?.qty = quantity as NSNumber?
}
}
in viewcontroller my code as below:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return chartViewModel.numberOfRowsInSection(section: section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "cell"
var cell: QM_ChartCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? QM_ChartCell
if cell == nil {
tableView.register(UINib(nibName: "QM_ChartCell", bundle: nil), forCellReuseIdentifier: identifier)
cell = tableView.dequeueReusableCell(withIdentifier: identifier) as? QM_ChartCell
}
cell.setEventData(carts: chartViewModel.datafordisplay(atindex: indexPath))
print(cell.numbers)
see here can see the total.So that way i need to display the total
this is my code ...here i getting the values model.cartsum.So this value is appending in var number:[String] = []
.But here i need-as values are appending ,here i need to add the number.So first value is 1 and second value is 2 then i need to add this values.And if next number is 3 then i need to get sum = 6.
How to get
You need to calculate all the cart stuff just after you get it either from service or from local. You don't need to do calculation in cellForRowAtIndex: method because it should be done once.
I assume this is your data structure as mentioned in question.
let jsonArray = [["id":"311","name":"Dosa Fest","price":"QR 40","quantity":"3"],
["id":"312","name":"Organic Vegan Fest","price":"QR 40","quantity":"2"],
["id":"313","name":"Food Of Life Time","price":"QR 56","quantity":"7"],
["id":"314","name":"Tea Time","price":"QR 88","quantity":"1"],
["id":"315","name":"Dosa Fest","price":"QR 13","quantity":"6"],
["id":"316","name":"Organic Vegan Fest","price":"QR 4","quantity":"8"],
["id":"317","name":"Food Of Life Time","price":"QR 90","quantity":"3"],
["id":"318","name":"Tea Time","price":"QR 66","quantity":"2"],
["id":"319","name":"Dosa Fest","price":"QR 81","quantity":"6"],
["id":"320","name":"Organic Vegan Fest","price":"QR 49","quantity":"2"]]
Here is how to calculate the total of the cart. As per your data considering price is in Int
var total = 0
for item in jsonArray {
/// Will check if quantity is available in dictionary and convertible into an Int else will be 0
let quantity = Int(item["quantity"] ?? "0") ?? 0
/// Will check price key exists in the dictionary
if let priceStr = item["price"] {
/// Will fetch the last components and check if it is convertible into an Int else 0
let price = Int(priceStr.components(separatedBy: " ").last ?? "0") ?? 0
/// Multiply price with quantity and add into total
total += price * quantity
}
}
print(total)
Output: 1776
Another way:
let total = jsonArray.map { (item) -> Int in
let quantity = Int(item["quantity"] ?? "0") ?? 0
let price = Int(item["price"]?.components(separatedBy: " ").last ?? "0") ?? 0
return quantity * price
}.reduce(0, +)
print(total)
Please try this code if you have array of Int
var arrayOfInt = [2,3,4,5,4,7,2]
let reducedNumberSum = arrayOfInt.reduce(0,+)
print(reducedNumberSum)
Answer: 27
In your code
let model = chartViewModel.datafordisplay(atindex: indexPath)
Please describe more so we can answer according your problem.

How to sort A - z in table view swift 2.0

i have one table view, which will dipslay the json data from one url.Its all working fine. What i need is?. in my table view i have one lable called "name label ".Which will display the name in my table view.
And i have one menu option with one button name called" sort data A - z ".When i click that, my table view data should reload with sorting the date from A-z alphabets order ( My name title ).
This is my button action :
#IBAction func sortByAZBtnPress(sender: AnyObject) {
}
My viewcontroller.swift
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var RightMenu: UIView!
#IBOutlet weak var tableView: UITableView! // UITable view declaration
#IBOutlet weak var Resultcount: UILabel! // count label
var arrDict :NSMutableArray=[] // array to store the value from json
let cellSpacingHeight: CGFloat = 5 // cell spacing from each cell in table view
override func viewDidLoad() {
super.viewDidLoad()
self.jsonParsingFromURL() // call the json method
let nib = UINib(nibName:"customCell", bundle: nil)
tableView.registerNib(nib, forCellReuseIdentifier: "cell")
RightMenu.layer.borderWidth = 1
}
// web services method
func jsonParsingFromURL () {
let url = NSURL(string: "http://sampleUrl.com”)
let session = NSURLSession.sharedSession()
let request = NSURLRequest(URL: url!)
let dataTask = session.dataTaskWithRequest(request) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
// print("done, error: \(error)")
if error == nil
{
dispatch_async(dispatch_get_main_queue()){
self.arrDict=(try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSMutableArray
//print(self.arrDict)
if (self.arrDict.count>0)
{
self.Resultcount.text = "\(self.arrDict.count) Results"
self.tableView.reloadData()
}}
// arrDict.addObject((dict.valueForKey("xxxx")
}
}
dataTask.resume()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return self.arrDict.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
// calling each cell based on tap and users ( premium / non premium )
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:customCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! customCell
cell.vendorName.text=arrDict[indexPath.section] .valueForKey("name") as? String
cell.vendorAddress.text=arrDict[indexPath.section] .valueForKey("address") as? String
return cell
}
// MARK:
// MARK: Sort Method
#IBAction func sortByRevBtnPress(sender: AnyObject) {
self.indicator.startAnimating()
self.indicator.hidden = false
RightMenu.hidden = true
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (Int64)(1 * NSEC_PER_SEC)), dispatch_get_main_queue()){
self.indicator.stopAnimating()
self.indicator.hidden = true
};
self.tableView.reloadData()
}
#IBAction func sortByAZBtnPress(sender: AnyObject) {
}
}
Please help me out. I am not getting clear idea to do that...Thanks
Updated :
(
{
ad = 0;
address = "900 York Mills Rd, Toronto, ON M3B 3H2, Canada";
"category_id" = 1;
latitude = "43.7563";
longitude = "-79.3495";
name = "Honeybee Restaurant";
phone = 9876543210;
rating = "5.0";
},
{
ad = 1;
address = "1677 Wilson Ave, Toronto, ON M3L 1A5, Canada";
"category_id" = 1;
latitude = "43.7194";
longitude = "-79.5153";
name = "Maki My Way";
phone = 9875463254;
rating = "4.0";
},
{
ad = 1;
address = "75 Bremner Blvd, Toronto, ON M5J 0A1, Canada";
"category_id" = 1;
latitude = "43.6429";
longitude = "-79.3814";
name = "Blow Fish Restaurant";
phone = 9873245610;
rating = "5.0";
},
{
ad = 0;
address = "4150 Yonge St, Toronto, ON M2P 2C6, Canada";
"category_id" = 1;
latitude = "43.747";
longitude = "-79.4079";
name = "SaigonFlower Restaurant";
phone = 7892345621;
rating = "3.0";
},
{
ad = 1;
address = "604 King St W, Toronto, ON M5V 1M6, Canada";
"category_id" = 1;
latitude = "43.6445";
longitude = "-79.4004";
name = "Sushi Gen";
phone = 7456321065;
rating = "2.0";
},
)
Assuming that your array is an array of dictionaries and your dictionary has a "Name" key you can do this. Change the code if your dict has different keys that contains the name.
#IBAction func sortByAZBtnPress(sender: AnyObject) {
arrDict.sortUsingComparator { (dict1, dict2) -> NSComparisonResult in
if let name1 = dict1["name"] as? String, name2 = dict2["name"] as? String {
return name1.compare(name2)
}
return .OrderedAscending
}
self.tableView.reloadData()
}
For order by rating (High to low) you can use this.
arrDict.sortUsingComparator { (dict1, dict2) -> NSComparisonResult in
if let name1 = dict1["rating"] as? String, name2 = dict2["rating"] as? String {
return name2.compare(name1)
}
return .OrderedAscending
}
For sorting of the tableview you should sort youre dataSource firstly and than reload tableview.
In you're case:
Firstly sort by A to z:
arrDict
For sorting use functional programming:
arrDict = arrDict.sort({ $0.name > $1.name })
Secondly reload tableview by:
self.tableView.reloadData()
sort arrDict using below code then self.tableView.reloadData():
sortedArray = [anArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];

TableView lagging too much and taking up too much memory Swift

I have an Application that uses a UITableView inside of a UIViewController, and the way each cell gets formed is, the information about the Issues, or Blog Posts, are stored in CoreData after they are downloaded. But when I ran the instruments tool, I found out that everytime you scroll down or up it fetches that CoreData. Even if the cell was already initialized, so this combined with apparently NSDate takes up a look of Memory as well. The program is taking up around 40m of ram and around 60% of CPU when you scroll down! And I have looked for answers of how to only initialize it once but could find it anywhere. Here it my TableView code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("tableviewcell", forIndexPath: indexPath) as! BlogTableViewCell
let sortedPosts = SortPosts()
let realresults = sortedPosts.sortData()
if realresults.count > 0 {
println(cell)
let date = realresults[indexPath.row].valueForKey("date") as? NSDate
let numberOfTime = PostDateFormatter().getFormattedDate(date!)
let content = realresults[indexPath.row].valueForKey("content") as! String
let title = realresults[indexPath.row].valueForKey("title") as! String
cell.configureCell(Post(title: title.html2String, author: realresults[indexPath.row].valueForKey("author") as! String, date: numberOfTime, content: content.html2String))
tableView.hidden = false
self.view.backgroundColor = UIColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 255.0/255.0)
myIndicator.stopAnimating()
myIndicator.hidden = true
}
else {
if PostServices().isConnectedToNetwork() {
println("Error 0 results returned......")
self.tableView.reloadData()
}
}
return cell
}
and here it SortPosts:
class SortPosts {
func sortData() -> NSArray {
var jsonin = NSArray()
let formatter = NSDateFormatter()
formatter.dateFormat = "Y-MM-dd\'T\'HH:mm:ss"
var managedObjectContext : NSManagedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
var request = NSFetchRequest(entityName: "Post")
request.returnsObjectsAsFaults = false
let sortDescriptor = NSSortDescriptor(key: "date", ascending: false)
request.sortDescriptors = [sortDescriptor]
var results : NSArray = managedObjectContext.executeFetchRequest(request, error: nil)!
return results
}
}
And here is PostDateFormatter:
class PostDateFormatter {
func getFormattedDate(date : NSDate) -> String {
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.ShortStyle
let dateInShortFormat = dateFormatter.stringFromDate(date)
let inter = date.timeIntervalSinceNow
var seconds = Int(abs(inter))
var minutes = seconds/60
var hours = minutes/60
var days = hours/24
var weeks = days/7
if seconds < 60 {
return "\(seconds)s"
} else if minutes < 60 {
return "\(minutes)m"
} else if hours < 24 {
return "\(hours)h"
} else if days < 7 {
return "\(days)d"
} else if weeks <= 8 {
return "\(weeks)w"
} else if weeks > 8 {
return "\(dateInShortFormat)"
}
return "Something went wrong with date formatting"
}
}
You do realize that for each cell you're calling;
let sortedPosts = SortPosts()
let realresults = sortedPosts.sortData()
it would make more sense to init all your data outside of your tableView, or certainly at some point other than cellForRowAtIndexPath.
Build an Array or Dictionary of items elsewhere, then simply reference the items in the array in your cellForRowAtIndexPath. This way, you're only accessing core data once.
What you're saying in your question describes exactly how TableViews work. Each cell is dequeued only when it will be visible on the screen. Otherwise, there are no other cells in memory, and when new cells are displayed, they have to be dequeued and thus require hitting the database and the CPU for information and drawing whenever they are displayed.
If you're up for the challenge I would suggest to use: asyncdisplaykit.
I believe it would help your memory issues.
It was originally built to make Facebook's Paper possible.
http://asyncdisplaykit.org/

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