Using "set" in cocoa method name - cocoa

In my class, I have:
#property (atomic, retain) NSPredicate* predicate;
So obviously setPredicate:somePredicate works.
I want a class method that looks like:
-(void)setPredicateWithDate:(NSDate *)date
{
NSPredicate* predicate = (build a predicate with the NSDate)
[self setPredicate:predicate];
}
This seems to conflict with Cocoa's naming conventions because I do not have a property called predicateWithDate. What do folks usually name these sort of methods?
I was thinking "applyPredicateWithDate:"
Thoughts?

Naming a method that starts with set is absolutely fine. Not all methods that start with set has to be a setter directly to the instance variable named after set, it should just do what it says and change the ivars in your class accordingly, in which your case is perfectly fine.

Related

Correct implementation of NSView drawRect in derived classes

Is it necessary to call [super drawRect:dirtyRect] from overridden drawRect?
I have seen examples where [super drawRect:dirtyRect]
is not being called at all
-(void)drawRect:(NSRect) dirtyRect
{
// derived class code here
}
is being called before derived class code
-(void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// derived class code here
}
is being called after derived class code
-(void)drawRect:(NSRect)dirtyRect
{
// derived class code here
[super drawRect:dirtyRect];
}
Are all correct (especially not calling super drawRect) as per standard or specification OR they just happen to be working and may break some time. I mean is it a simple case of inheritance where derived class must override keeping base class behavior in consideration?
An answer with reference would be helpful.
Per NSView Docs
The default implementation does nothing. Subclasses should override this method if they do custom drawing.
...
If your custom view is a direct NSView subclass, you do not need to call super. For all other views, call super at some point in your implementation so that the parent class can perform any additional drawing.

How to notify parent class from child?

I'm trying to resolves one architectural issue, but I want to find best suitable approach.
I have game scene GameLayer.h which is my view in this case. I have also GameEngine class which I'm trying to be controller.
I have my custom Egg class derived from CCNode and some timer logic implemented via schedule in Egg class.
I have many Egg object on game scene added via addChild method.
So GameEngine.m imports GameLayer.h, and GameLayer.m imports Egg.h
After timer in each Egg runs out, I want to be notified, i.e run some actions, increase score etc.
Question is what is the best way to implement such situation. Also I want to obey MVC rule
Quick solutions which comes in my mind are
Include Gamelayer.h in Egg class and call some update method in it. Update view (self) and notify GameEngine to save new score.
Include Gamelayer.h in Egg class and call some update method in it. In update method call another update method which is now in GameEngine.h and now from GameEngine save score, and update Gamelayer (the view)
Implement KVO. Problem is that I never done it before, I don't know observer should be GameLayer or GameEngine
Please help, any examples are appreciated
More options:
1.
Use NSNotificationCenter. Your Eggs will post notifications, and your GameLayer will observe.
2.
Use a protocol. Example:
// in Egg.h
#class Egg;
#protocol EggDelegate
// an example method
-(void)egg:(Egg *)egg hadSomethingHappen:(int)parameter;
#end
#interface Egg: CCNode
#property (weak) id<EggDelegate> delegate;
#end
// in Egg.m
/// something happened and delegate has to be notified?
[self.delegate egg:self hadSomethingHappen:someNumber];
Then declare GameLayer to support this protocol:
#interface GameLayer : CCLayer <EggDelegate>
implement protocol's methods in GameLayer and make it delegate of your Egg objects:
// somewhere in GameLayer
Egg *egg = [Egg node];
egg.delegate = self;
...
//protocol implementation
-(void)egg:(Egg *)egg hadSomethingHappen:(int)parameter
{
// do something
}

Saving changes to a NSManagedObject

