How do I change data model? - swift

I'm making an social media apps.
user
- displayname
- username
- profileImg
- password
- email
comments
- username
- comment
- to
friends
- follower
- following
hashtags
- hashtag
- to
- by
- comment
likes
- to
- by
posts
- postImg
- username
- title
- uuid
My question is when USERS post the image with title text then
I want retrieve username, profileImg, title, comment, commentby, postImg, count of likes
My approach is redesign the posts db
posts
- postImg
- username
- title
- uuid
- comment
- commentby
- profileImg
- count of likes
But I think it is poor design of db.
func loadPosts() {
//STEP 1. Find posts related to people who we are following
let followQuery = PFQuery(className: “friends")
followQuery.whereKey(“following", equalTo: PFUser.current()!.username!)
followQuery.findObjectsInBackground (block: { (objects:[PFObject]?, error:Error?) -> Void in
if error == nil {
//clean up
self.followArray.removeAll(keepingCapacity: false)
//Appending where people following..
//find related objects
for object in objects! {
self.followArray.append(object.object(forKey: “following") as! String)
}
//append current user to see own posts in feed
self.followArray.append(PFUser.current()!.username!)
//STEP 2. Find posts made by people appended to followArray
let query = PFQuery(className: "posts")
query.whereKey("username", containedIn: self.followArray)
query.limit = self.page
query.addDescendingOrder("createdAt")
query.findObjectsInBackground(block: { (objects:[PFObject]?, error:Error?) -> Void in
if error == nil {
//clean up
self.usernameArray.removeAll(keepingCapacity: false)
// self.profileArray.removeAll(keepCapacity: false)
self.dateArray.removeAll(keepingCapacity: false)
self.postArray.removeAll(keepingCapacity: false)
self.descriptionArray.removeAll(keepingCapacity: false)
self.uuidArray.removeAll(keepingCapacity: false)
self.commentsArray.removeAll(keepingCapacity: false)
self.commentsByArray.removeAll(keepingCapacity: false)
//find related objects
for object in objects! {
self.usernameArray.append(object.object(forKey: "username") as! String)
// self.profileArray.append(object.objectForKey("profileImg") as! PFFile)
self.dateArray.append(object.createdAt)
self.postArray.append(object.object(forKey: "postImg") as! PFFile)
self.descriptionArray.append(object.object(forKey: "title") as! String)
self.uuidArray.append(object.object(forKey: "uuid") as! String)
//set Comments
let comment = object.object(forKey: "comment") as! String
let by = object.object(forKey: "commentby") as! String
let commentString = " " + comment
self.commentsByArray.append(by)
self.commentsArray.append(commentString)
}
//reload tableView & end spinning of refresher
self.tableView.reloadData()
self.refresher.endRefreshing()
} else {
print(error!.localizedDescription)
}
})
} else {
print(error!.localizedDescription)
}
})
}
defined cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//define cell
let cell = tableView.dequeueReusableCell(withIdentifier: "ShopDetailCell", for: indexPath) as! ShopDetailCell
cell.userNameLabel.text = usernameArray[(indexPath as NSIndexPath).row - 1]
cell.userNameLabel.sizeToFit()
cell.uuidLabel.text = uuidArray[(indexPath as NSIndexPath).row - 1]
cell.descriptionLabel.text = descriptionArray[indexPath.row - 1]
cell.descriptionLabel.sizeToFit()
cell.commentLabel.sizeToFit()
//Load ProfileImage
let profileImgQuery = PFQuery(className: "_User")
profileImgQuery.whereKey("username", equalTo: usernameArray[(indexPath as NSIndexPath).row - 1])
profileImgQuery.findObjectsInBackground(block: {(objects:[PFObject]?, error:Error?) -> Void in
if error == nil {
//shown wrong user
if objects!.isEmpty {
print("Wrong User")
}
//find related to user information
for object in objects! {
//Set Image
let profilePictureObject = object.object(forKey: "profileImg") as? PFFile
profilePictureObject?.getDataInBackground { (imageData:Data?, error:Error?) -> Void in
if(imageData != nil)
{
let profileURL : URL = URL(string: profilePictureObject!.url!)!
cell.userImg.sd_setImage(with: profileURL, placeholderImage: UIImage(named: "holderImg"))
}
}
}
} else {
print(error?.localizedDescription)
}
})
//Clip to circle
cell.userImg.layoutIfNeeded()
cell.userImg.layer.cornerRadius = cell.userImg.frame.size.width/2
cell.userImg.clipsToBounds = true
// place post picture using the sdwebimage
let postURL : URL = URL(string: postArray[(indexPath as NSIndexPath).row - 1].url!)!
cell.postImg.sd_setImage(with: postURL, placeholderImage: UIImage(named: "holderImg"))
//Calculate post date
let from = dateArray[(indexPath as NSIndexPath).row - 1]
let now = Date()
let components : NSCalendar.Unit = [.second, .minute, .hour, .day, .weekOfMonth]
let difference = (Calendar.current as NSCalendar).components(components, from: from!, to: now, options: [])
// logic what to show : Seconds, minutes, hours, days, or weeks
if difference.second! <= 0 {
cell.dateLabel.text = "NOW"
}
if difference.second! > 0 && difference.minute! == 0 {
cell.dateLabel.text = "\(difference.second!) SEC AGO"
}
if difference.minute! > 0 && difference.hour! == 0 {
cell.dateLabel.text = "\(difference.minute!) MIN AGO"
}
if difference.hour! > 0 && difference.day! == 0 {
cell.dateLabel.text = "\(difference.hour!) HR AGO"
}
if difference.day! > 0 && difference.weekOfMonth! == 0 {
cell.dateLabel.text = "\(difference.day!) DAY AGO"
}
if difference.weekOfMonth! > 0 {
cell.dateLabel.text = "\(difference.weekOfMonth!) WEEK AGO"
}
cell.dateLabel.sizeToFit()
//Set Text Label
if cell.descriptionLabel.text!.isEmpty == true || cell.descriptionLabel.text == " "{
if cell.commentLabel.text!.isEmpty == true || cell.commentLabel.text == " "{
cell.dateTop.constant = 7
}else {
cell.dateTop.constant = cell.commentTop.constant + cell.commentLabel.frame.height + 8
}
}else {
if cell.commentLabel.text!.isEmpty == true || cell.commentLabel.text == " "{
cell.dateTop.constant = cell.descriptionTop.constant + cell.descriptionLabel.frame.height + 8
}else {
cell.commentTop.constant = cell.descriptionTop.constant + cell.descriptionLabel.frame.height + 8
cell.dateTop.constant = cell.commentTop.constant + cell.commentLabel.frame.height + 8
}
}
// manipulate like button depending on did user like it or not
let didLike = PFQuery(className: "likes")
didLike.whereKey("by", equalTo: PFUser.current()!.username!)
didLike.whereKey("to", equalTo: cell.uuidLabel.text!)
didLike.countObjectsInBackground(block: {(count:Int32, error:Error?) -> Void in
//if no any likes are found, else found likes
if count==0 {
cell.likeBtn.setTitle("unlike", for: UIControlState())
cell.likeBtn.setImage(UIImage(named:"heartBtn"), for: UIControlState())
}else{
cell.likeBtn.setTitle("like", for: UIControlState())
cell.likeBtn.setImage(UIImage(named: "heartTapBtn"), for: UIControlState())
}
})
//count total likes of shown post
let countLikes = PFQuery(className: "likes")
countLikes.whereKey("to", equalTo: cell.uuidLabel.text!)
countLikes.countObjectsInBackground(block: {(count:Int32, error:Error?) -> Void in
cell.likesLabel.text="\(count) likes"
})
cell.userNameLabel.layer.setValue(indexPath, forKey: "index")
cell.commentBtn.layer.setValue(indexPath, forKey: "index")
cell.moreBtn.layer.setValue(indexPath, forKey: "index")
return cell
}
Could you anyone advising me?
I had read this tutorial "https://parse.com/tutorials/anypic" but I can't decided which data model is better for me
I wish to uses join or pointer method.

You can save the currentUser as a pointer in your Posts class whenever a user makes a post.
note: I will demonstrate in Objective-C but it's very easy for you to translate into Swift. But if you have trouble reading objc code, I will edit my answer to Swift version.
func post { //make a post
var post = PFObject(className:"Posts")
post["user"] = PFUser.current()//save the current user as a pointer pointing to the User class. You can add a column of type pointer in your parse dashboard inside your Posts class.
//set up other attributes here...
post.saveInBackground()
}
Then when we do the query, we can use includeKey to include the user pointer.
let query = PFQuery(className: "posts")
query.whereKey("username", containedIn: self.followArray)
query.includeKey("user")// THIS IS IMPORTANT
query.limit = self.page
query.addDescendingOrder("createdAt")
query.findObjectsInBackground(block: { (objects:[PFObject]?, error:Error?) -> Void in
if !error {
//we can access the user pointer by doing:
for object in objects {
var user = object["user"]
var username = user.username
var profileImage = user["profileImg"] //a PFFile
//...
}
}
Besides, you can always use a PFQueryTableViewController to load the objects for you, so you don't need to store the query results manually.

Related

Reload TableView only at new rows once user scrolls to bottom

I am trying to append new data to Tableview and only reload the Tableview for the new data added.
My app has been crashing giving me this error: "attempt to delete row 19 from section 0 which only contains 10 rows before the update'"
I would like to reload those rows once the asynchronous function contentQueryContinuous() is completed.
Here is my code:
//Once User Scrolls all the way to bottom, beginContentBatchFetch() is called
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offsetY = scrollView.contentSize.height
let contentHeight = scrollView.contentSize.height
if offsetY > contentHeight - scrollView.frame.height {
beginContentBatchFetch()
}
}
//Content Batch Fetch looks up to 10 new elements, tries to append to current array's, and then reload's tableview only for those new elements
func beginContentBatchFetch() {
contentFetchMore = true
ProgressHUD.show()
let oldcount = contentObjectIdArray.count
var IndexPathsToReload = [IndexPath]()
var startIndex = Int()
var endIndex = Int()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
//Calls self.contentQueryContinous which adds new elements
self.contentQueryContinous()
let newElements = self.contentObjectIdArray.count - oldcount
startIndex = oldcount
endIndex = self.contentObjectIdArray.count
for index in startIndex..<endIndex {
let indexPath = IndexPath(row: index, section: 0)
IndexPathsToReload.append(indexPath)
}
if newElements > 0 {
self.MyTableView.reloadRows(at: IndexPathsToReload, with: .fade)
}
ProgressHUD.dismiss()
}
}
Here is contentQueryContinous()
func contentQueryContinous() {
if contentObjectIdArray.count != 0 {
contentSkip = contentObjectIdArray.count
let query = PFQuery(className: "ContentPost")
query.whereKey("Spot", equalTo: SpotText)
query.limit = 10
query.skip = contentSkip
query.addDescendingOrder("createdAt")
query.findObjectsInBackground(block: { (objects: [PFObject]?,error: Error?) in
if let objects = objects {
for object in objects {
let ProfileImageFile = object["ProfileImage"] as? PFFileObject
let urlString = ProfileImageFile?.url as! String
if let url = URL(string: urlString) {
let data = try? Data(contentsOf: url)
if let imageData = data {
self.contentPostProPicUrlArray.append(urlString as NSString)
self.contentPostProPicImageCache.setObject(UIImage(data: imageData)!, forKey: urlString as NSString)
}
}
if object["Post"] != nil && object["UserLikes"] != nil && object["Username"] != nil && object["UserTime"] != nil {
self.contentPostArray.append(object["Post"] as! String)
self.contentLikeArray.append(object["UserLikes"] as! Int)
self.contentUsernameArray.append(object["Username"] as! String)
self.contentTimeArray.append(object["UserTime"] as! String)
self.contentObjectIdArray.append(object.objectId!)
}
}
print(self.contentPostArray)
}
})
}
}
You are trying to reload a cell that is not on the table. Try inserting it. Also, you are risking a race condition.
From the docs
Reloading a row causes the table view to ask its data source for a new cell for that row. The table animates that new cell in as it animates the old row out. Call this method if you want to alert the user that the value of a cell is changing.

swift - table is moving down when change the UISegmentedControl (segcon)

my table has 03 sections, 1) has no row, 2) has no row 3) has 04 segcons.
when segcon changed then the data changes then reload the section 03. the issue is when the data is not fill the screen then the table moves down automatically. what i want is, the table will not move down automatically eventhough the data is not enough to fill the screen.
i try to use the footer but no help
self.tableView.tableFooterView = UIView()
see here for more detail
https://youtu.be/40IqAh42nxM
media segcon
let mediaCell: MediaCell = tableView.dequeueReusableCell(withIdentifier: "mediaCell", for: indexPath) as! MediaCell
mediaCell.showImage(url: self.photoNsurlArray[indexPath.row]!)
mediaCell.reportButtonProgram.addTarget(self, action: #selector(self.funcToHandleReportButton), for: .touchUpInside)
let timestampe = funcConvertEpocTimeToDate(timeData: self.timestampsGlobal, atIndex: indexPath.row)
mediaCell.timestampLabel.text = "\(timestampe)"
let uid = self.uidkeys[indexPath.row]!
Utils.getUserDetail(uid: uid) { (user) in
let displayName = user?[Constants.User.display_name] as? String
if let name = displayName,!(displayName?.isEmpty)! {
mediaCell.timestampLabel.text = "by " + "\(name) on " + "\(timestampe)"
}
print("getUserDetail display name: ", displayName)
}
returnCell = mediaCell
near segcon
let locationcell: LocationCellDesign = tableView.dequeueReusableCell(withIdentifier: "locationCellDesign", for: indexPath) as! LocationCellDesign
let elementOfLocations = self.nearlocations[indexPath.row]!
print("***elementOfLocations \(String(describing: elementOfLocations))")
let locationDescriptionText = "\(String(describing: elementOfLocations["description"]!))"
if locationDescriptionText.characters.count != 0 {
locationcell.descriptionLabel.text = locationDescriptionText
} else {
locationcell.descriptionLabel.text = "No Description"
}
locationcell.address.text = "\(String(describing: elementOfLocations["address"]!))"
let ownerID = "\(String(describing: elementOfLocations["owner"]!))"
if ownerID == "ApperSystem" {
locationcell.owner.text = "Owner: " + "ApperSystem"
} else {
Utils.getOwnerDetail(uid: ownerID) { (user) -> Void in
let userProperties = user!
let userDisplayName = userProperties["displayName"] as! String
locationcell.owner.text = "Owner: " + userDisplayName
}
}
let userArray = elementOfLocations["users"]! as! [String]
locationcell.users.text = "Users: " + "\(String(describing: userArray.count))"
locationcell.nearLocationAvatarImageView.image = UIImage(named: "no-image")
if elementOfLocations[Constants.LocationDataFields.media_urls] != nil {
let avatarForLocation = elementOfLocations[Constants.LocationDataFields.media_urls] as! NSDictionary
let numberOfPitureInLocation = avatarForLocation.allKeys(for: 1).sorted { ($0 as! String) < ($1 as! String) }
if numberOfPitureInLocation.count > 0 {
Utils.loadImageFromFireBase(key: numberOfPitureInLocation.last! as! String) { (string) -> Void in
print("***loadImageFromFireBase string: \(String(describing: string))")
if let imageUrlString = string, !(string?.isEmpty)! {
let imageUrl:NSURL = NSURL(string: imageUrlString)!
locationcell.nearLocationAvatarImageView.sd_setImage(with: imageUrl as URL)
} else {
print("***loadImageFromFireBase mediaKey has no mediaUrl")
}
}
}
}
returnCell = locationcell

How do I retrieve data by using the Parse PFQuery?

Hi I'm trying to retrive data by using the PFQuery.
loadPosts in the tabelviewController.
My problem is commentsArray.
As you can see... commentsArray is not synchronized other arryas.
Because It has different db's class.
I want get all the data arrays to set the uilabel's text synchronizing
How do I achieve that?
I also had trying to parse's synchronized method. but It is really slow.
Here is my code. This function in the TableViewController.
func loadPosts() {
//STEP 1. Find posts related to people who we are following
let followQuery = PFQuery(className: "fans")
followQuery.whereKey("myfans", equalTo: PFUser.currentUser()!.username!)
followQuery.findObjectsInBackgroundWithBlock ({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
//clean up
self.followArray.removeAll(keepCapacity: false)
//Appending where people following..
//find related objects
for object in objects! {
self.followArray.append(object.objectForKey("fan") as! String)
}
//append current user to see own posts in feed
self.followArray.append(PFUser.currentUser()!.username!)
//STEP 2. Find posts made by people appended to followArray
let query = PFQuery(className: "posts")
query.whereKey("username", containedIn: self.followArray)
query.limit = self.page
query.addDescendingOrder("createdAt")
query.findObjectsInBackgroundWithBlock({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
//clean up
self.usernameArray.removeAll(keepCapacity: false)
self.profileArray.removeAll(keepCapacity: false)
self.dateArray.removeAll(keepCapacity: false)
self.postArray.removeAll(keepCapacity: false)
self.descriptionArray.removeAll(keepCapacity: false)
self.uuidArray.removeAll(keepCapacity: false)
//For Test
self.commentsArray.removeAll(keepCapacity: false)
self.isTappedLikeArray.removeAll(keepCapacity: false)
//find related objects
for object in objects! {
self.usernameArray.append(object.objectForKey("username") as! String)
self.profileArray.append(object.objectForKey("profileImg") as! PFFile)
self.dateArray.append(object.createdAt)
self.postArray.append(object.objectForKey("postImg") as! PFFile)
self.descriptionArray.append(object.objectForKey("title") as! String)
self.uuidArray.append(object.objectForKey("uuid") as! String)
//STEP 3.
//Check Comment
let countQuery = PFQuery(className: "comments")
countQuery.whereKey("to", equalTo: object.objectForKey("uuid") as! String)
countQuery.limit = 1
countQuery.addAscendingOrder("createdAt")
countQuery.countObjectsInBackgroundWithBlock({(count:Int32, error:NSError?) -> Void in
//if no any likes are found, else found likes
if count==0 {
self.commentsArray.append("")
}else{
let commentQuery = PFQuery(className: "comments")
commentQuery.whereKey("to", equalTo: object.objectForKey("uuid") as! String)
commentQuery.limit = 1
commentQuery.addDescendingOrder("createdAt")
commentQuery.findObjectsInBackgroundWithBlock({(objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
//clean up
//find related object
for object in objects!{
let comment = object.objectForKey("comment") as! String
let by = object.objectForKey("username") as! String
let commentString = by + " " + comment
self.commentsArray.append(commentString)
}
//reload tableView & end spinning of refresher
self.tableView.reloadData()
self.refresher.endRefreshing()
}else {
print(error?.localizedDescription)
}
})
}
})
}
} else {
print(error!.localizedDescription)
}
})
} else {
print(error!.localizedDescription)
}
})
}

