Like button doesn't work every time - ios

I'm trying to make a social media app and one of the features is the like button, unfortunately when you click everything works fine but all of the time it doesn't update the amount of likes correctly and the like button correctly. I believe it's something to do with the refreshing it but I need it to be refreshed so that it can update the amount of likes. Does anyone know what's going on...
func like(sender: AnyObject) {
var buttonPosition: CGPoint = sender.convertPoint(CGPointZero, toView: self.table)
var indexPath: NSIndexPath = self.table.indexPathForRowAtPoint(buttonPosition)!
if sender.currentTitle == "Like" {
sender.setTitle("Unlike", forState: .Normal)
var addLikeQuery = PFQuery(className: "Post")
addLikeQuery.whereKey("message", equalTo: self.messages[indexPath.row])
addLikeQuery.findObjectsInBackgroundWithBlock { (aPosts, error) -> Void in
if let aPosts = aPosts {
for aPost in aPosts {
aPost.addUniqueObject(PFUser.currentUser()!.objectId!, forKey: "likers")
self.likeDisplayText = ((aPost["likers"] as! [String]).count - 1).description + " Like"
self.table.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
aPost.saveInBackgroundWithBlock({ (success, error) -> Void in
if error != nil {
self.likeDisplayText = "Couldn't Like Picture!"
}
})
}
}
}
} else {
sender.setTitle("Like", forState: .Normal)
var removeLikeQuery = PFQuery(className: "Post")
removeLikeQuery.whereKey("message", equalTo: self.messages[indexPath.row])
removeLikeQuery.findObjectsInBackgroundWithBlock { (rPosts, error) -> Void in
if let rPosts = rPosts {
for rPost in rPosts {
rPost.removeObject(PFUser.currentUser()!.objectId!, forKey: "likers")
self.likeDisplayText = ((rPost["likers"] as! [String]).count - 1).description + " Like"
self.table.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
rPost.saveInBackgroundWithBlock({ (success, error) -> Void in
if error != nil {
self.likeDisplayText = "Couldn't Like Picture!"
}
})
}
}
}
}
}

I'm not exactly sure what your question is but i may have some insight. If you are trying to edit some elemts in your UI through a function that does a lot of processing or is in another thread i suggest making it Asynchronus and anywhere in that function where you are editing UI make that code run on the main thread. Here's what i mean.
func like(sender: AnyObject) {
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) {
// do background stuff
dispatch_async(dispatch_get_main_queue()) {
// when you want to edit the text of the button
// or change something in your UI
// do it in here
self.likeDisplayText = "Couldn't Like Picture!"
}
}
}

Related

textField Editing Changed not reacting fast enough (Asynchronous calls)

I have a textfield that queries a firebase database for existing users and then display a UIImage according to if the user is available or not. The problem is that once the async code loads, the textfield doesn't react on changed value.
example. If i type 12345 as a username, i don't query the database. Everything ok. If i add a 6 it queries firebase and it shows me the user is free. if i press backspace and have 12345 the textFieldChanged is triggered again, and database is not queried. All OK.
but the problem is, when i have 12345, and i type 6 and very fast back so i have 12345, the query is running and shows me the available icon (because the back was pressed very fast). Is this because of the Simulator or is it a real problem and can i be fixed easily ?
my code:
#IBAction func textFieldChanged(_ sender: UITextField) {
if let username = usernameInputText.text, username.count > 5 {
checkIfUserExists(username: username) { doesExist in //(2)
if doesExist! {
self.completeSignupButton.isEnabled = false
self.ifAvailableImageView.image = UIImage(named: "Close")
} else {
self.completeSignupButton.isEnabled = true
self.ifAvailableImageView.image = UIImage(named: "Check")
}
}
} else {
ifAvailableImageView.image = UIImage(named: "Close")
self.completeSignupButton.isEnabled = false
}
}
func checkIfUserExists(username: String, completion: #escaping (Bool?) -> Void) {
spinner.startAnimating()
self.ifAvailableImageView.image = nil
let docRef = db.collection("users").document(username)
docRef.getDocument { (document, error) in
if error != nil {
self.spinner.stopAnimating()
completion(nil)
} else {
self.spinner.stopAnimating()
if let document = document {
if document.exists {
completion(true)
} else {
completion(false)
}
}
}
}
}
You can just compare the username being processed with the current text in the text field and not process the result if it not the same because you only want to process the latest one.
#IBAction func textFieldChanged(_ sender: UITextField) {
if let username = usernameInputText.text, username.count > 5 {
checkIfUserExists(username: username) { doesExist in //(2)
// Check if current text and the completion being processed are for the same username
if username != sender.text {
return
}
if doesExist! {
self.completeSignupButton.isEnabled = false
self.ifAvailableImageView.image = UIImage(named: "Close")
} else {
self.completeSignupButton.isEnabled = true
self.ifAvailableImageView.image = UIImage(named: "Check")
}
}
} else {
ifAvailableImageView.image = UIImage(named: "Close")
self.completeSignupButton.isEnabled = false
}
}

