I have an NSTextStorage delegate imlementing textStorageDidProcessEditing:. Unfortunately, the userInfo doesn't contain any details about the changes to the text.
How can I find out what actually changed?
Ideally I'd like to know everything sent to [NSLayoutManager textStorage:edited:range:changeInLength:invalidatedRange:], but just the edited range would be good enough.
You could just write your own subclass of NSLayoutManager and add it to your NSTextSTorage using addLayoutManager:.
You could also use KVO to observe the characters property of your NSTextStorage object (or any other property of it) and you get exactly what is changed in
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
Use NSTextStorage's getter methods editedRange and changeInLength.
Related
I am using core data binding in cocoa app. My app has three NSTableViews bound to there NSArrayControllers in relationship such that if first table is selected, relevant data using Core data relationship is shown in the other two tables.
I have hooked NSSearchField to first NSArrayController. The problem is when search begins the other two tables do not remain in sync with the first one.They show nothing at all.
The other two tables only shows content when first one if clicked by user.
How can I make all three NSTables update simultaneously when search occurs.
So, I have this solution which works, I would like to know if this can be improved further.
Since when search starts the first NSTableView's selection is lost and hence the connected tables too, although the table is updated correctly showing filtered value. I am setting self as observer for change is NSArrayController selectedObject.
-(void)awakeFromNib{
[super awakeFromNib];
[_firstArrayController addObserver:self forKeyPath:#"selectedObjects" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];
}
Now I am observing for changes and if selected object do change and if NSSearchField is the one changing it, programatically select NSTableView first row
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)
object change:(NSDictionary *)change context:(void *)context
{
NSResponder *firstResponder = [[NSApp keyWindow] firstResponder];
if ([firstResponder isKindOfClass:[NSText class]] && [(id)firstResponder delegate] == _searchField) {
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:0];
[_authorTable selectRowIndexes:indexSet byExtendingSelection:NO];
}
}
Since when filtering using NSSearchField, the first object is what the most relevant, hence I am setting selection to row 0.
I have a NSCollectionView in a cocoa application.
I can get information about the currently selected object in the collection view through the following roundabout way:
NSIndexSet* index = [self.currentCollectionView selectionIndexes];
CardModel* card = [[self.currentCollectionView itemAtIndex:index.firstIndex] representedObject];
Does the NSCollectionView class have a method that returns the selected object? Or is this the preferred way to go about it?
Unlike NSTableView you do not have delegates/notifications which gives notifies you about the selection. So selectionIndexes is the way to go.
I am not sure if you have set up observers for array controllers or not. But the code which you have shown is only to retrieve the selected objects. To get notified about the selection of objects you need to add observer for key path selectionIndexes (or what ever is set in IB) on array controllers.
[myArrayController addObserver:self
forKeyPath:#"selectionIndexes"
options:NSKeyValueObservingOptionNew
context:nil];
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if([keyPath isEqualTo:#"selectionIndexes"])
{
// This will be invoked whenever objects are selected in Collection View.
// Now collectionView selectionIndexes can be used to get the selected objects.
}
}
If you're asking if selectionIndexes is the only way to access an NSCollectionView's selection the answer is yes.
One approach is to use bindings in the xib. Set up an NSArrayController for the items to be represented by the views in the collection. In the xib, in the Bindings Inspector of the Collection View, bind the Content of the CollectionView to the collectionViewArrayController.arrangedObjects. Also bind the Selection Indexes to collectionViewArrayController.selectionIndexes. Now you can make an outlet to the array controller, say in the App Delegate, and access the selected objects there.
For example, declare a selectedCard property, also a collectionViewAC outlet property connected to thecollectionViewArrayController. Now you can get the card item(s) you wish by way of selectedObjects.
- (id)selectedCard
{
id selectedCards = [collectionViewAC selectedObjects];
if ([selectedCards count]) {
return [selectedCards objectAtIndex:0];
}
return nil;
}
Use of bindings keeps everything observed and updated.
I have a UITableView that has cells containing text fields. As the user edits each text field, I need to keep track of the cell's text field value (even when the cell is no longer displayed, so simply tracking the text field does not work). So I used Key-Value Observing and set an observer for each text field in my UITableViewController subclass:
[cell addObserver:self forKeyPath:#"textField.text" options:NSKeyValueObservingOptionNew context:nil];
And save new changes to the text field in the class's observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context method.
This works great in iOS 6, but in iOS 7 KVO isn't called when the user changes the text field value. Apparently the setter method called by pressing "done" on the keyboard doesn't call KVO anymore.
Is there a workaround to this? Or a better way of listening for this change? I need to know what cell the text field belongs to, so implementing the text field's method editingDidEnd doesn't work for me.
Thanks in advance.
I'm not sure about whether there is a workaround for this or not but there surely is a better way of listening for the UITextField text value. If you are creating instances of UITextField programmatically, all you need to do is add an event handler for them to notify your class about the text changing events. For example:
[myTextField addTarget:self action:#selector(editingChanged:) forControlEvents:UIControlEventEditingChanged];
- (void)editingChanged:(UITextField *)sender {
NSString *targetText = sender.text;
}
I've got an observer on my UItextFields. It basically looks to see if the "enabled" property has changed.
If the enabled status changes, I want a method called fade to run. Fade requires a TextField to be passed to it.
How do I use the textField associated with the Observer that sends the message?
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change: (NSDictionary *)change context:(void *) context;
{
int new = [change objectForKey:NSKeyValueChangeNewKey];
int old = [change objectForKey:NSKeyValueChangeOldKey];
if (new != old)
{
[self fadeEnable:"requires txtField"];
}
}
Thanks
As shown in the Docs,
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSKeyValueObserving_Protocol/Reference/Reference.html, the parameter 'Object' in the method call encapsulates your textfield.
you just have to cast it and youre done.
To cast it, use:
UITextField *tf = (UITextField *)object;
Edit to address your follow-up Question:
In The case of a Gesture recognizer, you want to get the view that you added the recognizer to. The 'sender' being the recognizer itself, sender.view will get you the associated view.
None of this will ever change, because recognizers only work with VIEWS.
Now, with an observer, you do not necessarily want a view. You want an OBJECT. Thats why 'object' is of type 'id', so you can add observers to objects of arbitrary types. The downside is, of course, that you need a cast. If you dont know what type to cast to, the
-isKindOfClass:
method might be useful.
Oh, and none of this is about interface-builder versus doing stuff in code.
If you find this confusing, i suggest you go study the docs on gestureRecognizers and Key-Value-Observing. If you have specific questions, let me know, or just ask an new question ;)
Let's say I've got a Player instance, and I want to add an observer to their score so I can update the score display in my iOS application. I believe I should be calling a method that looks like this to add the observer:
[[self.game player] addObserver:self
forKeyPath:#"score"
options:NSKeyValueObservingOptionNew
context:nil];
In the same class, I then have a method defined that should receive this message:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
However, the breakpoint that I have placed in this method never gets hit.
I would like to know how to diagnose this issue. I have verified that player instance is filled, and when the score is updated, the same instance is updated.
If it helps, I have just converted my project to ARC, but this code above is new and has never worked.
Either your key path "score" is not KVO compliant (are you not defining a #property for *score?) or the adding of the observer method is never being called. You can easily diagnose the latter by placing a breakpoint onto the line of code in your first snippet, and verifying that an observer indeed is being registered.