Parse.com API: How to query for objects incrementally? - iphone

I have a table view controller that is fetching objects from a database hosted by parse.com. At the moment, I have the view controller fetch all the objects in the database for storage in an array. Right now, there are 100 objects in the database, and what I would like to do, is have the table view controller fetch 20 of those objects and display them, then have it fetch and display 20 more when the table scrolls to the bottom.
Here is my init method:
- (id)initWithStyle:(UITableViewStyle)style{
self = [super initWithStyle:style];
if (self) {
//create the array, set the table view size, then populate it.
listings = [[NSMutableArray alloc] init];
[self populateArrayWithListings];
}
return self;
}
[self populateArrayWithListings]; simply fills the array listings with the 100 objects in the database. I have this method to detect when the table view controller is scrolled to at the bottom:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentOffset.y + scrollView.bounds.size.height > scrollView.contentSize.height * 0.9) {
}
}
Question is, what should I put inside those brackets to have it fetch the next 20 objects in the database?

I did the same thing what you needed.
FQuery *postQuery = [PFQuery queryWithClassName:#"Challenge"];
[postQuery addDescendingOrder:#"createdAt"];
[postQuery includeKey:#"userId"];
[postQuery setSkip:[mutArrPagingResponce count]];
[postQuery setLimit:20];
// Declare this array gloabally or keep it in another array
NSMutableArray *mutArrPagingResponce = [[postQuery findObjects] mutableCopy];
This will work as you are trying to achieve. Let me know if anything else required.

Related

IOS - Add subview to certain UICollectionView Cells only

This is the flow of my app: I have UICollectionview with thumbnails of images. User selects images and taps on Save button. Images get saved to camera roll.
This is what I want to do: With a subview I want to mark those cells that user has already tapped on and saved. How do I achieve that?
This is what I have already done....my approach: Obviously I'm missing something and I can't get it resolved.
When user taps on save button I successfully save the IDs of those images to local store using core data. Then, I fetch objects from that entity and put those objects in Array. Now, I'm trying to compare image IDs from fetched array with image IDs in collection view so I can only add subview to those cells where ID's match and that's where i get stuck. I don't know if that's the right approach.
My foolish attempt/s:
This method gets called when user taps on Save Button but it doesn't work properly. I feel as it's randomly adding subviews.
-(void)markSavedImages
{
BOOL isMarked;
NSArray *savedImages = [self fetchSavedPhotos];
NSEnumerator *enumerator = [savedImages objectEnumerator];
NSString *savedImage;
while (savedImage = [enumerator nextObject]) {
if ([[self.eventPhotos valueForKey:#"id"] containsObject:savedImage]) {
isMarked = YES;
}
else
{
isMarked = NO;
}
}
CVCell *cell = [[CVCell alloc]init];
for (NSIndexPath *indexPath in self.livePhotosCollectionView.indexPathsForVisibleItems) {
if (isMarked) {
cell = (CVCell*)[self.livePhotosCollectionView cellForItemAtIndexPath:indexPath];
UIImageView *markedImage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"markedCVCell.png"]];
[cell addSubview:markedImage];
[cell bringSubviewToFront:markedImage];
cell.userInteractionEnabled = NO;
}
}
}
The self.eventPhotos is an array that contains image's IDs and paths to server.
Please help me guys, I've been killing my self with this and couldn't find anything on google.
EDIT - as per request
-(NSArray*)fetchSavedPhotos
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication]delegate];
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Photos" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"eventID ==[c] %#", _eventID];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
if (fetchedObjects == nil) {
NSLog(#"No rows returned");
}
NSArray *savedImages = [fetchedObjects valueForKey:#"imageID"];
return savedImages;
}
Some more information....if I output savedImages array in markSavedImages method i do get the array I want to get. Something like this: (1324,2353,2324). Basically array of image IDs.
To begin with, don't add views just to some cells. This usually results in the view being shown on reused cells and multiple copies of the view being added to the cells which looks confusing and is inefficient.
Instead, add the view to every cell. Do it in your cell subclass and make the view publicly visible or add a method to show and hide it. Then do just that. Every time you return a cell, set the visibility of your subview. This ensures that the cell is always configured correctly and that your configuration code is simple.
For disabling the cells use a similar approach, always choose of the cell is enabled or not and set the user interaction enabled on the cell as a whole.
For your data you have a list of the source image ids and a list of the saved image ids. When you configure the cell you know the associated image id. To check if the cell should be tagged and disabled you can do:
BOOL saved = [self.savedImages containsObject:imageId];
(Compare the current id to the list of selected ids, don't do any looping).

UITableViewWith Paging Prev,next button

I have one NSMutableArray that has number of records (suppose 52).
for (int i=0;i<=52;i++) {
[arrSavedCalculation addObject:[NSString stringWithFormat:#"Bianca lb,Red %d",i]];
}
I want to give a paging Prev and Next button at bottom of tableview. and each page display 6 record.How can i do this. i also see this document but not success
http://www.ke-cai.net/2011/04/pagination-with-uitableview-in-ios.html
Try with these. Tested and found working. Can be improved.
#interface ViewController (){
//pageNumber will hold the current page index
NSInteger _pageNumber;
//keeping the max page number for ease of calculation
NSUInteger _maxPageNumber;
//the batch size
NSUInteger _numberOfVisibleRows;
}
#end
#implementation ViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
//Main data array is formed
NSUInteger rows = 0;
NSMutableArray *tempArray = [NSMutableArray array];
while (rows<52) {
[tempArray addObject:[NSString stringWithFormat:#"Item %d",rows+1]];
rows++;
}
self.mainArray = [NSArray arrayWithArray:tempArray];
_pageNumber = 0;
_numberOfVisibleRows = 5;
_maxPageNumber = [self.mainArray count]/_numberOfVisibleRows;
self.subArray = [self subArrayForPageNumber:_pageNumber];
}
- (NSArray *)subArrayForPageNumber:(NSUInteger)pageNumber{
NSRange range = NSMakeRange(_pageNumber*_numberOfVisibleRows, _numberOfVisibleRows);
if (range.location+range.length>[self.mainArray count]) {
range.length = [self.mainArray count]-range.location;
}
return [self.mainArray subarrayWithRange:range];
}
- (IBAction)buttonPressed:(UIBarButtonItem *)button{
//Same method is used for calculating the page numbers
if (button.tag ==1) {
_pageNumber= MIN(_maxPageNumber, _pageNumber+1);
}else{
_pageNumber = MAX(0, _pageNumber-1);
}
self.subArray = [self subArrayForPageNumber:_pageNumber];
[self.tableView reloadData];
}
Source Code
you should take two array.
1) Your MAIN ARRAY containing all of objects.
2) A Temporary array contain only 6 objects.
While loading tableview use that Temporary Array.
keep a page counter which will count your current page.
According to your 52 objects you can have 52/6 = 9 page but last page contain only 4 object.
like.
#define kNumberOfObjectInOnePage 6
set self.page=0 in "viewDidLoad" //self.page is the page counter.
-(void)nextPage:(id)sender{
self.page++;
take 6 objects from main array into temp array
reload your table view.
}
-(void)previous:(id)sender{
self.page--;
take 6 previous object into temp array
reload your table view.
}
A simple solution is if you are accessing records from web service then ask to him provide the total number of records in initial hits as well as first 6 records to display. If you want to do paging you should keep page 0 or 1 at initial hit to service and as you got 6 records on first page and you find that total records which are given from service is more than the 6 then you have to show previous and next button otherwise no need of these button.
As you accessing the records you have to put into in an NSArray or in Coredata. When you press next buttonyou have to increase the page count and fetch next 6 records and save to DB or array and reload the table.
if you press previous button you have to remove last 6 object from array or you can delete or check some kind last 6 records to display and reload table.
Hope this helps.

How to stop reallocation of an NSArray in iPhone?

I am using an NSArray and alloc it to in viewDidload method.
I have two views in my app and add data to this array from these views. The total number of rows shown in a table according to [array count].
But the problem I'm facing is that when I call the view where I'm using this array from another view then this array realloc and due to this my array size again start from 0. I don't want that. I want the array size start from its last position.
So please help me to remove out this problem where I declare this array or any alternate to do this.
You need to make the array lazily loaded.
Have the view where the array is located have a getter that looks like this:
-(NSArray *)theArray {
if(theArray == nil) {
theArray = [[[NSArray alloc] init] autorelease]; //If using ARC, don't autorelease
}
return theArray;
}
In your firstView's viewDidLoad, just call the array like this:
[self theArray];
Now in your second view, call the first array like this
[firstView theArray];

Memory Management, ARC - what to nil?

Background -
I am using automatic reference counting on a project. The root view is a Table View (Master / Detail setup) showing a list of "slide shows". Click on a table cell and you are taken to the detail view which consists of a Scroll view with views (viewController.view) in it (this is the "slide show"). Each slide show has a front cover and back cover (same view controller formatted differently) that sandwich an variable number of pages. Here is the code to load the slide show:
- (void)loadScrollView
{
// The front and back cover are set in Interface Builder because they
// are reused for every slide show, just labels are changed.
[self.scrollView addSubview:self.frontCoverViewController.view];
[self.frontCoverViewController setCoverTitle:_data.name creationDate:_data.creationDate isFrontCover:YES];
[self.pagesArray addObject:self.frontCoverViewController];
for (int i = 0; i < [self getTotalNumberOfComps]; i++)
{
PageViewController *pageView = [[PageViewController alloc] init];
pageView.data = [_compsArray objectAtIndex:i];
[_scrollView addSubview:pageView.view];
pageView.data.imgView = pageView.imageView;
pageView.slideShowViewController = self;
[_pagesArray addObject:pageView];
}
[self.scrollView addSubview:self.backCoverViewController.view];
[self.backCoverViewController setCoverTitle:_data.name creationDate:_data.creationDate isFrontCover:NO];
[self.pagesArray addObject:self.backCoverViewController];
[self.scrollView bringSubviewToFront:_frontCoverViewController.view];
[self setCurrentPage:0];
}
Problem -
So Im trying to reuse this slide show view controller so I need to nil and recreate the pages in the middle because each slide show has a different number of slides. Note a slide [PageViewController] is just a view with an ImageView in it. It has more functionality so we need the controller however the main display of the V.C. is the ImageView. I have created the following method to "empty" the slide show before running loadScrollView again with new data. Here is the empty method:
- (void)saflyEmptyScrollView
{
for (int i = 0; i < [self.pagesArray count]; i++)
{
if (i == 0 && i == ([self.pagesArray count]-1)) {
CoverViewController *cover = (CoverViewController*)[self.pagesArray objectAtIndex:i];
[cover.view removeFromSuperview];
} else {
PageViewController *page = (PageViewController*)[self.pagesArray objectAtIndex:i];
[page.view removeFromSuperview];
page = nil;
}
}
self.pagesArray = nil;
self.pagesArray = [[NSMutableArray alloc] init];
}
Big Question -
My main question is do I need to set the ImageView of each of these pages to nil? Or does setting the page itself to nil also free up the memory used by the ImageView/Labels/etc that are used in that view controller?
I tried adding self.imageView = nil; to the PageViewController's viewDidUnload and viewWillUnload methods (one at a time not in both) and I realized that setting page = nil does not call the pages Unload methods. Am I freeing up memory correctly.
I've read a lot of articles but Im still not sure if Im managing memory in the best way possible. Thanks so much for the help!
Generally, you shouldn't have to set things to nil. And in this specific case, the setting things to nil is doing nothing.
The line page = nil; is redundant, because the variable page goes out of scope immediately afterwards anyway. ARC knows this and doesn't need you to set it to nil.
And self.pagesArray = nil; is redundant because you follow it with self.pagesArray = [[NSMutableArray alloc] init];. The second line on its own will suffice.

Caching data using UINavigationController

I am using the TableViewUpdates example from WWDC #2010. Basically Apple creates collapsable and expandable TableViews by clicking on the section header. The data for the TableView gets created in viewWillAppear like so:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
/*
Check whether the section info array has been created, and if so whether the section count still matches the current section count. In general, you need to keep the section info synchronized with the rows and section. If you support editing in the table view, you need to appropriately update the section info during editing operations.
*/
if ((self.sectionInfoArray == nil) || ([self.sectionInfoArray count] != [self numberOfSectionsInTableView:self.tableView])) {
// For each play, set up a corresponding SectionInfo object to contain the default height for each row.
NSMutableArray *infoArray = [[NSMutableArray alloc] init];
for (Play *play in self.plays) {
SectionInfo *sectionInfo = [[SectionInfo alloc] init];
sectionInfo.play = play;
sectionInfo.open = NO;
NSNumber *defaultRowHeight = [NSNumber numberWithInteger:DEFAULT_ROW_HEIGHT];
NSInteger countOfQuotations = [[sectionInfo.play quotations] count];
for (NSInteger i = 0; i < countOfQuotations; i++) {
[sectionInfo insertObject:defaultRowHeight inRowHeightsAtIndex:i];
}
[infoArray addObject:sectionInfo];
[sectionInfo release];
}
self.sectionInfoArray = infoArray;
[infoArray release];
}
}
I've noticed for my case, where I have a lot of data, this is an expensive operation. I'd like to cache the data. The data gets created each time since it's in viewWillAppear. Because I'm using a UINavigationController to push this view onto the stack, if I put it into viewDidLoad, when I move away from this view and go back to home, I have to recreate the view again, viewDidLoad will run again, and it'll be slow again.
I haven't cached data before and was wondering what a good way to do it would be? Right now all of the data for the row headers and rows are in a database. So when this view gets pushed onto the stack, I grab the data, and create the table. I didn't know what a good mechanism would be to create the table and somehow cache the view or something to make it load faster on subsequent pushes of the viewController. Thanks.
The code you are showing is constructing the data source for the table view, and is not part of the view itself, per se. Wouldn't it meet your needs to execute this code in the view controller initializer and/or whenever your data source requires an update?
You can draw the parallel with NSFetchedResultsController and its delegate methods. These are executed apart form the view handling methods, with the fetched results controller being an ivar or (very often) a property of your view controller. Then, for example, once a fetched results controller has done its fetch, it can manage changes on a case-by-case basis, coordinated with the table view and its controller, or you can purposely refetch entirely. The view can appear and disappear completely independent of the data source maintenace.