How to override a small part of a method? - cocoa

I have these methods in a class at the moment which seem to work fine. I would like to create a subclass which inherits these methods. The problem I have is that in the third method (shiftViewUpForKeyboard) I want the if statement to be a different UITextField (mirror being the current example).
I've read that to override a method in the subclass, you have to basically copy it exactly with the new coding, but if I want to just change that small section what is the best way to do it?
Thank you in advance.
- (void) viewWillAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(shiftViewUpForKeyboard:)
name: UIKeyboardWillShowNotification
object: nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: #selector(shiftViewDownAfterKeyboard)
name: UIKeyboardWillHideNotification
object: nil];
}
- (void) viewDidDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] removeObserver: self
name: UIKeyboardWillShowNotification
object: nil];
[[NSNotificationCenter defaultCenter] removeObserver: self
name: UIKeyboardWillHideNotification
object: nil];
}
- (void) shiftViewUpForKeyboard: (NSNotification*) theNotification;
{
if(mirror.isEditing == YES)
{
CGRect keyboardFrame;
NSDictionary* userInfo = theNotification.userInfo;
keyboardSlideDuration = [[userInfo objectForKey: UIKeyboardAnimationDurationUserInfoKey] floatValue];
keyboardFrame = [[userInfo objectForKey: UIKeyboardFrameBeginUserInfoKey] CGRectValue];
UIInterfaceOrientation theStatusBarOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if UIInterfaceOrientationIsLandscape(theStatusBarOrientation)
keyboardShiftAmount = keyboardFrame.size.width;
else
keyboardShiftAmount = keyboardFrame.size.height;
[UIView beginAnimations: #"ShiftUp" context: nil];
[UIView setAnimationDuration: keyboardSlideDuration];
self.view.center = CGPointMake( self.view.center.x, self.view.center.y - keyboardShiftAmount);
[UIView commitAnimations];
viewShiftedForKeyboard = TRUE;
}
}

In any case if you don't want to copy the full method in the subclass, and adding your little customization, the only other possible approach I see is to change the original class. To do it I can suggest two possibilities:
1)
You could create in the original class a method called:
-(UITextField *)keyboardShiftTextField
and then in this class replace the mirror.isEditing code with:
[[self keyboardShiftTextField] isEditing]
In such case the only difference between the two classes will be in the implementation of the new method, that for the original class will be:-(UITextField *)keyboardShiftTextField {
return mirror;
}
while in the subclass this return the right text field.
2)
A second approach is more elegant as it requires the definition of the delegate pattern. This requires some overhead in term of code but we'll provide you more flexibility. Besides if the only reason to make the subclass is just to override this third method, then using the delegate pattern you can avoid creating the subclass at all, as the "custom" work will be done by the delegate. If the number of methods to override is more than one, you can still use this mechanism by moving into the protocol section all the parts that need customization. This is a quite common technique for Obj-C and Cocoa, which limits the need for some classes in many cases. Typically you use a subclass when you want to provide a different functionality, but in your case you're not providing a different functionality, but just a customization for the same functionality (the view shift up).

The usual approach would be the http://en.wikipedia.org/wiki/Template_method_pattern: pull out just the bit of the method that varies between classes, make that a separate method, and override that in subclasses.
[EDITED to add ...] An alternative approach -- I can't tell whether it would work well here without seeing more of your code -- would be to make the thing that varies a parameter that's passed into the method (or select it on the basis of a parameter passed into the method, or something else of the kind). (You'd typically then use other mechanisms rather than inheritance+polymorphism to get the effect you want, of multiple things with similar behaviour: they'd be instances of the same class but fed with different data.)

Related

How to send a message to the Master Class of a class?

