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
Related
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]
}];
}
I have a table view that pulls data from my database, and displays it in my tableview cells. The values returned are:
This is the messages data (
{
body = "13-10-2017 12:12";
name = "Leeya";
},
{
body = "09-10-2017 19:37";
name = Leeya;
},
{
body = "test";
name = "Jessica";
}
That said, when my dictionary comes back with multiple arrays containing the same 'name' value (as it is in this case, 'Leeya'), I only want my tableview to display the first of these arrays (one cell), much like in a chat app when you see a list of conversations.
The code I have currently accomplishes this, but it literally only returns ONE cell and ONE array, populated by the most recently posted data. E.g. tableview returns cell:
LEEYA
When it should return:
LEEYA
JESSICA
See my code below. I assume this is because the line
NSString *nodeTitle = self.messages[0][#"name"];
is always equal to one username, and thus it returns one value.
So my question is: How can I make that line function so that, "If 'name' value in dictionary self.messages appears more than once, only show the first value" ? Or in other words: If 'name' value is equal to 'name' in arrays that follow, only show the first?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *nodeTitle = self.messages[0][#"name"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"name == %#", nodeTitle];
NSArray *filteredArray = [self.messages filteredArrayUsingPredicate:predicate];
id firstFoundObject = nil;
firstFoundObject = filteredArray.count > 0 ? filteredArray.firstObject : nil;
NSDictionary *firstObjects = firstFoundObject;
NSLog(#"The first objects are %#", firstObjects);
static NSString *PointsTableIdentifier = #"MyMessagesCell";
MyMessagesCell *cell = (MyMessagesCell *)[tableView dequeueReusableCellWithIdentifier:PointsTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MyMessagesCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
I hope I explained this clearly. Thanks!
I would suggest not trying to do this in cellForRowAtIndexPath.
Instead, when you first get your data from the server, write a method that takes the input array and filters it to remove duplicates. Something like this:
- (NSArray *) UniqueEntriesForArray: (NSArray *) sourceArray {
NSMutableArray *results = [NSMutableArray array];
NSMutableSet *uniqueStrings = [NSMutableSet new];
for (NSDictionary *element in sourceArray) {
NSString *name = element[#"name"];
if (![uniqueStrings contains: name]) {
[uniqueStrings addObject: name];
[results addObject: element];
}
}
return [results copy];
}
(My Objective-C is getting a little rusty so that might need some adjustment. It would also be cleaner if you used the new typed array syntax for Objective-C, but I don't remember that off the top of my head.)
In my project i get response data in the form of
{
"procedures": [5950]
0: {
"Procedures": {
"id": "1"
"procedure_name": "3d render w/o postprocess"
"procedure_code": "76376"
}
}
1: {
"Procedures": {
"id": "2"
"procedure_name": "3d rendering w/postprocess"
"procedure_code": "76377"
}
there are 5950 elements in the data array. i create separate arrays for "id" and "Procedure_name" and show data in UITableView it displays correct data with id.
I also applied search functionality to filter data as it is so difficult to find any element in 5950 elements by scrolling.
search function is also works well but when i select any UITableViewCell of the filtered result it doesn't provide the actual id of that element whereas it returns the current indexpath value of the UITableView.
for searching i put the following code
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
tempArray = [NSArray arrayWithArray:dataArray];
NSString *stringToSearch = textField.text;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF BEGINSWITH[c] %#",stringToSearch]; // if you need case sensitive search avoid '[c]' in the predicate
NSArray *tempresults = [dataArray filteredArrayUsingPredicate:predicate];
if (tempresults.count > 0)
{
tempArray = [NSArray arrayWithArray:tempresults];
}
[searchdiagtable reloadData];
return YES;
}
in DidSelectRow method i apply the following code
if (tableView == searchdiagtable)
{
UITableViewCell *selectedCell = [searchdiagtable cellForRowAtIndexPath:indexPath];
NSLog(#"%#", selectedCell.textLabel.text);
searchdiag. text = selectedCell.textLabel.text;
searchResultId = [iddict objectAtIndex:indexPath.row]; // iddict is the array where i store the values of "id" from dictionary.
[searchdiagtable deselectRowAtIndexPath:indexPath animated:YES];
searchdiagtable.hidden = YES;
}
checking on your question which I can get is issue must be using a wrong array iddict for search as it looks to me its should be tempArray
searchResultId = [iddict objectAtIndex:indexPath.row];
This line of code creates the problem, tableView reuses cells.
if you selected the cell , it selects the cell index (0, 1, 2 ... etc). So your array returns the first values.
If you are not displaying the "Id" in your table view. You cant get correct id from your array.
There is one solution for this,save id and name in a single dictionary,
like:{
"id":"name"
}
using the name you can get the id from dictionary.
hey you already get two arrays , one for ids and one for names.
How you do that..after response may be you did like this
NSMutableArray *id;
NSMutableArray *name;
for( ------ ){
[id addObject:[dict objectforkey:#"id"];
[name addObjec:[dict objectForKey:#"procedure_name"];
}
dict is reponse dictionary.
This is the way how you get arrays.now for dictionary
NSMutableDictionary *dict1 = [[NSMutableDictionary alloc]init];
for(){
[dict setObject:[dict objectforkey:#"id"] forKey:[dict objectForKey:#"procedure_name"];
}
just check the for loop carefully in your code..
i just get an easy way to create A:B type dictionary
i am sharing my code i used and works perfectly for me
NSDictionary * searchdict = [NSDictionary dictionaryWithObjects:dataArray forKeys:iddict];
NSLog(#"dict formed by combining %#",searchdict);
where dataarray and iddict are two arrays
it gives output dictionary as
1034 = "Catheterize for urine spec";
1035 = "Cauterization of cervix";
1036 = "CBC without platelet";
1037 = "CBC/diffwbc w/o platelet";
1038 = "Cbt 1st hour";
1039 = "Cbt each addl hour";
104 = "Amino acids quan 6 or more";
1040 = "Ccp antibody"; ......
yaa, I know we can directly add two arrays to dictionary. But it takes memory for two arrays and dictionary also.. so instead of creating two arrays.you can directly add it to dictionary. But what you did is also correct. In mobile applications every time we have to think about memory management also..
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.
I am trying to display sections and rows correctly for my uiTableView.
I have had great help from one contributor and am fairly close to fixing my issue. The Issue can be seen here. Its not far off being right, its just the sections that need to be sorted.
It is repeating the section titles instead of only showing it once. Im not sure exactly how to fix this.
// Find out the path of recipes.plist
NSString *path = [[NSBundle mainBundle] pathForResource:#"lawpolice" ofType:#"plist"];
// Load the file content and read the data into arrays
self.dataArray = [NSArray arrayWithContentsOfFile:path];
//Sort the array by section
self.sortedArray = [self.dataArray sortedArrayUsingDescriptors:#[
[NSSortDescriptor sortDescriptorWithKey:#"Section" ascending:YES],
[NSSortDescriptor sortDescriptorWithKey:#"Title" ascending:YES]]];
self.temp = [[NSMutableDictionary alloc] init];
for (NSDictionary *dict in self.sortedArray) {
NSMutableArray *array = self.temp[dict[#"Section"]];
// No items with the same section key stored yet, so we need to initialize a new array.
if (array == NULL) {
array = [[NSMutableArray alloc] init];
}
// Store the title in the array.
[array addObject:dict[#"Title"]];
// Save the array as the value for the section key.
[self.temp setObject:array forKey:dict[#"Section"]];
}
self.policePowers = [self.temp copy]; // copy returns an immutable copy of temp.
//Section for sorting
self.sectionArray = [self.sortedArray valueForKeyPath:#"Section"];
NSLog(#"%#", self.sectionArray);
//Title
self.namesArray = [self.sortedArray valueForKeyPath:#"Title"];
//Offence
self.offenseArray = [self.sortedArray valueForKeyPath:#"Offence"];
//Points to Prove
self.ptpArray = [self.sortedArray valueForKeyPath:#"PTP"];
//Action
self.actionsArray = [self.sortedArray valueForKeyPath:#"Actions"];
//Notes
self.notesArray = [self.sortedArray valueForKeyPath:#"Notes"];
//Legislation
self.legislationArray = [self.sortedArray valueForKeyPath:#"Legislation"];
//PNLD
self.pnldArray = [self.sortedArray valueForKeyPath:#"PNLD"];
//Image
self.imageString = [self.sortedArray valueForKeyPath:#"image"];
titleForHeaderInSection
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return [self.sectionArray objectAtIndex:section];
}
numberOfSectionsInTableView
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.policePowers count];
}
numberOfRowsInSection
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSArray *sectionrows = self.policePowers[self.sectionArray[section]];
return [sectionrows count];
}
Update
To be clear, if two items have the same Section value, I want to automatically group them into an array and have that array mapped to the Section value at the end
NSDictionary dictionaryWithObjects:forKeys: basically loops through two arrays and maps the object in one array at the current index as the key for the object in the other array at the same index. When you're calling
self.policePowers = [NSDictionary dictionaryWithObjects:self.namesArray forKeys:self.sectionArray];
it therefore maps the items in self.sectionArray as the keys for the items in self.namesArray. Looking at your plist file, the "Title" keypath (which is mapped to self.namesArray) has a value of string, so your NSLog results make sense, as self.namesArray is an array of strings, not an array of arrays.
I'm not sure how you were supposed to get a result like
"Alcohol: Licensing/Drive unfit" = {
"Drive/attempt to drive/in charge whilst unfit or over",
"Drive/attempt to drive/in charge whilst unfit or over",
"Drive/attempt to drive/in charge whilst unfit or over",
}
Where is that array supposed to come from?
-- EDIT --
I don't think there's a concise way to accomplish what you want, so it'd have to be done manually. I haven't actually used [NSArray arrayWithContentsOfFile:path] before, so is self.dataArray an array of dictionaries with each item representing one of the items in the plist (Item 44, Item 45, etc)? If so, you could do something like this:
NSMutableDictionary *temp = [[NSMutableDictionary alloc] init];
for (NSDictionary *dict in self.dataArray) {
NSMutableArray *array = temp[dict[#"Section"]];
// No items with the same section key stored yet, so we need to initialize a new array.
if (array == null) {
array = [[NSMutableArray alloc] init];
}
// Store the title in the array.
[array addObject:dict[#"Title"]];
// Save the array as the value for the section key.
[temp setObject:array forKey:dict[#"Section"]];
}
self.policePowers = [temp copy]; // copy returns an immutable copy of temp.
-- EDIT AGAIN --
The app crashes because self.policePowers is an NSDictionary, not an NSArray; thus it doesn't have an objectAtIndex: method. If you're trying to get the section title, try this instead:
return [self.sectionArray objectAtIndex:section];
Furthermore, if you're working with a table view, I'd basically have self.sectionArray sorted whichever way you like, then whenever I needed to populate data in each section, I would use self.policePowers[self.sectionArray[section]] to return the array of titles mapped to that section title.
-- YET ANOTHER --
If you break it up into the following lines, where is the NSRangeException thrown? If you NSLog, do the results match what you expect?
NSString *title = self.sortedKeys[indexPath.section];
NSArray *array = self.policePowers[title];
NSString *value = array[indexPath.row];