I have a problem with making changes to ManagedObjects and saving those changes to the persistent store.
What does work is deleting objects, inserting object. and fetching those objects. As said, i fail making a change to a fetched managed object.
I have two view controllers.
Both have a public property:
#property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
The AppDelegate.m file sets this property for the first view controller in the application:didFinishLaunchingWithOptions: method like so:
// get reference to view controller //
//..
controller1.managedObjectContext = self.managedObjectContext;
view controller1 again passes the managedObjectContext when pushing to the second view controller, it does this in the prepareForSegue: method like so:
// Get reference to the detail view //
MySecondViewController *controller2 = segue.destinationViewController;
// Pass the managed object context //
controller2.managedObjectContext = self.managedObjectContext;
Within this second view controller I fetch objects from core data and I store them in a property of the view controller like this:
#property (nonatomic, strong) MyKindOfObject *object;
...
self.object = [fetchResults lastObject];
Fetching objects seems to work fine as i nicely get results..
The fetched object has a number of properties, one of them is of type NSString. The value of this property I show in a UITextField. The user can change the value here and when done he presses a button. I catch the action and I do the following:
self.object.mytext = textField.text;
followed by trying to save to core data:
// Save to CoreData //
NSError *error = nil;
if(![self.managedObjectContext save:&error]){
// handle the error //
NSLog(#"is error");
}
else{
NSLog(#"no error");
}
1) The next time the user returns to this view controller, again the object will be fetched. But it still has the old value.
2) Also I use a Firefox add-on called SQLite Manager to keep an eye on the data within the related sqlite file. When stepping through the code, after calling the save method nothing changes in the file.
3) Also within Xcode i use the Variables View to keep an eye on the self.managedObjectContext object. When I am storing the new data into my object, right before calling save, none of the properties of self.managedObjectContext change (like unprocessedChanges or changedObjects).
4) I also added a call to the hasChanges method of the managedObjectContext, but this returns NO:
if([self.managedObjectContext hasChanges]){
NSLog(#"changes managed object context!");
}
5) I also added a call to the hasChanges method of the related managedObject, but this also returns NO:
if([self.object hasChanges]){
NSLog(#"changes in managed object!");
}
I am probably doing something totally wrong here but I can not figure out what it is.
I truly hope somebody can help me out?.
Thanks in advance!
Oke let me answer my own question: as the newby that I am, i indeed did something totally wrong. I was thinking I knew better then apple themselves. lesson learned: I do not!
After creating entities within the model I created custom managed object classes for each entity (or actually NSManagedObject subclasses for each entity).
Within the generated implementation files all properties (attributes) are implemented as #dynamic. Due to lack of knowledge I did not understand/recognise this. So the newby in me thought: lets change all that to #synthesize.
Hopefully this post can help some other newby to not make the same mistake.

Bind a NSTextField to an object instance method?

I have an instance method on an object that spits out a string based on some other objects. Can I bind this instance method to a field?
Create an NSString property, open the nib file and bind a text field's value to the property. Then any changes you make to the property will be reflected in the text field.
You can bind the getter instance method to the field's value only if you implement a corresponding setter method for the binding to try to write changes in the value to. (No setter needed for some other binding types)
It is okay for the setter to be empty.
For example this is used to display the number of new items across an instance's subtree:
- (NSNumber*) numberOfNewItems
{
NSUInteger total = 0;
for (childItem* ci in self.items)
total += (ci.isNewValue) ? ci.items.count : 0;
return #(total);
}
// Does nothing but allows binding to above to be recalculated
- (void) setNumberOfNewItems:(NSNumber*)number { }
That makes a 2-way bindings happy. If you did not have the setter, you would get errors relating to the keypath not being KVC compliant. (Unless you are binding to something one-way like "title" or "tool tip" -- those don't need setters. But the setter comes in handy to get those titles and tooltips recalculated through the getter)
Note that within the childItems, you can then just do this:
self.isNewValue = NO;
[self.parent setNumberOfNewItems:nil]; // Trigers KVO for binding (does nothing else)
... which is a lot easier than trying to have the parent register as a KVO observer for all the children.
And if you want other properties in the instance to retrigger the getter, you can add this class method:
+ (NSSet *)keyPathsForValuesAffectingNumberOfNewItems {
return [NSSet setWithObjects:#"items", #"somethingElse", nil];
}

Add editing behaviors to bound NSFormCell

I've a Core Data model class with a customer ID attribute. It's bound to a form cell. When the user finishes editing the text I want a chance to convert their entry to upper case, using logic which depends on the old and new values.
Ideally I want to keep the behavior close to the view where it belongs, using an object I can instantiate in the nib and hook up to the text cells. But I'd settle for an object I had to hook up to the model.
I've implemented this three different ways:
Custom setter method in the model class
Text editing delegate implementing NSControlTextEditingDelegate
Helper class which uses KVO to notice the change and initiate a subsequent change
All three implementations have problems. The issues, respectively:
This behavior doesn't belong in the model. I should be able to set the attribute in code, for example, without triggering it.
I can't get the "before" value because the form cell doesn't provide controlTextDidBeginEditing: calls (and the old value is gone by the time controlTextDidEndEditing: is called). Furthermore tabbing in and out of the field without typing anything triggers a call to controlTextDidEndEditing:.
When the observation fires for the user's change, and I initiate a subsequent change to that property, the view ignores the change notification and doesn't redraw. (I presume the binder does this for efficiency. Normally when updating the model, it can ignore the KVO observations from the field being updated.)
How would you solve this problem?
After some discussion here, it sounds like some possible ways to do tho:
Put a category on the model class and override validateMyKey
Subclassing NSFormCell
I tried both. More issues:
validateMyKey isn't called until after the model updates itself, so the old value isn't available.
editWithFrame:inView:editor:delegate:event: isn't always called upon entering a field, so it's difficult to access the old value in endEditing:.
New solution is a refinement on my original #2: text editing delegate implementing NSControlTextEditingDelegate.
Instead of controlTextDidBeginEditing: and controlTextDidEndEditing:, implement only control:textShouldEndEditing:. In that method, manipulate the text if necessary, then return YES.
I instantiate this in the nib and make it the form's delegate (not the cells'). In the code below I get the old value using infoForBinding: but if you aren't using bindings, you could add an outlet to the model object instead.
-(BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor {
NSCell *cell = [(NSForm *)control selectedCell];
NSString *identifier = [(NSCell *)[(NSForm *)control selectedCell] identifier];
if (!identifier) return YES;
NSDictionary *bindingInfo = [cell infoForBinding:#"value"];
if (!bindingInfo) return YES;
NSString *oldValue = [[bindingInfo valueForKey:NSObservedObjectKey] valueForKeyPath:[bindingInfo valueForKey:NSObservedKeyPathKey]];
NSString *newValue = cell.stringValue;
if ([identifier isEqualTo:#"firstField"]) {
if (criteria)
cell.stringValue = ....;
} else if ([identifier isEqualTo:#"secondField"]) {
if (criteria)
cell.stringValue = ....;
}
return YES;
}

Resources