Quick Question:
I am creating an object from the MainWindowController:
about = [[About alloc]init];
In the Class About I do my Init:
-(id)init{
if(!_viewAbout){
[NSBundle loadNibNamed:#"About" owner:self];
[NSApp beginSheet:self.viewAbout modalForWindow:*?????* modalDelegate:self didEndSelector:NULL contextInfo:NULL];
}
return self;
}
My problem is that the Window is created in the MainWindowController. My question is how to call/send a message to the creator of the class if the class itself doesn't know the master class?
If I understand you correctly, most classes have self.superclass and just super, like
[super someMethod....
or
[self.superclass blegh....
Or are you asking for the class that creates another class ? If that is the case, you need to declare the creator class inside the other one, some (id) variable would do the trick.
But the most popular design pattern on the mac is the delegate pattern, and once you start using that you will love it. Declaring a delegate is usually the way Cocoa and UIKit do things, but other programming languages might not. Obj-C doesn't have any magic variables like python f.ex. has. Either you have a delegate or you have a declared variable which you would set right after the init/alloc stuff.
Also your (init) call doesn't look right. Usually it looks like :
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Here you could declare your setting.
}
return self;
}
But my opinion is that if you are declaring a singular pattern, you would call a specific method in your class, like you do with so many classes on the iOS/Cocoa, like :
[someclass DefaultClass]
This would be your init class where you would do init, unless the class had been declared before and then you would just return the object.

Xcode override all methods

I want override all methods of a subclass automatically on xcode, for example I have a class extended of UiViewControler, how I override all methods of UiViewController on xcode to be more or less well:
- (id) init
{
return [super init];
}
My intention with this is to log all methods to see when they are called, then my methods will be more or less well
- (id) init
{
[self log];
return [super init];
}
where log is as follow method:
-(void) log
{
NSLog(#"%#",[(NSString *) (NSArray *) [NSThread callStackSymbols][1] componentsSeparatedByString:#"-["][1]);
}
thanks a lot!
In this case you don't have to do anything. If you don't provide an implementation, then the superclass's implementation will be used.
Edited after the question was edited
If you put the log statement in the superclass's implementation then it doesn't matter what you do with your own initialiser.
Why?
One of the many conventions in Cocoa is that each class has a designated initialiser. All the other designated initialisers then call this initialiser. And when you subclass the class, then you create a new designated initialiser for the new class, and as part of the initialisation - this calls the superclass's designated initialiser.
Which is why you see NSObject subclass initialisers calling [super init], because NSObject's designated initialiser is init.
So, just call your logging method in the designated initialiser of your class, and as long as you follow the above convention, this initialiser will always be called by a subclass, and so your logging method will always be called.

How to manage multiple windows in Cocoa apps with Interface Builder

I have this application with 3 classes: AppController, Profile, ProfileBuilder. I also need 3 windows: one for each class. I tried keeping all 3 as subclasses of NSObject and applying initWithNibName to an NSWindowController class WindowController variable but when I tried outputting some values on each window it wouldn't work, and also the window resulted as null using NSLog. I was wondering what was the best way to manage multiple windows, perhaps all from a same class like an AppWindowsController involving as least as possible specific code in the other classes, and keeping, if possible, the other classes as subclasses of NSObject and not NSWindowController. So if there is, maybe a way to control the behavior of the windows remotely, adding as least as possible code inside the specific classes, just to keep them as clear as possible and uniquely focused on their content. Thanks, hope I made myself clear, I'm actually pretty new to the Cocoa framework.
You should be able to load the nib files with your windows in an init method for your different classes. For example, in Profile, you could do something like this:
-(id)init {
if (self = [super init]) {
NSArray *array;
BOOL success = [[NSBundle mainBundle] loadNibNamed:#"ProfileWindow" owner: self topLevelObjects:&array];
if (success) {
for (id obj in array) {
if ([obj isKindOfClass:[NSWindow class]]) {
self.profileWindow = obj;
}
}
[self.profileWindow makeKeyAndOrderFront:self];
}
}
return self;
}
profileWindow is a property (typed as strong). In the xib file, I set the File's Owner to Profile.
I just like to improve the solution of rdelmar.
You don't need to iterate over the array to find the NSWindow class.
If you define profileWindow as an outlet and connect it in the IB, the call
[[NSBundle mainBundle] loadNibNamed:#"ProfileWindow" owner:self topLevelObjects:&array];
will assign the window object to your outlet, the array stuff is not required.
The key here is the owner object which act as interface. In the IB you can define the class type of the owner and if so, see its outlets.

NSKeyedArchiver: distinguishing between different instances of the same class

I'm implementing support for Lion's "Resume" feature in my OS X app.
I have a custom subclass of NSViewController in which I implemented the method
encodeRestorableStateWithCoder: as:
#implementation MyClass (Restoration)
-(void)encodeRestorableStateWithCoder:(NSCoder*)coder {
[coder encodeObject:_dataMember forKey:#"object_key"]; // I get the warning below when this line is executed for the second time
}
- (void)restoreStateWithCoder:(NSCoder *)coder {
_dataMember = [coder decodeObjectForKey:#"object_key"];
}
#end
However, since I have multiple instances of MyClass, different values are saved into the same key ("object_key") and I get the following warning from Cocoa:
NSKeyedArchiver warning: replacing existing value for key
'object_key'; probable duplication of encoding keys in class hierarchy
What is the best practice to overcome this problem?
Edit: I found here that each instance automatically has its own namespace to avoid collisions, so the problem might be in the way I'm manually calling encodeRestorableStateWithCoder to different instances with the same NSCoder object without telling it that these are different instances. However, I still can't figure out how to do that properly.
Thanks in advance!
To overcome this problem, it is possible to create a new NSMutableData where each of which is written by a separate (new) NSKeyArchiver, and store them all in an array which is stored in the original NSCoder object.
Here is an example for encoding the restorable state of subitems. The decoding part can be straight-forward given this code.
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder
{
[super encodeRestorableStateWithCoder:coder];
// Encode subitems states:
NSArray* subitems = self.items;
NSMutableArray* states = [NSMutableArray arrayWithCapacity: subitems.count];
for (SubItemClass* item in subitems)
{
NSMutableData* state = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:state];
[item encodeRestorableStateWithCoder:archiver];
[archiver finishEncoding];
[states addObject:state];
}
[coder encodeObject:states forKey:#"subitems"];
}

How to add a method to an existing protocol in Cocoa?

I want to extend or add another method to an existing protocol. Although the protocol in particular is not important, this is what I am trying to do.
#protocol NSMatrixDelegate
- (void)myNewMethod:(id)sender;
#end
The compiler warns that I have a duplicate declaration of the same protocol. How would I do this properly?
Thanks.
You can't define categories for protocols. There are 2 ways around this:
use a new formal protocol
use an informal protocol and runtime checking
Formal Protocol
Defining a new formal protocol would look like this:
#protocol MyCustomMatrixDelegate <NSMatrixDelegate>
- (void) myNewMethod:(id)sender;
#end
Then you would make your custom class conform to <MyCustomMatrixDelegate> instead of <NSMatrixDelegate>. If you use this approach, there's something to be aware of: [self delegate] will likely be declared as id<NSMatrixDelegate>. This means that you can't do [[self delegate] myNewMethod:obj], because <NSMatrixDelegate> does not declare the myNewMethod: method.
The way around this is to retype the delegate object via casting. Maybe something like:
- (id<MyCustomMatrixDelegate>) customDelegate {
return (id<MyCustomMatrixDelegate>)[self delegate];
}
(However, you might want to do some type checking first, like:
if ([[self delegate] conformsToProtocol:#protocol(MyCustomMatrixDelegate)]) {
return (id<MyCustomMatrixDelegate>)[self delegate];
}
return nil;
)
And then you'd do:
[[self customDelegate] myNewMethod:obj];
Informal Protocol
This is really a fancy name for a category on NSObject:
#interface NSObject (MyCustomMatrixDelegate)
- (void) myNewMethod:(id)sender;
#end
Then you just don't implement the method. In your class that would send the method, you'd do:
if ([[self delegate] respondsToSelector:#selector(myNewMethod:)]) {
[[self delegate] myNewMethod:someSenderValue];
}

Resources