Filtering A Tree Controller - objective-c

I have now nearly figured out how to Filter a NSTreeController, to do this I have sub-classed NSManagedObject and added some code to my App Delegate, I have also bound my NSSearchField to the filterPredicate of my App Delegate but I think I need to connect my NSTreeController and NSSearchField in some way to make it work.
Below I have posted all the code I have used so far to try and make it work.
NSManagedObject Sub-Class Header File.
#interface Managed_Object_Sub_Class : NSManagedObject {
NSArray *filteredChildren; // this should fix the compiler error
}
- (NSArray *)filteredChildren;
#end
NSManagedObject Sub-Class Implementation File.
#implementation Managed_Object_Sub_Class
static char *FilteredChildrenObservationContext;
- (id)initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context {
if (self = [super initWithEntity:entity insertIntoManagedObjectContext:context]) {
[[NSApp delegate] addObserver:self forKeyPath:#"filterPredicate" options:0 context:&FilteredChildrenObservationContext];
[self addObserver:self forKeyPath:#"subGroup" options:0 context:&FilteredChildrenObservationContext];
}
return self;
}
// use finalize with GC
- (void)dealloc {
[[NSApp delegate] removeObserver:self forKeyPath:#"filterPredicate"];
[self removeObserver:self forKeyPath:#"subGroup"];
[super dealloc];
}
- (NSArray *)filteredChildren {
if (filteredChildren == nil) {
NSPredicate *predicate = [[NSApp delegate] filterPredicate];
if (predicate)
filteredChildren = [[[self valueForKey:#"subGroup"] filteredArrayUsingPredicate:predicate] copy];
else
filteredChildren = [[self valueForKey:#"subGroup"] copy];
}
return filteredChildren;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == &FilteredChildrenObservationContext) {
[self willChangeValueForKey:#"filteredChildren"];
[filteredChildren release];
filteredChildren = nil;
[self didChangeValueForKey:#"filteredChildren"];
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#end
Code Added To App Delegate Header File
NSPredicate *filterPredicate;
Code Added To App Delegate Implementation File
- (NSPredicate *)filterPredicate {
return filterPredicate;
}
- (void)setFilterPredicate:(NSPredicate *)newFilterPredicate {
if (filterPredicate != newFilterPredicate) {
[filterPredicate release];
filterPredicate = [newFilterPredicate retain];
}
}
Search Field Binding
alt text http://snapplr.com/snap/vs9q
This doesn't work yet, and so that is why I am asking what I need to do from here to make it work, like I said I think I need to connect the NSSearchField and NSTreeController Together in some way.

Again I hav answered my own question, I also hope that this will help other people so they know how to Filter a NSTreeController.
To make it work from my post above do the following.
1.For your entity set the Class as your NSManagedObject Sub-Class in my Case JGManagedObject.
alt text http://dvlp.me/c3k
2.For your search field in IB set the predicate format to what you want to Filter ( The Property in your entity, for me it is name).
alt text http://dvlp.me/9k9rw

Related

Key-Value Observing adjustingFocus no notification

I'm not familiar with iOS but I'm trying to find when the default, built-in camera application is focusing. To do this I create my own separate Objective-C application and following this answer here [iPhone : camera autofocus observer? but I'm not getting anything from observeValueForKeyPath in the NSLog.
#import "ViewController.h"
#import "AVFoundation/AVCaptureDevice.h"
#import "AVFoundation/AVMediaFormat.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(#"viewDidLoad");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// callback
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
NSLog(#"observeValueForKeyPath");
if( [keyPath isEqualToString:#"adjustingFocus"] ){
BOOL adjustingFocus = [ [change objectForKey:NSKeyValueChangeNewKey] isEqualToNumber:[NSNumber numberWithInt:1] ];
NSLog(#"Is adjusting focus? %#", adjustingFocus ? #"YES" : #"NO" );
NSLog(#"Change dictionary: %#", change);
}
if( [keyPath isEqualToString:#"focusMode"] ){
AVCaptureFocusMode focusMode = [ [change objectForKey:NSKeyValueChangeNewKey] isEqualToNumber:[NSNumber numberWithInt:1] ];
NSLog(#"focusMode? %ld", focusMode);
}
}
// register observer
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear: animated];
NSLog(#"viewWillAppear");
AVCaptureDevice *camDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
int flags = NSKeyValueObservingOptionNew;
[camDevice addObserver:self forKeyPath:#"adjustingFocus" options:flags context:nil];
[camDevice addObserver:self forKeyPath:#"focusMode" options:flags context:nil];
}
#end
Any help much appreciated.
For anyone who visits this question, the answer is what Bluewings wrote as a comment. I was trying to use KVO to observe one application from another which is not possible since only one lock on a capture device is possible at one time.

addObserver:forKeyPath is crashing the app, KVO, objective c

I am doing the parallax effect by using category by doing :
add and UIView into the uitableView (via category
add addObserver:forKeyPath so that whenever tableview is moving, i will reframe the view above
Details are below
UIScrollView+Parallax.h
#import <UIKit/UIKit.h>
#class ParallaxView;
#interface UIScrollView (Parallax)
#property (strong, nonatomic) ParallaxView *parallaxView;
- (void) addParallaxViewWith:(UIView*)parallaxView;
- (void) removeKVO;
#end
#interface ParallaxView : UIView
#end
UIScrollView+Parallax.m
static char parallaxKey;
#implementation UIScrollView (Parallax)
#dynamic parallaxView;
#pragma mark - Add parallax view to scrollView
- (void) addParallaxViewWith:(ParallaxView*)pView {
if ( !self.parallaxView) {
[self addSubview:pView];
[self setParallaxView:pView];
}
}
#pragma mark - Set parallaxView + register parallaxView as an observer
- (void) setParallaxView:(ParallaxView *)parallaxView {
objc_setAssociatedObject(self, &parallaxKey, parallaxView, OBJC_ASSOCIATION_ASSIGN);
/* THESE LINE ARE CRASHING THE APP */
// [self addObserver:self.parallaxView
// forKeyPath:#"contentOffset"
// options:NSKeyValueObservingOptionNew
// context:nil];
}
#pragma mark - Get parallaxView
- (ParallaxView*) parallaxView {
return (objc_getAssociatedObject(self, &parallaxKey));
}
#pragma mark - Remove
- (void)removeKVO {
[self removeObserver:self.parallaxView forKeyPath:#"contentOffset"];
}
#end
#implementation ParallaxView
-(id)init
{
//load xib from main bundle and assign it to self
self = [[[NSBundle mainBundle]loadNibNamed:#"Parallex"
owner:self
options:nil] objectAtIndex:0];
return self;
}
-(id)initWithFrame:(CGRect)frame
{
self = [self init];
[self setFrame:frame];
return self;
}
................
#end
And I am adding parallax to the table by doing
ParallaxView *pView = [[ParallaxView alloc]initWithFrame:CGRectMake(0, 0, 320, 160)];
[self.tableView addParallaxViewWith:pView];
However, [self addObserver:forKeyPath:options:context:nil] keeps crashing the app without no clues at all. If I comments this line out and app is not crashing but parallex effect is not working.
Any ideas for this problematics. Please help. Thanks
Problem in code
-(id)initWithFrame:(CGRect)frame
{
self = [self init];
[self setFrame:frame];
return self;
}
In above code self = [self init]; and [self setFrame:frame]; will go in recursion will give crash
,First fix this I guess it will solve your problem,It should be like this
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
and also loading View from nib using
self = [[[NSBundle mainBundle]loadNibNamed:#"Parallex"
owner:self
options:nil] objectAtIndex:0];
this code is really a bad idea.
you can refer THIS for this task.
Happy and clean coding...
#implementation ParallaxView
//Add Observe Method
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if([keyPath isEqualToString:#"contentOffset"]){
NSLog(#"contentOffset:%#", [change objectForKey:NSKeyValueChangeNewKey]);
}
}
#end
Try to replace
objc_setAssociatedObject(self, &parallaxKey, parallaxView, OBJC_ASSOCIATION_ASSIGN);
with
//Change to OBJC_ASSOCIATION_RETAIN_NONATOMIC
objc_setAssociatedObject(self, &parallaxKey, parallaxView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
parallaxView should be a strong reference.

Why is my NSOperation subclass never finishing?

I have an NSOperation subclass that I want to run concurrently.
My understanding is that for concurrent operations to work:
I need to define isConcurrent to return YES.
I need to define the start method
I need to send KVOs notification for isExecuting and isFinished when it's done.
Using #synthesize will automatically send the appropriate KVO notifications when the values for isExecuting and isFinished are changed.
Despite this, I have verified that my queue never moves on to the next item.
Here's the meat of my code:
#interface MyOperation()
#property (readwrite) BOOL isExecuting;
#property (readwrite) BOOL isFinished;
#end
#implementation MyOperation
- (void)start
{
#autoreleasepool {
self.isExecuting = YES;
self.HTTPOperation = [[AFHTTPRequestOperation alloc] initWithRequest: URLRequest];
_HTTPOperation.completionBlock = [^{
[self completed];
self.isExecuting = NO;
self.isFinished = YES;
} copy];
[_HTTPOperation start];
}
}
- (BOOL)isConcurrent
{
return YES;
}
- (void)completed
{
}
#end
What am I missing?
(This is on an iPhone, but I can't imagine that matters.)
It looks like whatever KVO notifications #synthesize sends aren't enough for NSOperationQueue to move on.
Sending the notifications manually fixes the problem:
- (void)start
{
#autoreleasepool {
[self willChangeValueForKey:#"isExecuting"];
self.isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
NSURLRequest *URLRequest = [self buildRequest];
if (!URLRequest) {
[self willChangeValueForKey:#"isFinished"];
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
return;
}
self.HTTPOperation = [[AFHTTPRequestOperation alloc] initWithRequest: URLRequest];
_HTTPOperation.completionBlock = [^{
[self completed];
[self willChangeValueForKey:#"isFinished"];
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
} copy];
[_HTTPOperation start];
}
}
See also:
Why does NSOperation disable automatic key-value observing?
What's your "queue" look like? Are you using an NSOperationQueue?
Anyway, I'll try to answer your question with what I understood :P
I would create a delegate for my NSOperation and have KVO take care of calling this.
Say for example your NSOperation class looks like this
#interface MyOperation : NSOperation
#property (assign) id<MyOperationDelegate> delegate;
Your implementation
#synthesize delegate;
#synthesize error;
-(id)init{
self = [super init];
if(self){
[self addObserver:self forKeyPath:#"isFinished"
options:NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
-(void)dealloc{
[self removeObserver:self forKeyPath:#"isFinished"];
[super dealloc];
}
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if([keyPath isEqualToString:#"isFinished"] == YES){
if([self isCancelled] == NO){
if(delegate != nil && [delegate respondsToSelector:#selector(operationComplete:)]){
[delegate taskComplete:self];
}
}else{
if(delegate != nil && [delegate respondsToSelector:#selector(operationCancelled)]){
[delegate taskCancelled];
}
}
}
}
-(void)main{
[NSException exceptionWithName:kTaskException
reason:#"Only to be used with subclass"
userInfo:nil];
}
And finally your protocol
#class MyOperation;
#protocol MyOperationDelegate <NSObject>
#optional
-(void)operationComplete:(MyOperation*)operation;
-(void)operationCancelled;

Key value observing with Array not doing anything

I have two views, my first view class1.m and second view is class2.m. My second view is initialized as a popover when a button is pressed on a toolbar in the first view. I have an array in my second view, in which objects is added, if any of the rows is pressed. I'm trying to set up a KVO in my first view, so that i can access the allSelectedFocus array from the second view in my first view, but it's not working. I realize that i don't invoke removeObserver, but i don't know where to invoke it, without it removing the observer before it's used. If anybody know any better ways to do this, I'm open for suggestions, but if someone can get this to work, that would be really awesome too.
//class2.m
#import "class2.h"
#import "class1.h"
#implementation class2
#synthesize selectedFocus = _selectedFocus;
#synthesize focusArray = _focusArray;
#synthesize allSelectedFocus = _allSelectedFocus;
- (void)viewDidLoad
{
_focusArray = [[NSArray alloc]initWithObjects:#"Balance",#"Bevægelse",#"Elementskift",#"Vejrtrækning",#"Alle",nil];
[super viewDidLoad];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return _focusArray.count;
}
- (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];
}
NSString *cellValue = [_focusArray objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
_selectedFocus = [[_focusArray objectAtIndex:indexPath.row] stringByAppendingString:#","];
if(![[self mutableAllSelectedFocus] containsObject:_selectedFocus])
{
//add object to array, if it's not already there
[[self mutableAllSelectedFocus] addObject:_selectedFocus];
}
else
{
//remove object from array, if it's already there
[[self mutableAllSelectedFocus] removeObject:_selectedFocus];
}
}
-(NSMutableArray *)allSelectedFocus
{
if(_allSelectedFocus == nil)
{
_allSelectedFocus = [[NSMutableArray alloc]init];
}
return _allSelectedFocus;
}
-(NSMutableArray *)mutableAllSelectedFocus
{
return [self mutableArrayValueForKey:#"allSelectedFocus"];
}
#end
//class1.m
#import "class1.h"
#import "class2.h"
#implementation class1
- (void)viewDidLoad
{
[super viewDidLoad];
if(_focusTag == nil)
{
_focusTag = [[class2 alloc]init];
}
[_focusTag addObserver:self forKeyPath:#"selectedFocus" options:NSKeyValueObservingOptionNew context:NULL];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:#"allSelectedFocus"])
{
NSLog(#"%#", [object valueForKeyPath:keyPath]);
}
}
I suspect that this is either a function of the fact that NSArray objects are not observable or some broader violation of KVC compliance. Regardless, just implement manual change notification for NSArray objects and you should be fine. I just tested changes to NSArray objects (the adding of objects) and automatic notification did not take place, but when I added manual notification, it worked fine. (Though, curiously, NSKeyValueObservingOptionOld doesn't work as expected, showing the new value instead of the old value.) FYI, here is an example of the update method which adds something to my objects NSMutableArray, using manual notification:
- (void)addToMyArray:(id)obj
{
[self willChangeValueForKey:#"myArray"];
[_myArray addObject:obj];
[self didChangeValueForKey:#"myArray"];
}
Update:
By the way, if you need NSKeyValueObservingOptionOld, you can do something like:
- (void)addToMyArray:(id)obj
{
NSMutableArray *tempArray = [NSMutableArray arrayWithArray:_myArray];
[tempArray addObject:obj];
[self setMyArray:tempArray];
}
This way, you don't need manual notification, and you can retrieve both old and new values, but it also seems like an inefficient use of memory, so there are pros and cons.
Just a heads up, at no point are you using an accessor method to set / get your properties. This means that KVO won't work. I believe KVO relies on the getting / setting of properties via accessors.
I'm not sure of what you're trying to accomplish with the application, but I put together some code that may be of help. I commented within the code so I won't be explaining it throughout this answer.
I'll start with class2 as you did:
#import <UIKit/UIKit.h>
#interface Class2ViewController : UITableViewController
#property (nonatomic, strong) NSArray *focusArray;
#property (nonatomic, strong) NSMutableArray *allSelectedFocus;
// This is a readonly property that will return a mutable array of the allSelectedFocus property
// This gives you the ability to have automatic KVO if you add/remove using this property
// You won't have to wrap your calls to will/didChangeValueForKey:
#property (nonatomic, readonly, strong) NSMutableArray *mutableAllSelectedFocus;
#end
#import "Class2ViewController.h"
#import "Class1ViewController.h"
#implementation Class2ViewController
#synthesize focusArray = _focusArray;
#synthesize allSelectedFocus = _allSelectedFocus;
- (void)viewDidLoad
{
[super viewDidLoad];
// This is what you have
// FYI you are accessing the iVar directly, not sure if that matters in your app or not
_focusArray = [[NSArray alloc] initWithObjects:#"Balance",#"Bevægelse",#"Elementskift",#"Vejrtrækning",#"Alle",nil];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Grab the string of interest from the _focusArray --> this is direct access again which I imagine is fine
NSString *selectedFocus = [[_focusArray objectAtIndex:indexPath.row] stringByAppendingString:#","];
// Use the new mutableAllSelectedFocus property to check if the array doesn't contain the string of interest
if (![[self mutableAllSelectedFocus] containsObject:selectedFocus]) {
// If it doesn't contain it, add it using the mutableAllSelectedFocus property
[[self mutableAllSelectedFocus] addObject:selectedFocus];
}
}
// This is getter that lazily instantiates your _allSelectedFocus array
- (NSMutableArray *)allSelectedFocus
{
// Check to see if the backing iVar is nil
if (_allSelectedFocus == nil) {
// If it is, create an empty mutable array
_allSelectedFocus = [[NSMutableArray alloc] init];
}
// return the array
return _allSelectedFocus;
}
// This is our new property
- (NSMutableArray *)mutableAllSelectedFocus
{
// mutableArrayValueForKey: returns a mutable array for the given key (property)
// Allows us better KVO and efficiency with changing properties
return [self mutableArrayValueForKey:#"allSelectedFocus"];
}
And now class 1:
#import "Class1ViewController.h"
#import "Class2ViewController.h"
#implementation Class1ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// If you are using ARC, this instance will be deallocated after -viewDidLoad
// You will want to store this in an instance variable if you need to keep it around
Class2ViewController *class2ViewController = [[Class2ViewController alloc] init];
[class2ViewController addObserver:self
forKeyPath:#"allSelectedFocus"
options:NSKeyValueObservingOptionNew
context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"allSelectedFocus"]) {
NSLog(#"%#", [object valueForKeyPath:keyPath]);
}
}
I'm not sure if this change in code will be helpful in your application. Two things I would do though would be read the Key-Value Coding and Key-Value Observing Guides if you haven't and read this post on to-many relationships and properties.
If I got something wrong, just leave a comment.
Good luck.

How to setup NSArrayController with Core Data?

I am trying to programmatically setup an NSArrayController to work with Core Data.
I know that my Core Data store has content since I can manually retrieve objects through the managed object context. I hooked up an NSArrayController to the same managed object context and then bound the value parameter of a NSTableColumn to the NSArrayController.
I asked the NSArrayController to fetch but it returns an empty array.
Any suggestions on what I might be doing wrong?
Interface
#interface MTTableViewController : NSObject <NSTableViewDelegate, NSTableViewDataSource>
{
NSMutableArray *tableData;
MTTableCell *tableCell;
IBOutlet NSTableColumn *tableColumn;
NSArrayController *dataController;
}
Implementation
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
dataController = [[NSArrayController alloc] init];
[dataController setManagedObjectContext:[[NSApp delegate] managedObjectContext]];
[dataController setEntityName:#"Track"];
[dataController setAutomaticallyPreparesContent:YES];
[dataController fetch:self];
NSArray *content = [dataController arrangedObjects];
NSLog(#"Count :%i", (int)[content count]); //Outputs 0
tableCell = [[MTTableCell alloc] initTextCell:#""];
[tableColumn setDataCell:tableCell];
}
return self;
}
The fetch: doesn't wait for data to be loaded, instead it returns instantly.
This makes sense in bindings-enabled environment. You usually have a table view bound to an array controller, which updates whenever controller content changes.
In this case you could observe for changes in arrangedObjects of the controller:
[self.arrayController addObserver:self forKeyPath:#"arrangedObjects" options:NSKeyValueObservingOptionNew context:NULL];
And then:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
NSLog(#"Data: %#", self.arrayController.arrangedObjects);
}
Source: https://stackoverflow.com/a/13389460