NSTableView sort problem(with KVC) - objective-c

I had a weird problem on NSTableView sort.
I created a simple app with a NSTableView which
has 4 columns. Then I used KVC to bind it to a
Array controller.
Then I added some data to the NSMutableArray.
Bulid&&Run the app and I can see the data inside
the table and by clicking the header the data can
be sorted correctlly.
Everything works fine up till now.
Then I tried to add the "caseInsensitiveCompare" to
each columns. So I opened the IB, set the sort key and
selector ("caseInsensitiveCompare:") to each columns.
Then Bulid&&Run the app, but when I click the header to
sort, I got the error message:
-[NSCFNumber caseInsensitiveCompare:]: unrecognized selector sent to instance 0x1006254f0
-[NSCFNumber caseInsensitiveCompare:]: unrecognized selector sent to instance 0x1006254f0
......
Then I tried to delete all the content in the sort key and
selector of each columns. And the app became OK again.
What seems to be the problem? I am really confused...
ps:
If I use compare: instead of caseInsensitiveCompare:, everything works fine again...

One of the values (such as a property or key) of your objects in your NSMutableArray is of class NSNumber. Since this class deals with numbers, it doesn't respond to the caseInsensitiveCompare: selector. This selector is meaningful to NSString.
The column on your table view that is displaying number values should keep using compare: for sorting the values.

Related