Set the KILabel or ActiveLabel's height to 0 when it has no string. But Original UILabel is working fine

This question is related the UITableView and UILabel's height.
Hi I'm using the auto layout to set the dynamic Height in rows.
This is my layout in the table's cell.
Description Label
LikeCountLabel is blue color "1"
Comment Label
Time table is grey "Label"
Time Label
I also checked UILabel's property
import UIKit
import Parse
class ShopDetailCell: UITableViewCell {
#IBOutlet weak var commentBtn: UIButton!
#IBOutlet weak var likeBtn: UIButton!
#IBOutlet weak var profileImg: UIImageView!
#IBOutlet weak var postImg: UIImageView!
#IBOutlet weak var moreBtn: UIButton!
#IBOutlet weak var userNameBtn: UIButton!
#IBOutlet weak var uuidLabel: UILabel!
#IBOutlet weak var descriptionLabel: KILabel!
#IBOutlet weak var likesLabel: UILabel!
#IBOutlet weak var commentLabel: KILabel!
#IBOutlet weak var dateLabel: UILabel!
}
My TableView Controller
import UIKit
import Parse
import SDWebImage
class FeedVC: UITableViewController {
//UI Objects
#IBOutlet weak var indicator: UIActivityIndicatorView!
var refresher=UIRefreshControl()
//arrays to hold server data
var profileArray = [PFFile]()
var usernameArray = [String]()
var dateArray = [NSDate?]()
var postArray = [PFFile]()
var uuidArray = [String]()
var descriptionArray = [String]()
var commentsArray = [String]()
var isLoadedView:Bool = false
var sellingArray = [Bool]()
var followArray = [String]()
//advertise
var advArray = [PFFile]()
//page size
var page : Int = 10
//Default func
override func viewDidLoad() {
super.viewDidLoad()
//background color
tableView?.backgroundColor = UIColor(red: 0.0 / 255.0, green: 0.0 / 255.0, blue: 0.0 / 255.0, alpha: 1)
//automatic row height
tableView.estimatedRowHeight = 450
tableView.rowHeight = UITableViewAutomaticDimension
//pull to refresh
refresher.addTarget(self, action: #selector(FeedVC.loadPosts), forControlEvents: UIControlEvents.ValueChanged)
tableView.addSubview(refresher)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(FeedVC.refresh), name: "liked", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(FeedVC.uploaded(_:)), name: "Uploaded", object: nil)
self.navigationController?.setNavigationBarHidden(true, animated: true)
//calling function to load posts
loadPosts()
//receive notification from UploadViewController
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(FeedVC.scrollToFirstRow(_:)), name: "scrollToTop", object: nil)
}
func scrollToFirstRow(notification:NSNotification) {
if isLoadedView == true {
print("scrollToTop!!!!!")
let indexPath = NSIndexPath(forRow: 0, inSection: 0)
self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Top, animated: true)
}
}
// reloading func with posts after received notification
func uploaded(notification:NSNotification){
print("receicev")
loadPosts()
}
//refresh function
func refresh(){
self.tableView.reloadData()
}
// load posts
func loadPosts() {
//STEP 1. Find posts related to people who we are following
let followQuery = PFQuery(className: "fans")
followQuery.whereKey("myfans", equalTo: PFUser.currentUser()!.username!)
followQuery.findObjectsInBackgroundWithBlock ({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
//clean up
self.followArray.removeAll(keepCapacity: false)
//Appending where people following..
//find related objects
for object in objects! {
self.followArray.append(object.objectForKey("fan") as! String)
}
//append current user to see own posts in feed
self.followArray.append(PFUser.currentUser()!.username!)
//STEP 2. Find posts made by people appended to followArray
let query = PFQuery(className: "posts")
query.whereKey("username", containedIn: self.followArray)
query.limit = self.page
query.addDescendingOrder("createdAt")
query.findObjectsInBackgroundWithBlock({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
//clean up
self.usernameArray.removeAll(keepCapacity: false)
self.profileArray.removeAll(keepCapacity: false)
self.dateArray.removeAll(keepCapacity: false)
self.postArray.removeAll(keepCapacity: false)
self.descriptionArray.removeAll(keepCapacity: false)
self.uuidArray.removeAll(keepCapacity: false)
//find related objects
for object in objects! {
self.usernameArray.append(object.objectForKey("username") as! String)
self.profileArray.append(object.objectForKey("profileImg") as! PFFile)
self.dateArray.append(object.createdAt)
self.postArray.append(object.objectForKey("postImg") as! PFFile)
self.descriptionArray.append(object.objectForKey("title") as! String)
self.uuidArray.append(object.objectForKey("uuid") as! String)
}
//reload tableView & end spinning of refresher
self.tableView.reloadData()
self.refresher.endRefreshing()
} else {
print(error!.localizedDescription)
}
})
} else {
print(error!.localizedDescription)
}
})
}
//scrolled down
override func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.y >= scrollView.contentSize.height - self.view.frame.size.height * 2 {
loadMore()
}
}
// pagination
func loadMore(){
//if posts on the server are more than shown
if page <= uuidArray.count {
//start animating indicator
indicator.startAnimating()
//increase page size to load + 10 posts
page = page + 10
//STEP 1. Find posts related to people who we are following
let followQuery = PFQuery(className: "fans")
followQuery.whereKey("myfans", equalTo: PFUser.currentUser()!.username!)
followQuery.findObjectsInBackgroundWithBlock ({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
//clean up
self.followArray.removeAll(keepCapacity: false)
//Appending where people following..
//find related objects
for object in objects! {
self.followArray.append(object.objectForKey("fan") as! String)
}
//append current user to see own posts in feed
self.followArray.append(PFUser.currentUser()!.username!)
//STEP 2. Find posts made by people appended to followArray
let query = PFQuery(className: "posts")
query.whereKey("username", containedIn: self.followArray)
query.limit = self.page
query.addDescendingOrder("createdAt")
query.findObjectsInBackgroundWithBlock({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
//clean up
self.usernameArray.removeAll(keepCapacity: false)
self.profileArray.removeAll(keepCapacity: false)
self.dateArray.removeAll(keepCapacity: false)
self.postArray.removeAll(keepCapacity: false)
self.descriptionArray.removeAll(keepCapacity: false)
self.uuidArray.removeAll(keepCapacity: false)
//find related objects
for object in objects! {
self.usernameArray.append(object.objectForKey("username") as! String)
self.profileArray.append(object.objectForKey("profileImg") as! PFFile)
self.dateArray.append(object.createdAt)
self.postArray.append(object.objectForKey("postImg") as! PFFile)
self.descriptionArray.append(object.objectForKey("title") as! String)
self.uuidArray.append(object.objectForKey("uuid") as! String)
}
//reload tableView stop indicator animation
self.tableView.reloadData()
self.indicator.stopAnimating()
} else {
print(error!.localizedDescription)
}
})
} else {
print(error!.localizedDescription)
}
})
}
}
//number of cell
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return uuidArray.count + 1
}
// cell config
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// 가장 첫 페이지는 광고영역..
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("AdvertiseCell", forIndexPath: indexPath) as! AdvertiseCell
return cell
}
//define cell
let cell = tableView.dequeueReusableCellWithIdentifier("ShopDetailCell", forIndexPath: indexPath) as! ShopDetailCell
cell.userNameBtn.setTitle(usernameArray[indexPath.row - 1], forState: UIControlState.Normal)
cell.userNameBtn.sizeToFit()
cell.uuidLabel.text = uuidArray[indexPath.row - 1]
cell.descriptionLabel.text = descriptionArray[indexPath.row - 1]
cell.descriptionLabel.sizeToFit()
// manipulate like button depending on did user like it or not
let isComments = PFQuery(className: "comments")
isComments.whereKey("to", equalTo: cell.uuidLabel.text!)
isComments.limit = 1
isComments.addAscendingOrder("createdAt")
isComments.countObjectsInBackgroundWithBlock({(count:Int32, error:NSError?) -> Void in
//if no any likes are found, else found likes
if count==0 {
cell.commentLabel.text = nil
cell.commentLabel.sizeToFit()
// cell.commentHeight.constant = 0
}else{
//STEP 2. Request last (page size 15) comments
let query = PFQuery(className: "comments")
query.whereKey("to", equalTo: cell.uuidLabel.text!)
query.limit = 1
query.addDescendingOrder("createdAt")
query.findObjectsInBackgroundWithBlock({(objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
//find related object
for object in objects!{
let comment = object.objectForKey("comment") as! String
let by = object.objectForKey("username") as! String
let commentString = by + " " + comment
let boldString = NSMutableAttributedString(string: commentString)
boldString.addAttribute(NSFontAttributeName, value: UIFont(name: "SFUIText-Bold", size: 14)!, range: NSRange(0...by.characters.count))
cell.commentLabel.attributedText = boldString
cell.commentLabel.sizeToFit()
// self.tableView?.reloadData()
}
}else {
print(error?.localizedDescription)
}
})
}
})
//STEP 1. Load data of guest
let profileImgQuery = PFQuery(className: "_User")
profileImgQuery.whereKey("username", equalTo: usernameArray[indexPath.row - 1])
profileImgQuery.findObjectsInBackgroundWithBlock({(objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
//shown wrong user
if objects!.isEmpty {
print("Wrong User")
}
//find related to user information
for object in objects! {
//Set Image
let profilePictureObject = object.objectForKey("profileImg") as? PFFile
profilePictureObject?.getDataInBackgroundWithBlock { (imageData:NSData?, error:NSError?) -> Void in
if(imageData != nil)
{
let profileURL : NSURL = NSURL(string: profilePictureObject!.url!)!
cell.profileImg.sd_setImageWithURL(profileURL, placeholderImage: UIImage(named: "holderImg"))
}
}
}
} else {
print(error?.localizedDescription)
}
})
// place post picture using the sdwebimage
let postURL : NSURL = NSURL(string: postArray[indexPath.row - 1].url!)!
cell.postImg.sd_setImageWithURL(postURL, placeholderImage: UIImage(named: "holderImg"))
//Calculate post date
let from = dateArray[indexPath.row - 1]
let now = NSDate()
let components : NSCalendarUnit = [.Second, .Minute, .Hour, .Day, .WeekOfMonth]
let difference = NSCalendar.currentCalendar().components(components, fromDate: from!, toDate: now, options: [])
// logic what to show : Seconds, minutes, hours, days, or weeks
if difference.second <= 0 {
cell.dateLabel.text = "NOW"
}
if difference.second > 0 && difference.minute == 0 {
cell.dateLabel.text = "\(difference.second) SEC AGO"
}
if difference.minute > 0 && difference.hour == 0 {
cell.dateLabel.text = "\(difference.minute) MIN AGO"
}
if difference.hour > 0 && difference.day == 0 {
cell.dateLabel.text = "\(difference.hour) HR AGO"
}
if difference.day > 0 && difference.weekOfMonth == 0 {
cell.dateLabel.text = "\(difference.day) DAY AGO"
}
if difference.weekOfMonth > 0 {
cell.dateLabel.text = "\(difference.weekOfMonth) WEEK AGO"
}
//
//
// manipulate like button depending on did user like it or not
let didLike = PFQuery(className: "likes")
didLike.whereKey("by", equalTo: PFUser.currentUser()!.username!)
didLike.whereKey("to", equalTo: cell.uuidLabel.text!)
didLike.countObjectsInBackgroundWithBlock({(count:Int32, error:NSError?) -> Void in
//if no any likes are found, else found likes
if count==0 {
cell.likeBtn.setTitle("unlike", forState: .Normal)
cell.likeBtn.setBackgroundImage(UIImage(named:"heartBtn"), forState: .Normal)
}else{
cell.likeBtn.setTitle("like", forState: .Normal)
cell.likeBtn.setBackgroundImage(UIImage(named: "heartTapBtn"), forState: .Normal)
}
})
//count total likes of shown post
let countLikes = PFQuery(className: "likes")
countLikes.whereKey("to", equalTo: cell.uuidLabel.text!)
countLikes.countObjectsInBackgroundWithBlock({(count:Int32, error:NSError?) -> Void in
cell.likesLabel.text="\(count) likes"
})
//asign index
cell.userNameBtn.layer.setValue(indexPath, forKey: "index")
cell.commentBtn.layer.setValue(indexPath, forKey: "index")
cell.moreBtn.layer.setValue(indexPath, forKey: "index")
// #mention is tapped
cell.descriptionLabel.userHandleLinkTapHandler = { label, handle, rang in
var mention = handle
mention = String(mention.characters.dropFirst())
if mention.lowercaseString == PFUser.currentUser()?.username {
let home = self.storyboard?.instantiateViewControllerWithIdentifier("MyShopCollectionVC") as! MyShopCollectionVC
self.navigationController?.pushViewController(home, animated: true)
} else {
guestname.append(mention.lowercaseString)
let guest = self.storyboard?.instantiateViewControllerWithIdentifier("GuestShopCollectionVC") as! GuestShopCollectionVC
self.navigationController?.pushViewController(guest, animated: true)
}
}
// #Hashtag is tapped
cell.descriptionLabel.hashtagLinkTapHandler = {label, handle, range in
var mention = handle
mention = String(mention.characters.dropFirst())
hashtag.append(mention.lowercaseString)
let hashvc = self.storyboard?.instantiateViewControllerWithIdentifier("HashTagsCollectionVC") as! HashTagsCollectionVC
self.navigationController?.pushViewController(hashvc, animated: true)
}
// cell.layoutIfNeeded()
return cell
}
////////
#IBAction func userNameBtnTapped(sender: AnyObject) {
//call index of button
let i = sender.layer.valueForKey("index") as! NSIndexPath
//call cell to call further cell data
let cell = tableView.cellForRowAtIndexPath(i) as! ShopDetailCell
//if user tapped on himeself go home, else go guest
if cell.userNameBtn.titleLabel?.text == PFUser.currentUser()?.username {
let home = self.storyboard?.instantiateViewControllerWithIdentifier("MyShopCollectionVC") as! MyShopCollectionVC
self.navigationController?.pushViewController(home, animated: true)
}else {
guestname.append(cell.userNameBtn.titleLabel!.text!)
let guest = self.storyboard?.instantiateViewControllerWithIdentifier("GuestShopCollectionVC") as! GuestShopCollectionVC
self.navigationController?.pushViewController(guest, animated: true)
}
}
#IBAction func commentBtnTapped(sender: AnyObject) {
print("commentTapped")
// call index of button
let i = sender.layer.valueForKey("index") as! NSIndexPath
// call cell to call further cell data
let cell = tableView.cellForRowAtIndexPath(i) as! ShopDetailCell
//send related data to global variables
commentUUID.append(cell.uuidLabel.text!)
// commentOwner.append(cell.userNameLabel.titleLabel!.text!)
commentOwner.append(cell.userNameBtn.titleLabel!.text!)
let comment = self.storyboard?.instantiateViewControllerWithIdentifier("CommentVC") as! CommentVC
self.navigationController?.pushViewController(comment, animated: true)
}
#IBAction func moreBtnTapped(sender: AnyObject) {
let i = sender.layer.valueForKey("index") as! NSIndexPath
//Call cell to call further cell date
let cell = tableView.cellForRowAtIndexPath(i) as! ShopDetailCell
//Delete Action
let delete = UIAlertAction(title: "Delete", style: .Default) { (UIAlertAction) -> Void in
//STEP 1. Delete row from tablevIEW
self.usernameArray.removeAtIndex(i.row)
self.profileArray.removeAtIndex(i.row)
self.dateArray.removeAtIndex(i.row)
self.postArray.removeAtIndex(i.row)
self.descriptionArray.removeAtIndex(i.row)
self.uuidArray.removeAtIndex(i.row)
//STEP 2. Delete post from server
let postQuery = PFQuery(className: "posts")
postQuery.whereKey("uuid", equalTo: cell.uuidLabel.text!)
postQuery.findObjectsInBackgroundWithBlock({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
for object in objects! {
object.deleteInBackgroundWithBlock({ (success:Bool, error:NSError?) -> Void in
if success {
//send notification to rootViewController to update shown posts
NSNotificationCenter.defaultCenter().postNotificationName("uploaded", object: nil)
//push back
self.navigationController?.popViewControllerAnimated(true)
} else {
print(error?.localizedDescription)
}
})
}
} else {
print(error?.localizedDescription)
}
})
//STEP 2. Delete likes of post from server
let likeQuery = PFQuery(className: "likes")
likeQuery.whereKey("to", equalTo: cell.uuidLabel.text!)
likeQuery.findObjectsInBackgroundWithBlock({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
for object in objects! {
object.deleteEventually()
}
}
})
//STEP 3. Delete comments of post from server
let commentQuery = PFQuery(className: "comments")
commentQuery.whereKey("to", equalTo: cell.uuidLabel.text!)
commentQuery.findObjectsInBackgroundWithBlock({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
for object in objects! {
object.deleteEventually()
}
}
})
//STEP 4. Delete hashtags of post from server
let hashtagQuery = PFQuery(className: "hashtags")
hashtagQuery.whereKey("to", equalTo: cell.uuidLabel.text!)
hashtagQuery.findObjectsInBackgroundWithBlock({ (objects:[PFObject]?, error:NSError?) -> Void in
if error == nil {
for object in objects! {
object.deleteEventually()
}
}
})
}
//Complain Action
let complain = UIAlertAction(title: "Complain", style: .Default) { (UIAlertAction) -> Void in
//send complain to server
let complainObj = PFObject(className: "complain")
complainObj["by"] = PFUser.currentUser()?.username
complainObj["to"] = cell.uuidLabel.text
complainObj["owner"] = cell.userNameBtn.titleLabel?.text
complainObj.saveInBackgroundWithBlock({ (success:Bool, error:NSError?) -> Void in
if success {
self.alert("Complain has been made successfully", message: "Thank You! We will consider your complain")
}else {
self.alert("ERROR", message: error!.localizedDescription)
}
})
}
//Cancel ACTION
let cancel = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
//create menu controller
let menu = UIAlertController(title: "Menu", message: nil, preferredStyle: .ActionSheet)
//if post belongs to user
if cell.userNameBtn.titleLabel?.text == PFUser.currentUser()?.username {
menu.addAction(delete)
menu.addAction(cancel)
} else {
menu.addAction(complain)
menu.addAction(cancel)
}
//show menu
self.presentViewController(menu, animated: true, completion: nil)
}
//alert action
func alert(title: String, message:String){
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let ok = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
alert.addAction(ok)
presentViewController(alert, animated: true, completion: nil)
}
}
Ok My problem is ...If UILabel's string is nil or "" then It's height should be set to zero. Also Table cell's height is smaller than original height.
UILabel has no strings but It still has height.
Seems like this is the issue in the KILabel library, and the solution is (link) to create custom label class, inherit from KILabel and override this method:
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines {
if (self.text.length == 0) {
return CGRectZero;
}
return [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];
}
Declare your UILabel height constraint like this:-
#IBOutlet weak var likeLabelHeightConstraint: NSLayoutConstraint!
And set your height like this
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let height : CGFloat = 50 //Default cell Height
if likeLabel.text == nil || likeLabel.text == ""{
height = height - likeLabelHeightConstraint.constant
likeLabelHeightConstraint.constant = 0
}else{
//Set default scenario
}
return height
}
If you are retrieving data asynchronously then make sure you reload your data once your DataSource is updated....