Healthkit HKAnchoredObjectQuery in iOS 9 not returning HKDeletedObject

I was excited to learn that Apple added tracking of deletes in HealthKit in iOS 9. So I set up a test project to try it out. Unfortunately, while I can get new data just fine, I am not getting any deleted objects in my callbacks.
I have a functioning HKAnchoredObjectQuery that tracks HKQueryAnchor and gives me new HKSamples whenever I add a BloodGlucose quantity into HealthKit via the Health app. However when I delete that same quantity and re-run the app the HKDeletedObject is always empty. Even I do an add and a delete at the same time. It seems no matter what I do, the HKDeletedObject array is always empty. But additions work fine (only getting the added samples since the last anchor).
Here is my code. It is just 2 files. To recreate the project just make a new swift project, give yourself the HealthKit Entitlement, and copy these in. (Note: When you run it, you only get one update for each run so if you make changes in HealthKit you have to stop and restart the app to test the callbacks.)
This is my HealthKit client:
//
// HKClient.swift
// HKTest
import UIKit
import HealthKit
class HKClient : NSObject {
var isSharingEnabled: Bool = false
let healthKitStore:HKHealthStore? = HKHealthStore()
let glucoseType : HKObjectType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodGlucose)!
override init(){
super.init()
}
func requestGlucosePermissions(authorizationCompleted: (success: Bool, error: NSError?)->Void) {
let dataTypesToRead : Set<HKObjectType> = [ glucoseType ]
if(!HKHealthStore.isHealthDataAvailable())
{
// let error = NSError(domain: "com.test.healthkit", code: 2, userInfo: [NSLocalizedDescriptionKey: "Healthkit is not available on this device"])
self.isSharingEnabled = false
return
}
self.healthKitStore?.requestAuthorizationToShareTypes(nil, readTypes: dataTypesToRead){(success, error) -> Void in
self.isSharingEnabled = true
authorizationCompleted(success: success, error: error)
}
}
func getGlucoseSinceAnchor(anchor:HKQueryAnchor?, maxResults:uint, callback: ((source: HKClient, added: [String]?, deleted: [String]?, newAnchor: HKQueryAnchor?, error: NSError?)->Void)!){
let queryEndDate = NSDate(timeIntervalSinceNow: NSTimeInterval(60.0 * 60.0 * 24))
let queryStartDate = NSDate.distantPast()
let sampleType: HKSampleType = glucoseType as! HKSampleType
let predicate: NSPredicate = HKAnchoredObjectQuery.predicateForSamplesWithStartDate(queryStartDate, endDate: queryEndDate, options: HKQueryOptions.None)
var hkAnchor: HKQueryAnchor;
if(anchor != nil){
hkAnchor = anchor!
} else {
hkAnchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))
}
let onAnchorQueryResults : ((HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, NSError?) -> Void)! = {
(query:HKAnchoredObjectQuery, addedObjects:[HKSample]?, deletedObjects:[HKDeletedObject]?, newAnchor:HKQueryAnchor?, nsError:NSError?) -> Void in
var added = [String]()
var deleted = [String]()
if (addedObjects?.count > 0){
for obj in addedObjects! {
let quant = obj as? HKQuantitySample
if(quant?.UUID.UUIDString != nil){
let val = Double( (quant?.quantity.doubleValueForUnit(HKUnit(fromString: "mg/dL")))! )
let msg : String = (quant?.UUID.UUIDString)! + " " + String(val)
added.append(msg)
}
}
}
if (deletedObjects?.count > 0){
for del in deletedObjects! {
let value : String = del.UUID.UUIDString
deleted.append(value)
}
}
if(callback != nil){
callback(source:self, added: added, deleted: deleted, newAnchor: newAnchor, error: nsError)
}
}
let anchoredQuery = HKAnchoredObjectQuery(type: sampleType, predicate: predicate, anchor: hkAnchor, limit: Int(maxResults), resultsHandler: onAnchorQueryResults)
healthKitStore?.executeQuery(anchoredQuery)
}
let AnchorKey = "HKClientAnchorKey"
func getAnchor() -> HKQueryAnchor? {
let encoded = NSUserDefaults.standardUserDefaults().dataForKey(AnchorKey)
if(encoded == nil){
return nil
}
let anchor = NSKeyedUnarchiver.unarchiveObjectWithData(encoded!) as? HKQueryAnchor
return anchor
}
func saveAnchor(anchor : HKQueryAnchor) {
let encoded = NSKeyedArchiver.archivedDataWithRootObject(anchor)
NSUserDefaults.standardUserDefaults().setValue(encoded, forKey: AnchorKey)
NSUserDefaults.standardUserDefaults().synchronize()
}
}
This is my View:
//
// ViewController.swift
// HKTest
import UIKit
import HealthKit
class ViewController: UIViewController {
let debugLabel = UILabel(frame: CGRect(x: 10,y: 20,width: 350,height: 600))
override func viewDidLoad() {
super.viewDidLoad()
self.view = UIView();
self.view.backgroundColor = UIColor.whiteColor()
debugLabel.textAlignment = NSTextAlignment.Center
debugLabel.textColor = UIColor.blackColor()
debugLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping
debugLabel.numberOfLines = 0
self.view.addSubview(debugLabel)
let hk = HKClient()
hk.requestGlucosePermissions(){
(success, error) -> Void in
if(success){
let anchor = hk.getAnchor()
hk.getGlucoseSinceAnchor(anchor, maxResults: 0)
{ (source, added, deleted, newAnchor, error) -> Void in
var msg : String = String()
if(deleted?.count > 0){
msg += "Deleted: \n" + (deleted?[0])!
for s in deleted!{
msg += s + "\n"
}
}
if (added?.count > 0) {
msg += "Added: "
for s in added!{
msg += s + "\n"
}
}
if(error != nil) {
msg = "Error = " + (error?.description)!
}
if(msg.isEmpty)
{
msg = "No changes"
}
debugPrint(msg)
if(newAnchor != nil && newAnchor != anchor){
hk.saveAnchor(newAnchor!)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.debugLabel.text = msg
})
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Note: I know Apple recommends to set this up using an HKObserverQuery. I originally did it that way in a Xamarin project and the behavior was the same (no HKDeletedObjects were getting sent). So when trying it out with swift I left out the HKObserverQuery for simplicity.
Remove the predicate (predicate: nil) when you instantiate the query and you'll see more results, including deleted results. The first time you run this (before the HKQueryAnchor has been saved) you'll get all the results so you may want to do something to filter those; but subsequent execution of the query will use your saved anchor so you'll only see changes since that saved anchor.
You probably also want to set the updateHandler property on your query before executing. This will set the query up to run continuously in the background calling the update handler whenever there are changes (Adds or Deletes). The code near the end of getGlucoseSinceAnchor(...) looks like
...
let anchoredQuery = HKAnchoredObjectQuery(type: sampleType, predicate: nil, anchor: hkAnchor, limit: Int(maxResults), resultsHandler: onAnchorQueryResults)
anchoredQuery.updateHandler = onAnchorQueryResults
healthKitStore?.executeQuery(anchoredQuery)
...

Save to parse manually after data has been queried

I am using Parse pinInBackground feature to pin data (image, text, date, coordinates) in the background and that data is queried every time the app is opened.The app is used to log a photo and the location and coordinates.So every entry you make is queried and displays in a tableview (only the count of entries yet).
I want to be able to let the user manually sink with Parse.com and not use the saveEventually feature.
Meaning I want a button and when pressed the queried data must sink with Parse and the be in pinned.
Here is how my data is pinned
#IBAction func submitButton(sender: AnyObject) {
locationLogs["title"] = log.title
locationLogs["description"] = log.descriptionOf
println("log = \(log.title)")
println("log = \(log.descriptionOf)")
locationLogs.pinInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
if (success) {
}else{
println("error = \(error)")
}}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
let location:CLLocationCoordinate2D = manager.location.coordinate
let altitude: CLLocationDistance = manager.location.altitude
println("new long= \(location.longitude)")
println("new lat= \(location.latitude)")
println("new altitude= \(altitude)")
println("new timestamp = \(timestamp)")
locationLogs["longitude"] = location.longitude
locationLogs["latitude"] = location.latitude
locationLogs["altitude"] = altitude
locationLogs["timestamp"] = timestamp
}
And here I query it
var result : AnyObject?
override func viewDidLoad() {
super.viewDidLoad()
let query = PFQuery(className:"LocationLogs")
query.fromLocalDatastore()
query.findObjectsInBackgroundWithBlock( { (NSArray results, NSError error) in
if error == nil && results != nil {
self.result = results
println("count = \(self.result!.count)")
self.loggedItemsTableView.reloadData()
}else{
println("ERRRROOOORRRR HORROOORRR= \(error)")
}
})
}
I have tried to use this command:
result?.saveAllInBackground()
But this only gave back an error.
Can someone please give me the correct code on how to do this or give me a link showing me how.
Here is a full code explanation on how I solved it:
//create an Array
var tableData: NSArray = []
func queryAll() {
let query = PFQuery(className:"LocationLogs")
query.fromLocalDatastore()
query.findObjectsInBackgroundWithBlock( { (NSArray results, NSError error) in
if error == nil && results != nil {
println("array = \(results)" )
self.tableData = results!
self.loggedItemsTableView.reloadData()
}else{
println("ERRRROOOORRRR HORROOORRR= \(error)")
}
})
}
//Call save on the class and not the object
PFObject.saveAllInBackground(self.tableData as [AnyObject], block: { (success: Bool, error: NSError?) -> Void in
if (success) {
//Remember to unpin the data if it will no longer be needed
PFObject.unpinAllInBackground(self.tableData as [AnyObject])
println("Pinned Data has successfully been saved")
}else{
println("error= \(error?.localizedDescription)")
}
})
Use this to safely cast/checked your object
if let results = self.result { // this will verify if your self.result is a non-nil array of object
// if it has a value then it will be passed to results
// you can now safely proceed on saving your objects
PFObject.saveAllInBackground(results, block: { (succeeded, error) -> Void in
// additional code
if succeeded {
// alert, remove hud ......
}else{
if let reqError = error {
println(reqError.localizedDescription)
}
}
})
}
struct log {
// this is dummy datastructure that imitate some part of your code ... you dont need it
var title = ""
var descriptionOf = ""
}
struct LocationInfo{
var title:String!
var description:String!
var location:PFGeoPoint!
var timestamp:NSDate!
var username:String
}
var locationManager = CLLocationManager()
var arrayOfLocations = [LocationInfo]()
func submitButton(sender: AnyObject) {
var logData = log()
var point = OneLocation(locationManager)
let username = PFUser.currentUser()?.username
var locationLogs = PFObject(className: "")
locationLogs["title"] = logData.title
locationLogs["description"] = logData.descriptionOf
locationLogs["Location"] = point
locationLogs["username"] = username
locationLogs["TimeStamp"] = locationManager.location.timestamp
locationLogs.saveInBackgroundWithBlock { (success:Bool, error:NSError?) -> Void in
if error == nil
{
println("data was save")
}
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
CLGeocoder().reverseGeocodeLocation (manager.location, completionHandler: {(placemarks, error)->Void in
var UserCurrentLocation:CLLocationCoordinate2D = manager.location.coordinate
// println("User's parking Location : \(UserCurrentLocation.latitude) \(UserCurrentLocation.longitude)")
if (error != nil) {
println("Error")
return
}
locationManager.stopUpdatingLocation()
})
}
func OneLocation(Manager:CLLocationManager)->PFGeoPoint{
var latitude = Manager.location.coordinate.latitude
var longitude = Manager.location.coordinate.longitude
var point = PFGeoPoint(latitude: latitude, longitude: longitude)
return point
}
func QueryFromParse(){
var query = PFQuery(className: "")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let new_objects = objects as? [PFObject]
{
for SingleObject in new_objects
{
// with this single object you could get the description, title , username,etc
var location = SingleObject["Location"] as! PFGeoPoint
var username = SingleObject["username"] as! String
var title = SingleObject["title"] as! String
var time = SingleObject["TimeStamp"] as! NSDate
var description = SingleObject["description"] as! String
var singleLocationInfo = LocationInfo(title: title, description: description, location: location, timestamp: time, username: username)
arrayOfLocations.append(singleLocationInfo)
// reload data for the tableview
}
}
}
else
{
println("error")
}
}
}
Therefore you have an arrayoflocations that can be used to populate data in your tableView
Struct log is a dummy datastructure
And also I don't know if that was what you wanted ... but you should get the idea..
Hope that helps..

IBOutlet nil after refreshing data

I'm using XCode 6.3.1 and I'm facing a weird problem that I can't figure it out.
I have a View Controller on my Storyboard opened with a modal segue. When the ViewController is opened it loads some data from my backend (Parse) it looks first on the cache and shows cached data (if exists) while the updated data from server is retrieved on the background. The process is the next:
Get cached data
If cached data exists then update interface
Request server data (In background)
When data arrives update interface
Everything works fine until step 4. When I try to refresh my interface suddenly half of my #IBOutlets are nil and of course app crashes.
what am I missing??
Here's the code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//eventId is set when the ViewController is instantiated
if eventId != nil {
loadEvent(eventId)
}
}
func loadEvent(id: String) {
var query = Event.query()
query?.cachePolicy = Util.getCachePolicy() //First look in cache, the request network data
query?.getObjectInBackgroundWithId(id, block: { (event: PFObject?, error: NSError?) -> Void in
if error == nil {
var updatedEvent = event as! Event
self.event = updatedEvent
self.updateLayout()
//When self.updateLayout() is called with cached data
//all my IBOutlets are fine but when it's called the second time,
//with data from server half of the IBOutlets are nil
}
})
}
func updateLayout() {
if event != nil {
eventTitle.text = event.name
var paletteColor : UIColor!
var location = event.location
var locationName = location["name"] as! String
eventLocation.text = NSString(format: NSLocalizedString("event_subtitle", comment: ""), event.formattedTimes(), locationName) as String
eventDescription.text = event.abstract
if event.paletteColor != 0 {
paletteColor = Util.colorFromInt(event.paletteColor)
eventHeader.backgroundColor = paletteColor
speakersBlockTitle.textColor = paletteColor
mapButton.tintColor = paletteColor
}
if event.hasPhoto() {
self.eventPhoto.file = event.eventPhoto
self.eventPhoto.loadInBackground({ (image:UIImage?, error:NSError?) -> Void in
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.eventPhoto.alpha = 1.0
})
})
} else {
self.eventPhoto.removeFromSuperview()
if paletteColor == nil {
paletteColor = eventHeader.backgroundColor
}
actionBar.backgroundColor = paletteColor
}
if event.speaker.isDataAvailable() {
var speaker = event.speaker
speakerName.text = speaker["name"] as? String
speakerInfo.text = speaker["speakerInfo"] as? String
speaker["speakerPhoto"]?.getDataInBackgroundWithBlock({ (imageData:NSData?, error:NSError?) -> Void in
if error == nil {
self.speakerPhoto.image = UIImage(data:imageData!)
self.speakerPhoto.layer.cornerRadius = self.speakerPhoto.frame.size.width/2
self.speakerPhoto.clipsToBounds = true
}
})
} else {
speakerBlock.removeFromSuperview()
}
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.eventHeader.alpha = 1.0
self.eventDescription.alpha = 1.0
self.speakerBlock.alpha = 1.0
self.mapButton.alpha = 1.0
})
}
}
This are all the nil IBOutlets:
Since the code in my comment above doesn't display correctly, here's that comment again:
Something like this:
#IBOutlet weak var myOutlet: UIButton! {
didSet {
if myOutlet == nil {
// put a breakpoint on the next line
println("myOutlet set to nil")
}
}
}