NSTableView + NSTextView = Disaster :(

This is driving me a bit crazy..
Down below is a screenshot of my program so far.
On the right is an NSTableView (view-based). This is where the user can select a document they want to work on.
On the left is the NSTextView. Text will be displayed depending on what item they choose in the NSTableView.
There are also big plus and minus buttons for creating/deleting new items in the tableview.
Simple right? I wish.
Right now I have it so the tableview gets data from a mutable array. The mutable array contains objects of a class called DocumentItem. The DocumentItem just has two strings, one for the document text and one for the document title.
What works so far:
When I manually add objects to the array using code, I am able to freely switch through the documents and the textview will update accordingly.
What doesn't work:
When the user switches to a different document, I want to call the NSTableView replaceObjectAtIndex method save the changes that they have made to the object in the array.
How my code works so far:
The mutable array is stored in a data class. The data class is a shared class and is referred to in my code as theDATA.
I have a thread looping in my class that has the textview. In my tableview class I have a method called blastToScreen that will change a BOOL called shouldBLAST to YES.
Here is the code in my TableController class to set the BOOL to YES:
- (void) blastToScreen{
theDATA.blasttext = [[theDATA.globaldoclist objectAtIndex:[tablevieww selectedRow]] doccontents];
theDATA.shouldBLAST=YES;
}
Here is the shouldBLAST method in my looped thread(in a different class from the textview). Please note that the if-statement that says if(theDATA.switchedrow) is there to make sure that certain code gets runned only when a user switches their row in the tableview.
if(theDATA.shouldBLAST){
if(theDATA.switchedrow){
DocumentItem * itemr = [theDATA.globaldoclist objectAtIndex:theDATA.lastindex];
NSLog(#"(%li) prev content - >%#",(long)theDATA.lastindex,itemr.doccontents);
itemr.doccontents=textvieww.string;
NSLog(#"(%li)adding content - > %# <- to %#",theDATA.lastindex, itemr.doccontents,itemr.docname);
theDATA.switchedrow=NO;
[theDATA.globaldoclist replaceObjectAtIndex:theDATA.lastindex withObject:itemr ];
NSLog(#"changed: - > %#",[[theDATA.globaldoclist objectAtIndex:theDATA.lastindex] doccontents]);
}
textvieww.string=theDATA.blasttext;
theDATA.shouldBLAST=NO;
NSLog(#"changed: - > %#",[[theDATA.globaldoclist objectAtIndex:theDATA.lastindex] doccontents]);
theDATA.lastindex=theDATA.selectedrow;
}
Here's the weird part about all this:
According to the NSLog statements I set up, my code works for a split second and then resets.
Down below is what the console says. ignore the (0). that is just talking about the last selected index.
What it is saying is that the text before switching was nothing(fine). It is saying that it is adding the text "Potato" to that array(still fine). Then, the first time I fetched the object from the array it shows that it successfully changed to "Potato"(Still fine). Then when I tried to fetch the SAME exact data a few lines later, it returned nothing. :(
I feel like the issue resides somewhere in my TableController class. Here's a link to the code in my TableController class.
Here's what the console returned:
2015-09-14 17:17:46.024 Simplicity[4801:432580] (0) prev content - >
2015-09-14 17:17:46.025 Simplicity[4801:432580] (0)adding content - > Potato <- to Untitled
2015-09-14 17:17:46.025 Simplicity[4801:432580] changed: - > Potato
2015-09-14 17:17:46.025 Simplicity[4801:432580] changed: - >
I really hope you guys can help me. I tried pretty much everything I could to solve this issue.This is holding me back from finishing my software.
Probably, the doccontents property of your DocumentItem class is strong (or retain) when it should be copy.
From the docs for the string property of NSText (from which NSTextView inherits):
For performance reasons, this method returns the current backing store of the text object. If you want to maintain a snapshot of this as you manipulate the text storage, you should make a copy of the appropriate substring.
So, if you're just keeping a reference to that same object, when the text view's content is changed, the content of the object you've got a reference to also changes. You need to make a private copy.

Add NSArray to CLBeaconRegion via setValue forKey causes Error

Is it possible to add an Array to a CLBeaconRegion via a setValue forKey?
I've tried to add it:
[regionAdvert setValue:haveArray forKey:#"advertArray"];
But I receive just the following error:
> Terminating app due to uncaught exception 'NSUnknownKeyException',
> reason: '[<CLBeaconRegion 0x15379600> setValue:forUndefinedKey:]: this
> class is not key value coding-compliant for the key advertArray.'
setValue:forKey: is a KVC method which is basically available on every class - but that doesn't mean you can just pass anything to it. The key you use needs to be an existing property (well, really a method as that is what gets called, or a handled non-existent key). Anyway, the point is that you should know the key exists before trying to set it.
Technically you can use objc_setAssociatedObject to associated arbitrary objects with other objects but in this case I would encourage you to instead use the major and minor properties to check the purpose of the region before deciding how to display your alert.
No, you can't do this. If you want to associate other data with a CLBeaconRegion, then you can use other data structures like NSDictionary. Since each CLBeaconRegion is constructed with a unique "identifier" string, you can use this identifier as a key into a NSDictionary to store your other arbitrary data objects.
Also, you aren't supposed to change a CLBeaconRegion once constructed. The fields are immutable. This isn't really a big deal -- there are only three fields. Just create a new CLBeaconRegion, copying any of the identifiers from the old CLBeaconRegion that you wish to retain.

Core Data To Many Peer Relationship

I'm writing a Core Data based Cocoa app for recipes. I have an Ingredient entity, and want to create a ingredientSubstitutes To Many relationship to other Ingredients, but I'm getting errors either when setting the relationship or when saving the store that I can't figure out. Here's the Entity description:
Ingredient
Attributes:
ingredientName type:String
Relationships:
ingredientSubstitutes destination:Ingredient inverse:ingredientSubstitutes
In my Nib I have 3 array controllers:
All ingredients AC
Available substitutes AC
Selected ingredient substitutes AC
I have 3 table views that each display the contents of these array controllers. I then have a button to add one ingredient as a subsitute for another, that is bound as follows
Button bindings
Target: All Ingredients AC.selection
Selector Name: addIngredientSubstitutesObject:
Argument: Available Substitutes AC.seletion
With this setup, as soon as I click the add button, the app throws unrecognized selector sent to instance exception: "-[_NSControllerObjectProxy entity]: unrecognized selector sent to instance", as if Ingredient doesn't recognize addIngredientSubstitutesObject. I added a proxy method to make sure that's the selector that's not recognized, and that is indeed the problem.
After trying a bunch of things and getting no where, as an experiment, I then changed the model, so that ingredientSubstitutes has no inverse:
Ingredient
Attributes:
ingredientName type:String
Relationships:
ingredientSubstitutes destination:Ingredient inverse:*none*
When I run this the add is successful, and all the tables update accordingly, but on save, I get a different unrecognized selector and the app throws an exception:
-[_NSControllerObjectProxy _isKindOfEntity:]: unrecognized selector sent to instance
Any suggestions as to what might be going on? Am I taking the wrong approach?
Figured it out, this thread helped tip me off: Core Data Programmatically Adding a to-many Relationship
Basically, binding the target to the Array Controller's selection meant that the target object was an Array Controller's Proxy object for an Ingredient, not an actual Ingredient, which apparently does not respond to the To Many accessors that Ingredient does. I solved this by instead implementing a method in the app delegate that gets the actual objects and can use the To Many accessors:
- (void)addSubstituteForSelectedIngredient:(OFIngredient *)ingredient
{
OFIngredient *selectedIngredient = [[self.allIngredientsArrayController selectedObjects] objectAtIndex:0];
OFIngredient *selectedSubstitute = [[self.availableSubsArrayController selectedObjects] objectAtIndex:0];
[selectedIngredient addIngredientSubsObject:selectedSubstitute];
}
Note that NSArrayController's - (id) selection method (which I tried before, as mentioned in the original description) returns a proxy object, so you must use (NSArray *)selectedObjects!

Selecting the Tag of NSSegmentedControl

I'm attempting to implement an NSSegmentedControl button in my IB.
I have it connected to - (IBAction)editCart:(id)sender;
Also, it is connected to NSSegmentedControl *editCartButton;
The first "segment" is a "-" button to decrease the cart value.
The second "segment" is a "+" button to increase the cart value.
When I attempt to use the "sender" value like so: [sender selectedSegment], I get an error:
-[NSTableView selectedSegment]: unrecognized selector sent to instance 0x100622aa0
My button is located inside an NSTableView.
I've also tried: [[editCartButton cell] selectedTag]
When I run it through my conditions, it always returns a value of (null).
I would like to retrieve the specific tags of 0 and 1 I'm expecting to get, but can't find the right actions.
Help appreciated greatly, thanks.
This:
-[NSTableView selectedSegment]: unrecognized selector
sent to instance 0x100622aa0
basically tells you that the sender is not the NSSegmentedControl you think it is. The sender is an NSTableView. So either you wired things up the wrong way, or you have a severe memory management problem where the NSSegmentedControl is deallocated, and an NSTableView is currently to be found at its memory location.
In -(IBAction)editCart:(id)sender you could add a line:
NSLog(#"editCart, sender = %#",sender);
to confirm this. You can drop NSLog lines like this in other places in your code, to verify your ideas about what should be happening.
in IBAction try replacing (id) with (UISegmentedControl *)
I had a similar problem when working on updating Seashore (A port of GIMP to native OS X APIs).
First, you have to get NSSegmentedControl's cell object:
NSSegmentedControl *segControl = ...
NSSegmentedCell *segCell = [segControl cell];
Then, you set the tag for the segment you want to modify:
[segCell setTag:200 forSegment:2];
More info is available in Apple's documentation.

How to get NSFormatter subclass to work with NSTableColumn sort key and selector?

My setup:
I have a sqlite database from which I populate a NSMutableArray of NSDictionary objects this is the DataSource for my NSTableView.
One of the columns holds "time", the time is a float that holds seconds.
I would like to display the values in this column as minutes:seconds. For instance my data would be 123.4329387 I want to display 2:03 which I have no problem doing with a subclass of NSFormatter (or NSNumberFormatter) applied to my NSTextField in the column.
I have sorting set up by using the Table Column Attributes in IB, I just have the sort key set to "time" and the selector set to "compare:" which works fine without the formatter.
Currently this gives me something like this when I sort (descending)
1:37, 1:31, 0:10, 0:10, 0:09, 1:30, 1:30, 1:26, 0:09
and similar nonsense, it looks like something is going on but it's definitely not sorted.
How do I get the sort to look at the underlying data instead of the formatted value? Alternately, how do I specify a custom sort method and where do I put the code for said method? I have searched around quite a bit and have not found anything to help me out with this problem, any help with this is most appreciated.
It turns out that when it was sorting it was using compare: from the NSString class but it was using the underlying numeric data. I'm not exactly sure what all is going on under the hood here but I managed to fix this by creating my own compare method in a category of NSString called "timeCompare". Then in interface builder I just put "timeCompare:" in the selector field for the the table column. This gets it to call my custom compare method from which I just converted the strings given to NSNumbers and call compare: on them.