Put last 4 objects of an ascending array into UITableView - objective-c

My array (trips) grows depending on how many entries a user has made. Currently the array is in ascending order from the database.
I need to have it start at the last object and populate my tableview with the last 4 'trips' objects from the array.
My tableview only shows 4 rows, I want those rows to be the last 4 from the array.
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if ([trips count] < 4){
return [trips count]
} else {
return 4;
}
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *MyIdentifier = #"MyReuseIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:MyIdentifier];
}
UserMiles *cellInfo = [self milesAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%# to %#", cellInfo.beg_school, cellInfo.end_school];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc]init];
dateFormat.dateFormat = #"MM/dd/yyyy";
NSString *date = [dateFormat stringFromDate:cellInfo.driven_date];
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#", date];
return cell;
}
-(UserMiles *) milesAtIndex: (NSInteger) index
{
return [trips objectAtIndex:index];
}

You can use a NSSortDescriptor to get the array into the order you want. The quickest fix would be to just take your milesAtIndex: and change it to:
-(UserMiles *) milesAtIndex: (NSInteger) index
{
NSSortDescriptor *sort = [[NSSortDescriptor sortDescriptorWithKey:#"propertyHere" ascending:NO];
return [[trips sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]] objectAtIndex:index];
}
Note that #"propertyHere" can be any property of your UserMiles object that you want to sort by. So if you are using a date for example, you might have a myUserMiles.tripDate property, so you'd replace #"propertyHere" with #"tripDate".

I utilized the reverseObjectEnumerator class (which I had no idea existed!) to get it done.
It now works great!
trips = [[trips reverseObjectEnumerator] allObjects];

Related

Unable to sort NSMutableArray of Custom Objects

I've got an issue trying to sort an array of custom objects. It's looking as if my arrays aren't even hitting the sorting code, but rather simply just returning the array itself.
I have the following setup:
SearchResult : NSObject
--
Document : SearchResult
Tag : SearchResult
Folder : SearchResult
My code is getting returns as SearchResults then trying to compare them all with a key, name that is defined in the SearchResult implementation.
-(void) parseFolderContents:(NSDictionary *) data
{
NSMutableArray *searchResults = [[NSMutableArray alloc] init];
NSMutableArray *documents = [[NSMutableArray alloc] init];
NSMutableArray *folders = [[NSMutableArray alloc] init];
NSMutableArray *tags = [[NSMutableArray alloc] init];
NSArray *results = [data objectForKey:#"items"];
for (int i = 0; i < [results count]; i++)
{
SearchResult *result = (SearchResult *)[KTParser parseSearchResult:[results objectAtIndex:i]];
if ([result.type isEqualToString:#"document"]){
[documents addObject:result];
}
else if ([result.type isEqualToString:#"folder"])
{
[folders addObject:result];
}
else if ([result.type isEqualToString:#"tag"])
{
[tags addObject:result];
}
}
if ([documents count] > 0)
[searchResults addObject:documents];
if ([folders count] > 0)
[searchResults addObject:folders];
if ([tags count] > 0)
[searchResults addObject:tags];
....
So that's the code used to populate the array, which isn't anything special. I have tried each of these ways to compare the array. None have worked. Does anyone know where I'm going wrong?
First attempt:
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedResults;
sortedResults = [searchResults sortedArrayUsingDescriptors:sortDescriptors];
Second attempt:
NSArray *sortedResults;
sortedResults = [searchResults sortedArrayUsingSelector:#selector(compare:)];
(implementing custom compare method on SearchResult/Document.m)
- (NSComparisonResult)compare:(SearchResult *)otherResult{
return [self.name compare:otherResult.name];
}
Third attempt:
sortedResults = (NSMutableArray *)[searchResults sortedArrayUsingDescriptors:[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"name" ascending:YES selector:#selector(caseInsensitiveCompare:)]]];
Fourth attempt I tried using a block. I even tried putting code in to manipulate the sorting of it, which didn't work either. The array returned was exactly the same as the original:
sortedArray = [searchResults sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
if ([(SearchResult *)a itemId] < 20000 )
return NSOrderedAscending;
else
return NSOrderedDescending;
}];
Anyone have any ideas?
Found out the issue - I was adding arrays as objects instead of adding each object into the array. This was then calling sort on NSArray, instead of my custom objects. Changing the above code from [searchResults addObject:documents]; to [searchResults addObjectsFromArray:documents]; solved the issue!

uisearchbar uisearchdisplaycontroller tableview subtitle error

I have a little problem with my searchbar for a tableview
I have made a tableview with an array tableViewArray.
The tableViewArray consists of many rows of another array consisting of [text, distance].
Everything is working fine.
Now i added a searchBar and a searchdisplaycontroller, that searched based on a new array of string (from the "text" object of the tableViewArray).
I thought the search should only be available for the text, and the search method is implemented on that.
Now when i get the search result, it looks good, and the search returns expected rows. The problem is with the search tableViews subtitle. It is showing the distances for row 1, 2, 3 for the tableViewArray.
I need it to map the distance to the text shown in the search tableview rows.
I imagine i need to make a new table view array for the search results consisting of [text distance]. the text is not a problem since it is from the search result, but how do i map the new distance to the old distance???
The search method im using in the search delegate is :
searchResults = [[NSArray alloc]init];
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:#"SELF contains[cd] %#",
searchText];
searchResults = [searchItems filteredArrayUsingPredicate:resultPredicate];
Hope somebody can help :) Thanks in advance!
The original code:
- (NSInteger)tableView:(UITableView *)theTableView numberOfRowsInSection:(NSInteger)section
{
if (theTableView == self.searchDisplayController.searchResultsTableView) {
return [searchResults count];
} else {
return [tableViewArray count];
}
}
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle
reuseIdentifier: CellIdentifier] ;
if (theTableView == self.searchDisplayController.searchResultsTableView) {
} else {
cell = [theTableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
}
// Configure the cell...
// cell.textLabel.numberOfLines = 0;
// cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
if (theTableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [searchResults objectAtIndex:indexPath.row];
} else {
cell.textLabel.text = [[[tableViewArray objectAtIndex:indexPath.row] objectAtIndex:0] subtitle];
}
cell.detailTextLabel.textColor = [UIColor redColor];
float blabla= [[[tableViewArray objectAtIndex:indexPath.row] objectAtIndex: 1] doubleValue];
if (blabla < 1000) {
cell.detailTextLabel.text = [NSString stringWithFormat:#"%.2f m",blabla];
} else {
cell.detailTextLabel.text = [NSString stringWithFormat:#"%.2f km",blabla/1000];
}
NSString *text = [[[tableViewArray objectAtIndex:indexPath.row] objectAtIndex:0] subtitle];
NSRange q8RangeValue = [text rangeOfString:#"Q8" options:NSCaseInsensitiveSearch];
NSRange okRangeValue = [text rangeOfString:#"OK" options:NSCaseInsensitiveSearch];
if (q8RangeValue.length >0 ) {
cell.imageView.image = [UIImage imageNamed:#"q8.png"];
} else if (okRangeValue.length >0 ) {
cell.imageView.image = [UIImage imageNamed:#"OK logo.png"];
} else {
cell.imageView.image = nil;
}
return cell;
}
And where i make the array for the search:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSLog(#"didUpdateToLocation Location calculator distance array for the tableView");
NSMutableArray * distancesInReverseOrder = [[NSMutableArray alloc] init];
for (int i = 0; i<allAnnotations.count; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i inSection:0] ;
CLLocationCoordinate2D annotationCoord = [[allAnnotations objectAtIndex:indexPath.row] coordinate];
CLLocation *location = [[CLLocation alloc] initWithLatitude:annotationCoord.latitude longitude:annotationCoord.longitude];
distanceToMe = [newLocation distanceFromLocation:location];
[distancesInReverseOrder insertObject:[NSNumber numberWithFloat: distanceToMe] atIndex:0];
}
distances = [[NSMutableArray alloc] initWithArray:[[distancesInReverseOrder reverseObjectEnumerator] allObjects]];
// Assuming you have your points on the map in an NSArray called
// allAnnotations and your distances in distances, create a
// new mutable array to hold both
tableViewArray = [[NSMutableArray alloc] init];
// Iterate over all of the points, and add a new element to the mutable
// array which is a new array containing a point and its distance
for (int i = 0; i < allAnnotations.count; i++) {
NSArray *newItem = [NSArray arrayWithObjects: [allAnnotations objectAtIndex: i], [distances objectAtIndex: i], nil];
[tableViewArray addObject: newItem];
}
// Now, sort the new array based upon the distance in the second element
// of each array (ie, the distance).
[tableViewArray sortUsingComparator: ^(id obj1, id obj2) {
NSNumber *dist1 = [obj1 objectAtIndex:1];
NSNumber *dist2 = [obj2 objectAtIndex:1];
return [dist1 compare:dist2];
}];
searchResults = [NSMutableArray arrayWithCapacity:[tableViewArray count]];
searchItems = [[NSMutableArray alloc] init];
for (int i = 0; i < allAnnotations.count; i++) {
NSArray *newItem = [NSArray arrayWithObjects: [[[tableViewArray objectAtIndex:i] objectAtIndex:0] subtitle], #"bla", nil];
[searchItems addObject: newItem];
}
/*
for (int i=0; i<tableViewArray.count; i++) {
[searchItems insertObject:[[[tableViewArray objectAtIndex:i] objectAtIndex:0] subtitle] atIndex:0];
}
*/
NSLog(#"searchitems count is %i", searchItems.count);
[tableView reloadData];
[locationManager stopUpdatingLocation];
}
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
/*
// Update the filtered array based on the search text and scope.
// Remove all objects from the filtered search array
[searchResults removeAllObjects];
// Filter the array using NSPredicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF.name contains[c] %#",searchText];
// searchResults = [NSMutableArray arrayWithArray:[[tableViewArray objectAtIndex:1] filteredArrayUsingPredicate:predicate]];
*/
/*
searchResults = [[NSArray alloc]init];
NSPredicate *resultPredicate = [NSPredicate
predicateWithFormat:#"SELF contains[cd] %#",
searchText];
searchResults =[searchItems filteredArrayUsingPredicate:resultPredicate];
*/
// Create index set of all objects in textArray that contain searchText:
NSIndexSet *set = [searchItems indexesOfObjectsPassingTest:
^BOOL(NSString *text, NSUInteger idx, BOOL *stop) {
NSRange range = [text rangeOfString:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch)];
return (range.location != NSNotFound);
}];
// Filter textArray:
filteredTextArray = [searchItems objectsAtIndexes:set];
// Filter distanceArray:
filteredDistanceArray = [distances objectsAtIndexes:set];
NSLog(#"filtered text array is %#", filteredTextArray);
NSLog(#"filtered distance array is %#",filteredDistanceArray);
If I understand your problem correctly, you have 2 separate arrays that are used as data source for the table view, let's call them textArray and distanceArray.
Now you filter the textArray according to the search string and you need the "corresponding" filtering of distanceArray.
One way to do this is to replace filteredArrayUsingPredicate with indexesOfObjectsPassingTest, because that returns a set of matching indices that can be applied to both arrays:
// Create index set of all objects in textArray that contain searchText:
NSIndexSet *set = [textArray indexesOfObjectsPassingTest:
^BOOL(NSString *text, NSUInteger idx, BOOL *stop) {
NSRange range = [text rangeOfString:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch)];
return (range.location != NSNotFound);
}];
// Filter textArray:
filteredTextArray = [textArray objectsAtIndexes:set];
// Filter distanceArray:
filteredDistanceArray = [distanceArray objectsAtIndexes:set];
Now you can use filteredTextArray and filteredDistanceArray as data source for the search table view.
Alternatively, you could use a single array as data source if each object in the array is for example a dictionary containing both text and distance for one row.
UPDATE: As I understand it now, each item of your tableViewArray is an array with 2 items (one for the text and one for the distance).
In this case I would recommend to filter the tableViewArray directly:
NSIndexSet *set = [tableViewArray indexesOfObjectsPassingTest:
^BOOL(NSArray *item, NSUInteger idx, BOOL *stop) {
NSString *subtitle = [[item objectAtIndex:0] subtitle];
NSRange range = [subtitle rangeOfString:searchText options:(NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch)];
return (range.location != NSNotFound);
}];
searchResults = [tableViewArray objectsAtIndexes:set];
Now searchResults is the filtered array, and each item has the same structure as the items in tableViewArray.
This simplifies things in cellForRowAtIndexPath, e.g.
if (theTableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [[[searchResults objectAtIndex:indexPath.row] objectAtIndex:0] subtitle];
distance = [[[searchResults objectAtIndex:indexPath.row] objectAtIndex: 1] doubleValue];
} else {
cell.textLabel.text = [[[tableViewArray objectAtIndex:indexPath.row] objectAtIndex:0] subtitle];
distance = [[[tableViewArray objectAtIndex:indexPath.row] objectAtIndex: 1] doubleValue];
}

Scroll to certain point in tableview

I have a tableview that is filled up with dates. My section header is the month name. You can see my tableview over here.
What I want is that it scrolls to the section of the month of that moment. For setting my section headers I use this method.
id <NSFetchedResultsSectionInfo> theSection = [[self.fetchedResultsController sections] objectAtIndex:section];
static NSArray *monthSymbols = nil;
NSArray *dutchMonths = [[NSArray alloc]initWithObjects:#"Januari",#"Februari",#"Maart",#"April",#"Mei",#"Juni",#"Juli",#"Augustus",#"September",#"Oktober",#"November",#"December", nil];
if (!monthSymbols) {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setCalendar:[NSCalendar currentCalendar]];
[formatter setMonthSymbols:dutchMonths];
monthSymbols = [formatter monthSymbols];
}
NSLog(#"%#",monthSymbols);
NSInteger numericSection = [[theSection name] integerValue];
NSInteger year = numericSection / 1000;
NSInteger month = numericSection - (year * 1000);
NSString *titleString = [NSString stringWithFormat:#"%#", [monthSymbols objectAtIndex:month-1]];
label.text = titleString;
I know already that I have to use this method.
[sampleListTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
But how do I get the indexpath of the correct row?
Any help? If you need more details. Please help.
Kind regards.
Looks like you can get an index by looping through your sections array (from self.fetchedResultsController) and comparing the month obtained from the object in question to the month from the current date. If you have a match, then your indexPath would be:
NSIndexPath *path = [NSIndexath indexPathForRow:0 inSection:foundIndex];
You should also make the dutchMonths array static so it isn't created every time that method is called. Also, if you aren't using ARC, it's leaking (unless the release code is not posted). A general rule of thumb is to make the date formatter static too, or manage one instance of it in some way, because it is an expensive operation. I know with this code it is only created once since you only use it to populate the monthSymbols array, but if you need to use the same formatter in other code, then you'll want to rewrite that.
To do all of this, you should extract the logic in this method and put it in smaller, reusable methods like
- (NSInteger)monthFromSection:(id<NSFetchedResultsSectionInfo>)section;
Then you can write:
NSIndexPath *path = nil;
NSInteger currentMonth = // Calculate month from date returned by [NSDate date]
NSArray *sections = [self.fetchedResultsController sections];
for (int i = 0; i < sections.count; i++)
{
if (currentMonth = [self monthFromSection:[sections objectAtIndex:i]])
{
path = [NSIndexPath indexPathForRow:0 inSection:i];
break;
}
}

indexPath.row for whole tableview, not only section?

I have a tableView wich displays content from an array with objects. But the tableview have sections for each date, and I get the same content in all the sections. I guess NSDictionary *object = [array objectAtIndex:indexPath.row]; returns the row for the section and not for the total array. How can I get the row count for the whole tableView? Or is it some other way to do it?
int rowsOffset = 0;
for (int section; section ++; section < indexPath.section) {
rowsOffset += [[[titles objectAtIndex:section] objectForKey:#"Values"] count];
}
NSDictionary *object = [array objectAtIndex:indexPath.row+rowOffset];
probably better:
NSArray *theArray = [[titles objectAtIndex:indexPath.section] objectForKey:#"Values"];
NSDictionary *object = [theArray objectAtIndex:indexPath.row];

[NSCFNumber isEqualToString:]: unrecognized selector sent to instance

Alright, I'm having the following common problem
[NSCFNumber isEqualToString:]: unrecognized selector sent to instance
but this time I'm not sure how to fix it.
Here's the declaration in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *tempHours = [NSMutableArray array];
for (int i = 0; i < 12; i++) {
[tempHours addObject:[NSNumber numberWithInt:(i+1)]];
}
self.hours = tempHours; // 'hours' is a synthesized NSArray property of the ViewController
[tempHours release];
// two more similar array declarations here
}
Here's the code method of the UIPickerView where stuff breaks (e.g., the if statement)
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
NSString *stringIndex = [NSString stringWithFormat:#"Row #%d", row];
if(component == 0) {
return stringIndex = [self.hours objectAtIndex:row];
}
// more code for other components (of the same form) here
return stringIndex;
}
I think I need my NSArray of NSNumber objects to be type-casted as strings. How do I do that properly with that statement:
stringIndex = [self.hours objectAtIndex:row];
Thanks!
return [NSString stringWithFormat:#"%#",[self.hours objectAtIndex:row]];
You are returning an NSNumber as that is what is held in self.hours. As NSString is the expected return value you should create a string via:
[NSString stringWithFormat:#"%#", [self.hours objectAtIndex:row]];
or reevaluate your intent. Did you actually want to store indices in this way, or did you want to store NSStrings?
If anyone is having this problem and specifically returning an index of a row, then you can always convert the NSNumber to a stringValue by doing the following:
NSString *code = [[[JSONResponse objectForKey:#"meta"] objectForKey:#"code"] stringValue];
Placing a stringValue at the end of the method will convert anything to a string, you can also use intValue.
Comment [tempHours release];//it is autoreleased object. You didn't use and with alloc or copy or new:
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *tempHours = [NSMutableArray array];
for (int i = 0; i < 12; i++) {
[tempHours addObject:[NSNumber numberWithInt:(i+1)]];
}
self.hours = tempHours; // 'hours' is a synthesized NSArray property of the ViewController
// [tempHours release];
}
more over u have added NSNumber to array
so
convert to string value:
stringIndex =[NSString stringWithFormat:#"%#",[self.hours objectAtIndex:row]];