Collection view cell to be added in the beginning - ios

I have a collection view cell with a imageview in it.The data fetched from server is stored in a array.I want the latest image fetched be shown in collection view cell in the beginning instead of at the end.Could anyone help me out with this?

I have correct your code a little, no need to allocate data when your JSON class seems to give it to you. Assuming the serverData is of kind NSMutableArray, this should work. I changed addObject: to insertObject:atIndex:.
-(void) getPhotservice {
[NewJsonHelperClass getExecuteWithParams:msgStr secondParm:nil onCompletion:^(NSDictionary *json){
NSDictionary *user = [json valueForKey:#"user"];
NSMutableArray *dArr = [userDict valueForKey:#"Images"];
for (int i = 0; i <= dArr.count-1; i++) {
reUse = [ReuseVc new];
reUse.photo_link = dArr[i];
// Just add it to the top
[serverData insertObject:reUse atIndex:0];
}
[self.colView reloadData]
}];
}

Related

How to download images in order in iOS Parse?

I am trying to set up three NSMutableArray to use in UITableView.
Here is my code:
for (PFObject *object in objects) {
PFUser *user = (PFUser *) object[#"user"];
[ [user objectForKey:#"image"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error)
{
//Add Comment text
[_commentsArray insertObject:[object objectForKey:#"comment"] atIndex:i];
//Add comment Id
[_commentIDArray insertObject:object.objectId atIndex:i];
//Add user image
[_picsArray insertObject:[UIImage imageWithData:data] atIndex:i ];
if (i == [objects count]-1)
{
[self.tableView reloadData];
}
}
else
{
NSLog(#"Errrror == %ld",(unsigned long)[_picsArray count]);
}
i++;
}];
}
In the PFQuery I am ordering it:
[query orderByDescending:#"createdAt"];
But as far as I can understand image in first row is large. So it takes time to download it. So it goes to second loop. Try to download image. Size is small. Download finished. Add to array. Now download for first image is finished. Add to array but to second place. How can manage it so it add items one by one in the order?
Check this:
// initially, add place holder
for (int i=0; i<objects.count; i++) {
[_commentsArray addObject:#""];
[_commentIDArray addObject:#""];
[_picsArray addObject:#""];
}
for (PFObject *object in objects) {
PFUser *user = (PFUser *) object[#"user"];
[ [user objectForKey:#"image"] getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (!error)
{
NSInteger orderIndex = [objects indexOfObject:object];
//Replace Comment text
[_commentsArray replaceObjectAtIndex:[object objectForKey:#"comment"] atIndex:orderIndex];
//Replace comment Id
[_commentIDArray replaceObjectAtIndex:object.objectId atIndex:orderIndex];
//Replace user image
[_picsArray replaceObjectAtIndex:[UIImage imageWithData:data] atIndex:orderIndex ];
if (i == [objects count]-1)
{
[self.tableView reloadData];
}
}
else
{
NSLog(#"Errrror == %ld",(unsigned long)[_picsArray count]);
}
i++;
}];
}
Rather than downloading image and create array to populate tableview, you have to just create array of PFObjects and use it with SDWebImage for Asynchronous image downloading without any issue or blocking UI.
I'm guessing that the question is really about not expending effort to download images beyond the scroll position while the visible rows are still being fetched.
The solution to that problem is to load images lazily, when they're needed to configure a cell in cellForRowAtIndexPath:. There's plenty of generic content available about this idea. For a parse-specific solution, see PFImageView here.
The gist is that image view will take care of loading and caching an image from the file. It will do this asynchronously, so there will be a low perceived lag. Just give the file to the image view and let it do the rest...
Your cellForRowAtIndexPath will look something like this:
// just guessing that your "objects" array is the table's datasource
PFObject *object = self.objects[indexPath.row];
PFUser *user = (PFUser *) object[#"user"];
// put a PFImageView in the cell (in this case with a tag==32)
PFImageView *imageView = (PFImageView *)[cell viewWithTag:32];
imageView.image = [UIImage imageNamed:#”placeholder.png”];
imageView.file = [user objectForKey:#"image"]; // assuming this is a file attribute
[imageView loadInBackground];
You have a problem that you try to do order based adding, where your blocks fire asynchronously so it can be in random order.
You should change to a dictionary or any other keyed data structure and use keys for your comments and pics (e.g. use comment id as the key).
Also double check if the callback of the block is executed on the main queue or any serial queue, because if it's not you need to add locks.
I had the same problem, my images were downloaded but not appearing in the order it should, my table view images and the titles were not matching.
To solve that, I created a column at my class in Parse.com that hold exclusively nameForImages, then each downloaded image is saved using this name.
The nameForImages had to be the same used for the column title, for example:
Title = Pepperoni and Four Cheese | nameForImage =
PepperoniAndFourCheese
Title - Muzzarella and Spinach | nameForImage = MuzzarellaAndSpinach
Etc...
This trick fit to solve my problem because the name of the image and the title appearing in the cell were short and had no special caracters.
I hope it helps or light a solution, good luck.

Sorting NSArray of NSDictionary...not completely working

So I have NSMutableArray of NSDictionary items. Each NSDictionary item has a key "name" that should be used to sort the array alphabetically. Except its "sorting" partially works. Its not sorting in exact alphabetical order. It's moving some of the dictionary in order, but leaves some out of order.
NSMutableArray *section1 = [champs lastObject];
NSArray *oldSection1 = [NSArray arrayWithArray:section1];
[section1 sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSDictionary *dictA = (NSDictionary *)obj1;
NSDictionary *dictB = (NSDictionary *)obj2;
NSString *champName1 = dictA[#"name"];
NSString *champName2 = dictB[#"name"];
return [champName1 compare:champName2];
}];
// Animation
for (NSDictionary *champInfo2 in section1) {
if ([oldSection1 indexOfObject:champInfo2] != [section1 indexOfObject:champInfo2]) {
[self.collectionView moveItemAtIndexPath:[NSIndexPath indexPathForItem:[oldSection1 indexOfObject:champInfo2] inSection:1] toIndexPath:[NSIndexPath indexPathForItem:[section1 indexOfObject:champInfo2] inSection:1]];
}
}
I even tried this code
NSMutableArray *section1 = [champs lastObject];
NSArray *oldSection1 = [NSArray arrayWithArray:section1];
/*[section1 sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSDictionary *dictA = (NSDictionary *)obj1;
NSDictionary *dictB = (NSDictionary *)obj2;
NSString *champName1 = dictA[#"name"];
NSString *champName2 = dictB[#"name"];
return [champName1 compare:champName2];
}];*/
section1 = [[NSArray arrayWithArray:section1] sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES]]].mutableCopy;
// Animation
for (NSDictionary *champInfo2 in section1) {
if ([oldSection1 indexOfObject:champInfo2] != [section1 indexOfObject:champInfo2]) {
[self.collectionView moveItemAtIndexPath:[NSIndexPath indexPathForItem:[oldSection1 indexOfObject:champInfo2] inSection:1] toIndexPath:[NSIndexPath indexPathForItem:[section1 indexOfObject:champInfo2] inSection:1]];
}
}
Ok so the problem was I wasn't updating my snapshot of the content before reorganization as I updated it visually. Updated code:
// Animation
for (NSDictionary *champInfo2 in section) {
if ([oldSection indexOfObject:champInfo2] != [section indexOfObject:champInfo2]) {
[self.collectionView moveItemAtIndexPath:[NSIndexPath indexPathForItem:[oldSection indexOfObject:champInfo2] inSection:[champs indexOfObject:section]] toIndexPath:[NSIndexPath indexPathForItem:[section indexOfObject:champInfo2] inSection:[champs indexOfObject:section]]];
[oldSection removeObject:champInfo2];
[oldSection insertObject:champInfo2 atIndex:[section indexOfObject:champInfo2]];
}
}
The upper part of your code (i.e. the sorting) works fine. It can be simplified if you use sort descriptors, but it's definitely not the issue.
The real problem is with the code that moves things around.
Before the first move the indexing of the oldSection1 and self.collectionView is in sync, i.e. if an item XYZ is at index i in oldSection1, then it is also at index i in self.collectionView.
After the first move, however, the indexing gets out of sync, because the move is executed only in self.collectionView, but not in oldSection1. For example, if you start with
oldSection1 = C D A B
collectionView = C D A B
and you want it to be
sorted = A B C D
after the first move of C the data is going to look like
oldSection1 = C D A B
collectionView = A C D B
When it's time to move D to its new spot, C gets moved instead.
To fix this issue, make oldSection1 mutable, and execute "parallel" moves as you move things around in self.collectionView.
It looks like your actual data is correct, but your collection view is displaying them incorrectly since you're iterating over the data, moving one row at a time. This most likely results in re-ordering rows that have already been re-ordered.
Calling [self.collectionView reloadData] instead of manually moving rows should resolve this issue.
To perform multiple UICollectionView item updates (insert/delete/move), it is recommended to use performBatchUpdates:completion:. Otherwise you may encounter invalid indexes.
Link to official Apple's documentation

How to Sort object by alphabet order for UITableViewCell?

I want to sort objects that i created & stored in NSMutableArray in AppDelegate.m.
Stations is NSObject Class
I want to show station names in another UIViewController in alphabet order(in UITableViewCell) & when i click on them i want to pass the object that contains station name,latitude,longitude to next UIViewController
Currently i have extracted station name from stationList(Global NSMutableArray) to another NSMutableArray on UIViewControllers Cell & sorted it via
[sortedArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
but when didSelectRowAtIndexPath is being called i have to get this name from cell & search it in the stationList array to pass lat,long which is not good i think.
stationList Array Log(It has 100 objects):-
<__NSArrayM 0x79a2f110>(
<Stations: 0x78743540>,
<Stations: 0x78743630>,
<Stations: 0x78743670>,
<Stations: 0x78743750>,
<Stations: 0x78743830>,
<Stations: 0x78743910>,
<Stations: 0x78743a10>,
<Stations: 0x78743af0>
}
-(void)loadStations
{
stationList = [[NSMutableArray alloc]init];
NSString *path = [[NSBundle mainBundle] pathForResource:#"stations" ofType:#"txt"];
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
// NSLog(#"%#",content);
NSArray *tempArr = [content componentsSeparatedByString:#"\n"];
for (int i =0; i<[tempArr count]; i++)
{
NSString *rawData = [tempArr objectAtIndex:i];
if (rawData !=nil)
{
Stations *newStation = [[Stations alloc]init];
NSArray *data = [rawData componentsSeparatedByString:#"\t"];
newStation.sId = i+1;
newStation.name = [NSString stringWithFormat:#"%#",[data objectAtIndex:0]];
newStation.latitude = [[data objectAtIndex:1] doubleValue];
newStation.longitude = [[data objectAtIndex:2] doubleValue];
[stationList addObject:newStation];
}
}
}
Suggest me good practice/way for this, or maybe use Dictionary?
I see two solutions here:
1) you can retrieve object from your stationList based on indexPath.row
- (void) tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
Stations* station = stationsList[indexPath.row];
...
}
2) you can create custom UITableViewCell and store referenced object there:
#interface StationCell : UITableVIewCell
#property(weak) Stations* station;
#end
...
- (UITableViewCell*) tableView:(UITableVIew*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
StationCell* cell;
// dequeue StationCell
...
cell.station = stationList[indexPath.row];
}
...
- (void) tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
StationCell* cell = [tableView cellAtIndexPath:indexPath];
Stations* station = cell.station;
...
}
I would choose between solutions based on complexity of data displayed in cell - using custom UITableViewCell gives oportunity to move configuration of cell from view controller to cell implementation.
edit
As far as sorting stationsList, you can use e.g.:
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES];
stationsList = [stationsList sortedArrayUsingDescriptors:#[sort]];
I would advise against sorting an array of station names separate from your array stationList. Instead I would suggest sorting your stationList (or a copy of it if you only want to change the oder in the table view and need to maintain some other ordering elsewhere)
There are methods like sortUsingComparator: that takes comparator block as a parameter. You write a block that compares 2 elements in your array, and the method uses that block to figure out the ordering of your objects and sort the array. In your case it would simply be a matter of writing a block that compares the name properties of 2 station objects.

Creating an Array for cellForRowAtIndexPath with Other Methods

I have an indexed tableView that displays a list of songs from the user's iPod library. This is how I currently get the song titles, which causes scrolling to be very slow:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.textLabel.text= [self titleForRow:indexPath]; //getting cell content
}
...which calls these methods:
-(NSString *)titleForRow:(NSIndexPath *)indexpath{
NSMutableArray* rowArray=[[NSMutableArray alloc]initWithCapacity:0];
rowArray=[self getArrayOfRowsForSection:indexpath.section];
NSString *titleToBeDisplayed=[rowArray objectAtIndex:indexpath.row];
return titleToBeDisplayed;
}
-(NSMutableArray *)getArrayOfRowsForSection:(NSInteger)section
{
NSString *rowTitle;
NSString *sectionTitle;
NSMutableArray *rowContainer=[[NSMutableArray alloc]initWithCapacity:0];
for (int i=0; i<self.alphabetArray.count; i++)
{
if (section==i) // check for right section
{
sectionTitle= [self.alphabetArray objectAtIndex:i]; //getting section title
for (MPMediaItem *song in songs)
{
NSString *title = [song valueForProperty:MPMediaItemPropertyTitle];
rowTitle= [title substringToIndex:1]; //modifying the statement to its first alphabet
if ([rowTitle isEqualToString:sectionTitle]) //checking if modified statement is same as section title
{
[rowContainer addObject:title]; //adding the row contents of a particular section in array
}
}
}
}
return rowContainer;
}
The problem is: for each cellForRowAtIndexPath, I am calling these methods and creating these arrays for each cell. So I need to create a separate array and simply call the objectAtIndex from that array.
This is what I have tried so far: created an NSMutableArray *newArray, and the following method:
-(NSMutableArray *)getNewArray:(NSInteger)section
{
NSString *rowTitle;
NSString *sectionTitle;
for (int i=0; i<self.alphabetArray.count; i++)
{
if (section==i) // check for right section
{
sectionTitle= [self.alphabetArray objectAtIndex:i]; //getting section title
for (MPMediaItem *song in songs)
{
NSString *title = [song valueForProperty:MPMediaItemPropertyTitle];
rowTitle= [title substringToIndex:1]; //modifying the statement to its first alphabet
if ([rowTitle isEqualToString:sectionTitle]) //checking if modified statement is same as section title
{
[newArray addObject:title]; //adding the row contents of a particular section in array
}
}
}
}
return newArray;
}
But this seems very wrong and I'm really confused on how to solve this. I have no clue how to create a separate array with these methods, populating newArray with the song titles, and I've searched Google for quite some time now and I can't find anything that would help me.
Could somebody point me in the right direction, or please show me how I'd creat newArray? I've attached my tableView data source here. Thanks. Any help would be much appreciated.
Table View cells are enqueued and dequeued in a serial manner. Leveraging this ability you should expect that only sections change in less frequency. You can use static variables to exempt the fetching of a section. Look at my code
-(NSString *)titleForRow:(NSIndexPath *)indexpath{
static NSIndexPath *funIndexPath;
static NSMutableArray *funSectionArray;
if (!funIndexPath || funIdexpath.section != indexpath.section) {
funIndexPath = indexpath;
funSectionArray = [self getArrayOfRowsForSection:indexpath.section];
}
rowArray = funSectionArray;
NSString *titleToBeDisplayed = [rowArray objectAtIndex:indexpath.row];
return titleToBeDisplayed;
}
This is the first optimisation possible.
When I look at your fetching results array, some things are not clear. Why are you iterating to check section == i. You can just substitute section for i in your code.
-(NSMutableArray *)getNewArray:(NSInteger)section {
NSString *rowTitle;
NSString *sectionTitle;
sectionTitle = [self.alphabetArray objectAtIndex:section]; //getting section title
for (MPMediaItem *song in songs) {
NSString *title = [song valueForProperty:MPMediaItemPropertyTitle];
rowTitle= [title substringToIndex:1]; //modifying the statement to its first alphabet
if ([rowTitle isEqualToString:sectionTitle]) //checking if modified statement is same as section title
{
[newArray addObject:title]; //adding the row contents of a particular section in array
}
}
return newArray;
}
Its still not clear what you are trying to achieve here.
Going against my own answer, I suggest you that you do not compute you data for display during view rendering time. You need to create the cells as fast as possible. Why not create a property or an iVar (like #bauerMusic is Suggesting) and compute the date in viewDidLoad or declare a custom function which will reload your property (possible an Array or Dictionary) and while rendering the UI, just set the values (in you viewForIndexPath)?
You can use an array of two level depth to store for maintaining sections and rows. i.e. Level 1 - Sections and Array of rows, Level 2 - row.
The center of it all is:
cell.textLabel.text = [self titleForRow:indexPath];
Now, what you're actually using are NSString. Simple. Instead, you're creating a new array for each cell each call??
Why not have one array, say an NSMutableArray. Have it as a #property or local iVar.
#property (strong, nonatomic) NSMutableArray *rowArray;
// Instantiate in -viewDidLoad
rowArray = [NSMutableArray array]; // Much more elegant IMHO
Now call a method that do all the content loading, but do it ONCE. You can always call it again to refresh.
Something like:
// In -viewDidLoad
rowArray = [NSMutableArray array];
[self loadArrayContent];
Then, your cellForRowAtIndexPath should only be using your preloaded rowArray and simply pulling NSString titles from it.
My general way to handle a table view is to create (during table view setup) an NSMutableArray for the rows (anchored in the data source object). Each entry in the array is an NSMutableDictionary that is "primed" with the basic stuff for the row, and if more info needs to be developed to display the row, that is cached in the dictionary. (Where there are multiple sections I use an array of sections containing arrays of rows.)
What I might do for a play list is initialize the array with empty dictionaries, then, as one scrolls, access each play list element and cache it in the dictionary.
In fact, if one really wanted, updating the dictionary could be done via a background task (with appropriate locking) so that scroll speed wouldn't be hampered by the lookup operations.

add one NSMuatblearray to another NSMutablearray?

i have two nsmutableArray . Arr_jsondata and second is tempArray.
Already Arr_jsondata contain the data from json link and display in tableview .
i want to add the tempArray data into Arr_jsondata and reload the table using Arr_jsondata , because tableview delegate is also used this array to display the data in tableview .
but its always give me an error when i add temp array data into arr_jsondata .
for (int i=0; i<[Temp_arr_JsonData count]; i++)
{
NSString *str_brandname = [[Temp_arr_JsonData objectAtIndex:i] valueForKey:#"storebrand"];
NSLog(#"%#",str_brandname);
NSObject *myNewObject = [[NSObject alloc] init];
if ([str_brandname isEqualToString:#"Nike"])
{
NSLog(#"Data matched");
c++;
myNewObject = [Temp_arr_JsonData objectAtIndex:i];
[temparray addObject:myNewObject];
}
}
// arr_JsonData=temparray;
[[arr_JsonData arrayByAddingObjectsFromArray:temparray] mutableCopy];
NSLog(#"%#",temparray);//display all nike related data
NSLog(#"%#",arr_JsonData);//display null array .
[self.tableView reloadData];
[sender setSelected:YES];
in last i want only tempdata into Arr_jsondata ..... what can i do ?
Just add objects from array. See this apple's doc
if (arr_JsonData.count > 0)
[arr_JsonData addObjectsFromArray: (NSArray*)temparray];
else
arr_JsonData = [NSMutableArray arrayWithArray:(NSArray*)temparray]
You can try this
[arr_JsonData addObjectsFromArray:tempArray];
I think arr_JsonData is not NSMutableArray actually which can be judged from the exception description you listed in the answer. Try this:
arr_JsonData = [NSMutableArray arrayWithArray:arr_JsonData];
[arr_JsonData addObjectsFromArray: temparray];
Try using this:
NSMutableArray *ar = [NSMutableArray alloc] initWithArray:ar1];
Just have to initialize array with other array and it will have all contents from other array.
Or this one:
NSMutableArray *ar = [NSMutableArray alloc] initWithArray:ar1 copyItems:YES];
mutableCopy is defined for NSArray not for NSMutableArray
if ([arr_JsonData count] > 0)
{
[arr_JsonData addObjectsFromArray: temparray];
}

Resources