Array out of index when getting PFFile from Parse

My app gets the images just fine but if I scroll up and down for a while and then pull to refresh, it crashes and says array out of index. This does not happen every time. I believe it has something to do with the fact that I am not using getdatainbackground but when I go to use that instead of getData(), it loops faster than it can actually retrieve the files and they are out of order.
My current code also gives me this error: Break on warnBlockingOperationOnMainThread() to debug.
2016-03-02 00:35:48.630 App Name[1137:96655] Warning: A long-running operation is being executed on the main thread.
if Reachability.isConnectedToNetwork() {
self.skip = 0
//--RESETTING THE ARRAYS FOR DATA--\\
self.contactText.removeAll(keepCapacity: true)
self.names.removeAll(keepCapacity: true)
self.images.removeAll(keepCapacity: true)
self.prices.removeAll(keepCapacity: true)
self.sizes.removeAll(keepCapacity: true)
self.conditions.removeAll(keepCapacity: true)
self.dates.removeAll(keepCapacity: true)
self.ids.removeAll(keepCapacity: true)
self.createdBy.removeAll(keepCapacity: true)
//--RESET THE USERS LOCATION WHEN HE REFRESHES IN CASE OF A DARASTIC MOVE IN LOCATION--\\
let userGeoPoint = PFUser.currentUser()!["location"] as! PFGeoPoint
//--GETTING ALL OF THE OBJECTS WITHIN 60 MILES OF USERS CURRENT LOCATION--\\
let query = PFQuery(className:"Shoes")
query.whereKey("Location", nearGeoPoint: userGeoPoint, withinMiles: 60)
let user = PFUser.currentUser() as PFUser!
let array: AnyObject? = user["blockedUsers"]
if(array != nil){
query.whereKey("createdBy", notContainedIn: array! as! [AnyObject])
}
query.limit = 50
query.orderByDescending("createdAt")
query.skip = self.skip
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
print("Successfully retrieved \(objects!.count) scores.")
for object in objects! {
if let dateCreated = object.createdAt as NSDate? {
self.dates.append(dateCreated)
}
self.contactText.append(object["Contact"] as! String)
self.descriptions.append(object["Description"] as! String)
self.names.append(object["Name"] as! String)
if object["price"] as! String == "" || object["price"] == nil{
self.prices.append("Negotiable")
}else{
self.prices.append(object["price"] as! String)
}
if object["size"] as! String == "" || object["size"] == nil{
self.sizes.append("N/A")
}else{
self.sizes.append(object["size"] as! String)
}
if object["conditionType"] as! String == "" || object["conditionType"] == nil{
self.conditions.append("N/A")
}else{
self.conditions.append(object["conditionType"] as! String)
}
self.ids.append(object.valueForKey("objectId") as! String)
self.createdBy.append(object["createdBy"] as! String)
let imageFile = object["imageFile"] as! PFFile
let imageData = imageFile.getData()
if (imageData != nil) {
let image = UIImage(data:imageData!)
self.images.append(image!)
}
}
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
})
}
else {
print(error)
}
self.refresher.endRefreshing()
}
self.skip+=50
} else {
print("Internet connection not available")
self.refresher.endRefreshing()
let alert = UIAlertView(title: "No Internet connection", message: "Please ensure you are connected to the Internet", delegate: nil, cancelButtonTitle: "OK")
alert.show()
}
Try adding a check to make sure your arrays aren't empty when assigning data to cells.