Custom iOS "load more" button in row list

I'm using a UItableview with a scrolling list. Now I have the default "load more" button that loads 10 objects at a time. It destroys the interface. Any ideas on how to customize it? A tried a few things but we no luck. Thanks
Try implementing this method that Parse provides for customizing the "Load more" cell:
// Override to customize the look of the cell that allows the user to load the next page of objects.
// The default implementation is a UITableViewCellStyleDefault cell with simple labels.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForNextPageAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"NextPage";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.text = #"Load more...";
return cell;
}
Well there is not much you can do with Parse controller in terms of customization. Also the animation of that controller seems to be a bit clunky. I also got a bunch of memory errors while using the component with latest IOs 9.
For that same reason I've replaced the existing Parse controller with the following. Gave me much more control over the animations and design than the standard PARSE controller.
If you want to reverse the order of the tableview to look like a messenger app please have a look here
add global variables
var objects = [PFObject(className: "Posts")]
var lockScrollSensor = false
let objectsPerPage = 10
add to viewDidLoad
//blank screen
let blankView = UIView(frame: CGRectMake(0, 0, self.tableView.frame.size.width, self.tableView.frame.size.height - 60))
blankView.tag = 101
blankView.backgroundColor = customColorYellow()
self.view.addSubview(blankView)
self.tableView.backgroundColor = customColorYellow()
// load tableview
tableViewFirstLoad()
Detect the position of tableview scroll to either call refresh or next page
override func scrollViewDidScroll(scrollView: UIScrollView) {
let scrollViewHeight = scrollView.frame.size.height;
let scrollContentSizeHeight = scrollView.contentSize.height;
let scrollOffset = scrollView.contentOffset.y;
// refresh - touch top
if (scrollOffset < -60)
{
if lockScrollSensor == false {
// stop method from non stop calling
lockScrollSensor = true
// loader
let spinningActivity = MBProgressHUD.showHUDAddedTo(self.parentViewController?.view, animated: true)
spinningActivity.color = UIColor.clearColor()
spinningActivity.activityIndicatorColor = customColorRed()
spinningActivity.yOffset = -Float((self.parentViewController?.view.frame.height)!/2) + 95
// call method to load more
self.tableViewRefresh({ (done) -> Void in
if done == true
{
// stop loader and free method
spinningActivity.hide(true, afterDelay: 0)
self.lockScrollSensor = false
// animate tableview
self.tableView.contentOffset.y = -60
UIView.animateWithDuration(1.5, delay: 1, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: UIViewAnimationOptions.CurveEaseIn, animations: { () -> Void in
self.tableView.contentOffset.y = 0
}, completion: nil)
}
})
}
}
// load more - touch down
else if (scrollOffset + scrollViewHeight > scrollContentSizeHeight)
{
if lockScrollSensor == false {
// stop method from non stop calling
lockScrollSensor = true
// start loader
let spinningActivity = MBProgressHUD.showHUDAddedTo(self.parentViewController?.view, animated: true)
spinningActivity.color = customColorYellow()
spinningActivity.yOffset = (Float(tableView.frame.size.height)/2)-25
// call method to load more
self.tableViewLoadMore({ (done) -> Void in
if done == true {
self.lockScrollSensor = false
spinningActivity.hide(true)
}
})
}
}
}
Refresh or next page methods that will load your PFObject array.
// first load
func tableViewFirstLoad(result: (done: Bool) -> Void){
var query = PFQuery(className: "Posts")
query = segueDataChecker()
// limit objects
query.limit = objectsPerPage
// get objects
query.findObjectsInBackgroundWithBlock { (posts: [AnyObject]?, error: NSError?) -> Void in
if error != nil
{
result(done: true)
let alertView = createDefaultAlertError(message: "\(error!.localizedDescription.capitalizedString)", actions: [UIAlertAction(title: "Ok", style: UIAlertActionStyle.Cancel, handler: nil)])
self.presentViewController(alertView, animated: true, completion: nil)
}
else
{
// clean array
self.objects.removeAll()
// populate array
for eachPost in posts!
{
if self.objects.count < self.objectsPerPage
{
self.objects.append(eachPost as! PFObject)
}
}
// reload and send back it has finished
self.tableView.reloadData()
result(done: true)
self.tableView.backgroundColor = UIColor(patternImage: UIImage(named: "pattern_clear_white.pdf")!)
// remove uiview
UIView.animateWithDuration(0.5, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in
if let viewWithTag = self.view.viewWithTag(101)
{
viewWithTag.alpha = 0
}
}, completion: nil)
}
}
}
// refresh existing content
func tableViewRefresh(result: (done: Bool) -> Void){
var query = PFQuery(className: "Posts")
query = segueDataChecker()
// limit objects
query.limit = self.objects.count
// save number of objects already loaded
let actualNumberOfPosts = self.objects.count
// get objects
query.findObjectsInBackgroundWithBlock { (posts: [AnyObject]?, error: NSError?) -> Void in
if error != nil
{
result(done: true)
let alertView = createDefaultAlertError(message: "\(error!.localizedDescription.capitalizedString)", actions: [UIAlertAction(title: "Ok", style: UIAlertActionStyle.Cancel, handler: nil)])
self.presentViewController(alertView, animated: true, completion: nil)
}
else
{
// clean array
self.objects.removeAll()
// populate array with same updated objects
for eachPost in posts!
{
if self.objects.count < actualNumberOfPosts
{
self.objects.append(eachPost as! PFObject)
}
}
// reload and send back it has finished
self.tableView.reloadData()
result(done: true)
}
}
}
// load next results
func tableViewLoadMore(result: (done: Bool) -> Void){
var query = PFQuery(className: "Posts")
query = segueDataChecker()
// skip objects already loaded
query.skip = self.objects.count; // skip the first 20 results
// limit results to next page amount
query.limit = objectsPerPage
// save number of objects already loaded
let actualNumberOfPosts = self.objects.count
// get objects
query.findObjectsInBackgroundWithBlock { (posts: [AnyObject]?, error: NSError?) -> Void in
if error != nil
{
result(done: true)
let alertView = createDefaultAlertError(message: "\(error!.localizedDescription.capitalizedString)", actions: [UIAlertAction(title: "Ok", style: UIAlertActionStyle.Cancel, handler: nil)])
self.presentViewController(alertView, animated: true, completion: nil)
}
else
{
// if there anything new
if posts?.count != 0
{
// populate array with new objects
for eachPost in posts!
{
if (self.objects.count < self.objectsPerPage + actualNumberOfPosts)
{
self.objects.append(eachPost as! PFObject)
}
}
// reload and send back it has finished
self.tableView.reloadData()
result(done: true)
// slide tablview one result down
let newIndexPath = NSIndexPath(forRow: actualNumberOfPosts, inSection: 0)
self.tableView.scrollToRowAtIndexPath(newIndexPath , atScrollPosition: UITableViewScrollPosition.None, animated: true)
}
// if there is nothing new
else
{
result(done: true)
}
}
}
}

Resources