Sorting an array of NSIndexPaths - nsmutablearray

I have an NSMutableArray that contains NSIndexPath objects, and I'd like to sort them by their row, in ascending order.
What's the shortest/simplest way to do it?
This is what I've tried:
[self.selectedIndexPaths sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSIndexPath *indexPath1 = obj1;
NSIndexPath *indexPath2 = obj2;
return [#(indexPath1.section) compare:#(indexPath2.section)];
}];

You said that you would like to sort by row, yet you compare section. Additionally, section is NSInteger, so you cannot call methods on it.
Modify your code as follows to sort on the row:
[self.selectedIndexPaths sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSInteger r1 = [obj1 row];
NSInteger r2 = [obj2 row];
if (r1 > r2) {
return (NSComparisonResult)NSOrderedDescending;
}
if (r1 < r2) {
return (NSComparisonResult)NSOrderedAscending;
}
return (NSComparisonResult)NSOrderedSame;
}];

You can also use NSSortDescriptors to sort NSIndexPath by the 'row' property.
if self.selectedIndexPath is non-mutable:
NSSortDescriptor *rowDescriptor = [[NSSortDescriptor alloc] initWithKey:#"row" ascending:YES];
NSArray *sortedRows = [self.selectedIndexPaths sortedArrayUsingDescriptors:#[rowDescriptor]];
or if self.selectedIndexPath is a NSMutableArray, simply:
NSSortDescriptor *rowDescriptor = [[NSSortDescriptor alloc] initWithKey:#"row" ascending:YES];
[self.selectedIndexPaths sortedArrayUsingDescriptors:#[rowDescriptor]];
Simple & short.

For a mutable array:
[self.selectedIndexPaths sortUsingSelector:#selector(compare:)];
For an immutable array:
NSArray *sortedArray = [self.selectedIndexPaths sortedArrayUsingSelector:#selector(compare:)]

In swift:
let paths = tableView.indexPathsForSelectedRows() as [NSIndexPath]
let sortedArray = paths.sorted {$0.row < $1.row}

Related

Setting a predicate for different tableviews

This one has been a big problem for me, and i´m still stuck with it so i was hopping that someone could give me some kind of guidance.
What i have is:
3 tableviews with multiple cells and each cell with several textfields.
1 tableview that appears inside a popover every time a specific textfield on those cells is pressed. This tableview has all!! the core data methods to retrieve the necessary data from my database.
Everything works ok...but i need to distinguish what kind of data shall appear in tableview 1 or 2 or 3...So i know i have to use predicate!.
What i have done: ( and i have tried other things)
- (NSFetchedResultsController *)fetchedResultsController
{
if (_fetchedResultsController == nil)
{
NSFetchRequest *fetchRequestList = [[NSFetchRequest alloc] init];
NSEntityDescription *entityList = [NSEntityDescription entityForName:#"List" inManagedObjectContext:self.managedObjectContext];
[fetchRequestLista setEntity:entityList];
TableViewOne *table1 = [[Cobertura alloc]init];
TableViewTwo *table2 = [[Cobertura alloc]init];
if (table1 textFieldShouldBeginEditing:table1.textFieldPressed)
{
fetchRequestList.predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview1];
}
if (table2 textFieldShouldBeginEditing:table2.textFieldPressed)
{
fetchRequestList.predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview2];
}
NSSortDescriptor *cellTitle = [[NSSortDescriptor alloc] initWithKey:#"reference" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:cellTitle, nil];
[fetchRequestLista setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequestLista managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"referencia" cacheName:nil];
_fetchedResultsController.delegate = self;
self.fetchedResultsController = _fetchedResultsController;
return _fetchedResultsController;
}
In each of my tableviews, i have an instance of the "popoverTableview" in my method textFieldShouldBeginEditing:
popoverTableview = [self.storyboard instantiateViewControllerWithIdentifier:#"popoverTableview"];
popover = [[UIPopoverController alloc] initWithContentViewController:popoverTableview];
[popover presentPopoverFromRect:textField.bounds inView:textField permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
popoverTableview.delegate = self;
popoverTableview.popView = self.popover;
So, if i´m in tableview1 i need to get [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview1];
Should i be creating some kind of method that my tableviewS could access? What am i forgetting here or not paying attention?
Thanks in advance, and any kind of advise would be welcome!
Regards
For everyone that was experiencing the same problem that i was, here is what i have done to resolve:
This is when i´m creating the popoverview when a specific textfield is pressed:
popoverTableview = [self.storyboard instantiateViewControllerWithIdentifier:#"popoverTableview"]initWithTextFieldTag:myTextFieldThatWasPressed.tag]
popover = [[UIPopoverController alloc] initWithContentViewController:popoverTableview];
[popover presentPopoverFromRect:textField.bounds inView:textField permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
popoverTableview.delegate = self;
popoverTableview.popView = self.popover;
popoverTableview.aIntVariable = myTextFieldThatWasPressed;
then in my popovertableview:
- (id)initWithTextFieldTag:(int)textFieldTag
{
self.aIntVariable = textFieldTag;
return self;
}
Then in the fetchedResultsController method, you´ll just have to create simple if´s telling wich predicate you want...
Regards
If the fetched results controller is for the popover table and you need to know in which table the text field was selected, I'd recommend tagging each of the text fields when you create them and creating an int _currentTable instance variable. That way, when your textFieldShouldBeginEditing: method is called, you can set the ivar's value with the tag and check that tag when creating the fetched results controller for the popover table.
So, instead of
if (table1 textFieldShouldBeginEditing:table1.textFieldPressed)
{
fetchRequestList.predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview1];
}
if (table2 textFieldShouldBeginEditing:table2.textFieldPressed)
{
fetchRequestList.predicate = [NSPredicate predicateWithFormat:#"%K IN %#", #"reference", arrayTableview2];
}
you'll have
if (_currentTable == 1) {
fetchRequestList.predicate = // table one predicate
} else if (_currentTable == 2) {
fetchRequestList.predicate = // table two predicate
}
UPDATE:
This is how I would override the init from code. In your popover table view controller implementation:
- (id)initWithTableTag:(int)tableTag
{
self = [super init];
if (self) {
_currentTable = tableTag;
}
return self;
}
(Make sure you also declare - (id)initWithTableTag:(int)tableTag; in your header.) Then, when you create and present the popover controller (which I'm assuming you're doing in the textFieldShouldBeginEditing: delegate call):
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
// ...
YourPopoverTableViewControllerClass *vc = [[YourPopoverTableViewControllerClass alloc] initWithTableTag:textField.tag];
// ...
// display the popover
return YES;
}
Unfortunately, I don't know how to do this using storyboards.

nsstring to nsmutable array

I'm making an app in which i want to access the contact's first name and store them in to nsmutable array so that i can get the values of that array like array[0] up to array[i-1] and print them in table view.Here is my code:
CFErrorRef error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if (addressBook != nil)
{
contacts_Image_List=[[NSMutableArray alloc]init];
NSLog(#"Succesful.");
NSArray *allContacts = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
NSUInteger i = 0;
for (i = 0; i < [allContacts count]; i++)
{
Person *person = [[Person alloc] init];
ABRecordRef contactPerson = (__bridge ABRecordRef)allContacts[i];
NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(contactPerson, kABPersonFirstNameProperty);
NSString *lastName = (__bridge_transfer NSString *)ABRecordCopyValue(contactPerson, kABPersonLastNameProperty);
NSString *fullName = [NSString stringWithFormat:#"%# %#", firstName, lastName];
person.firstName = firstName;
person.lastName = lastName;
person.fullName = fullName;
// person.userThumb[i]=firstName;
//[person.userThumb[i] addObject:#"firstName"];
//above line gives null
NSLog(#"%#",person.userThumb[i]);
my mutable array is in Person.h class.
On each iteration just do:
[person.yourMutableArray addObject:fullName]
Just ensure that your MutableArray has already been allocated, and that you only allocate it once.

NSComparisonPredicate to find empty strings

I need to check if a string in my array is empty using the NSComparisonPredicate. The predicate that is being executed is: string MATCHES[c] ""
But nothing shows up in the results, it does not list my empty strings.
Is my predicate wrong or there is just another way of dealing with empty strings with NSPredicate?
I'm not sure why you're using NSComparisonPredicate ,I've never used that one, so I'm not familiar with it. Have you tried predicateWithFormat?
NSPredicate *pred = [NSPredicate predicateWithFormat:#"self.length == 0"];
It's not clear how you want to use the predicate, but this should work if you're using it to filter an array.
If you want to know the indexes of strings in your array that are empty, then you could use indexesOfObjectsPassingTest: like so:
NSIndexSet *indxs = [array indexesOfObjectsPassingTest:^BOOL(NSString *aString, NSUInteger idx, BOOL *stop) {
return aString.length == 0;
}];
NSLog(#"%#",indxs);
As rdelmar mentions above, for such a task you should just check the length of the string by string.length==0
You can filter your array using blocks. I would recommend that approach against using NSPredicate. Here is some sample code:
//creating array
NSArray *myArray = [NSArray arrayWithObjects:#"a", #"b", #"", #"c", nil];
//filtering
NSIndexSet *iset = [myArray indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
NSString *str = obj;
return !str.length;
}
];
//creating my result array with empty strings
NSArray *emptyStrings = [myArray objectsAtIndexes:iset];
//logging all strings
for (NSString *str in myArray) {
NSLog(#"string: %#", str);
}
//logging empty strings
for (NSString *str in emptyStrings) {
NSLog(#"empty string: %#", str);
}
EDIT:
If you really need to use NSComparisonPredicate, here it is:
NSExpression *left = [NSExpression expressionForKeyPath:#"length"];
NSExpression *right = [NSExpression expressionForConstantValue:[NSNumber numberWithInt:0]];
NSComparisonPredicateModifier modifier = NSDirectPredicateModifier;
NSPredicateOperatorType operator = NSEqualToPredicateOperatorType;
NSPredicate *predicate = [NSComparisonPredicate predicateWithLeftExpression:left rightExpression:right modifier:modifier type:operator options:0];
NSArray *filtered = [myArray filteredArrayUsingPredicate:predicate];

Selecting TableView cell returns incorrect object

I followed a tutorial I found online to create a tableview with sections and an index from an array of custom objects. This code works with the exception that when I select a row in the table I the index path for that section and not for the entire array. I can see why it doesn't work but I can't figure out how to address the fix, this is my cell for tableview code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"NameCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
// Configure the cell...
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
int displayOrder = [defaults integerForKey:#"displayOrder"];
int sortOrder = [defaults integerForKey:#"sortOrder"];
NSString *alphabet = [listIndex objectAtIndex:[indexPath section]];
NSPredicate *sectionPredicate = [[NSPredicate alloc] init];
if (sortOrder == 1) {
//NSLog(#"fName is predicate at cell level");
sectionPredicate = [NSPredicate predicateWithFormat:#"fName beginswith[c] %#", alphabet];
} else {
//NSLog(#"lName is predicate at cell level");
sectionPredicate = [NSPredicate predicateWithFormat:#"lName beginswith[c] %#", alphabet];
}
NSArray *sectionContacts = [filteredList filteredArrayUsingPredicate:sectionPredicate];
if (isSearching) {
current = [filteredList objectAtIndex:indexPath.row];
} else{
current = [sectionContacts objectAtIndex:indexPath.row];
}
if (displayOrder == 1) {
NSString *fullName = [NSString stringWithFormat:#"%# %#",[current valueForKey:#"fName"],[current valueForKey:#"lName"]];
[cell.textLabel setText:fullName];
//NSLog(#"FirstNameFirst");
} else {
NSString *fullName = [NSString stringWithFormat:#"%# %#",[current valueForKey:#"lName"],[current valueForKey:#"fName"]];
[cell.textLabel setText:fullName];
//NSLog(#"LastNameFirst");
}
[cell.detailTextLabel setText:[current valueForKey:#"extension"]];
return cell; }
THen I call the segue with this code.
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"showContact"]) {
DetailViewController *dvc = [segue destinationViewController];
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
NSDictionary *c = [filteredList objectAtIndex:path.row];
[dvc setCurrentContact:c];
[searchBar resignFirstResponder];
} }
The problem is that the objectAtIndex:path.row returns the index for that section but it isn't modified for the entire array, so if a name in the "B" section that is at index 4 of that section is tapped it returns the object at index 4 of the primary array. I have been scratching my head to figure out how to get the index for the full array and not for the one that is only local to that section.
I'll buy you a 6 pack of your favorite beverage if you can help!
Thanks!
You do it the same way that they do it in the first function, so change your prepareForSegue to this:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showContact"]) {
DetailViewController *dvc = [segue destinationViewController];
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
NSDictionary *c;
NSString *alphabet = [listIndex objectAtIndex:[path section]];
NSPredicate *sectionPredicate = [[NSPredicate alloc] init];
if (sortOrder == 1) {
sectionPredicate = [NSPredicate predicateWithFormat:#"fName beginswith[c] %#", alphabet];
} else {
sectionPredicate = [NSPredicate predicateWithFormat:#"lName beginswith[c] %#", alphabet];
}
NSArray *sectionContacts = [filteredList filteredArrayUsingPredicate:sectionPredicate];
if (isSearching) {
c = [filteredList objectAtIndex:path.row];
} else{
c = [sectionContacts objectAtIndex:path.row];
}
[dvc setCurrentContact:c];
[searchBar resignFirstResponder];
}
}
Note that it would probably be best to pull the common code out and make a separate function instead of using it twice like this.

How can I insert new objects at top of UITableView backed by Core Data/NSFetchedResultsController?

I have a tableview that is successfully incorporating an NSFetchedResultsController. However, I need the topmost cell in my tableview to read, "Add new object" and have UITableViewCellEditingStyleInsert instead of the default UITableViewCellEditingStyleDelete.
The FetchResultsController wants to check the managedObjectContext for objects--both to determine number of rows and to populate the table cells. The only way I can think to get around this is to create a dummy object, but I feel like there ought to be a more elegant solution.
UPDATE:
For those who might be curious as to what solution I ended up with, I decided to have my insert cell at the bottom, not the top. Here is the relevant code:
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
//self.clearsSelectionOnViewWillAppear = NO;
self.editing = YES;
self.tableView.allowsSelectionDuringEditing = YES;
self.tableView.delegate = self;
RubricAppDelegate *appDelegate = (RubricAppDelegate *)[[UIApplication sharedApplication] delegate];
managedObjectContext = [appDelegate managedObjectContext];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"myClass" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entity];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"classID" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[request setSortDescriptors:sortDescriptors];
[sortDescriptor release];
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil cacheName:nil];
NSError *error;
[fetchedResultsController performFetch:&error];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSLog(#"Number of sections = %d", [[fetchedResultsController sections] count]);
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
id <NSFetchedResultsSectionInfo> myClass = [[fetchedResultsController sections] objectAtIndex:section];
NSLog(#"Number of classes = %d", [myClass numberOfObjects]);
return ([[fetchedResultsController fetchedObjects] count] + 1);
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSLog(#"FRC count + 1 = %d", ([[fetchedResultsController fetchedObjects] count] + 1));
if (indexPath.row == ([[fetchedResultsController fetchedObjects] count])) {
cell.textLabel.text = #"Add New Class";
}
else {
myClass *theClass = [fetchedResultsController objectAtIndexPath:indexPath];
NSLog(#"Class name is: %#", theClass.classTitle);
cell.textLabel.text = theClass.classTitle;
}
return cell;
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == [[fetchedResultsController fetchedObjects] count]) {
return UITableViewCellEditingStyleInsert;
}
return UITableViewCellEditingStyleDelete;
}
The result (with some junk data):
Now my only issue is getting the delete functions to work properly. You can follow my post on that issue here
Normally the add row is at the bottom.
You can accomplish this by changing the -tableView:numberOfRowsInSection: and the -tableView:cellForRowAtIndexPath: methods to adjust the cell count and adjust for it. So your -tableView:numberOfRowsInSection: would return N+1 and your -tableView:cellForRowAtIndexPath: would get object at N-1 unless N == 0 then it would return your "Add new object" cell.
There is no need to mess with the underlying Core Data elements as this is strictly a UI issue.
Update
But now I'm not sure how to return the count of my fetched objects (assuming that is what I used for "N" in your above answer). Also, wouldn't I want -tableView:cellForRowAtIndexPath to return my "Add new object" cell when the indexPath.row = (N + 1), not N = 0? I may be misunderstanding what "N" equates to, but I thought it just meant count of fetched objects.
Yes it is the count of actual objects.
You do want your -tableView:cellForRowAtIndexPath: to return a cell for your "Add new object" otherwise what is the point? You just want it to return a different type of cell.
All you are doing in this solution is adding a cell that is not part of the NSFetchedResultsController and then compensating for it when you are retrieving an actual object from the NSFetchedResultsController and when the user selects a cell.

Resources