I have a UITextFieldDelegate that performs some formatting as you type by implementing textField(textField:shouldChangeCharactersIn:replacementString:) -> Bool.
I would like to write a few unit tests for this to check behaviour as key presses are sent, and as deletes are sent. Ideally, I'd do this by creating a UITextField in a test, attaching the delegate, and sending key events to it. I don't know how I should send key events to a UITextField programatically, though.
I understand I could unit test the delegate directly, but I want to test its interaction with the real UITextField implementation.
I would prefer to avoid UI tests if possible.
Thank you.
Related
I have the following code in a Cocoa application:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSArray* arrayAppList = [[NSWorkspace sharedWorkspace] runningApplications];
}
My intention is to use KVO to detect an application when changes its state between inactive to active.
I read that I have to use the instance method -addObserver:forKeyPath:options:context:
And then use -observeValueForKeyPath:ofObject:change:context: to respond to change notifications.
I understand that -observeValueForKeyPath is a callback method where I can write code to respond to the properties changes I am interested in.
Nevertheless, I feel confused in how I must to use the addObserver method in order to be notified when the active property of the runningApplications change. Now, I am wondering where is the place to make the registration, for now I am using -applicationDidFinishLaunching but not sure if is the right place to do it. Additionally if I use the -observeValueForKeyPath callback method, I have to implement it in the class that inherits from NSObject and is the same class where I am registering the notification?
You should call the addObserver:… method on each object in the runningApplications array (using isActive as the key path).
Starting the observing after your app finishes launching sounds about right. Time-wise, that is. As for the place, there should be a separate class dedicated to these observations. By implementing the observation code right in the app delegate you would violate the single-responsibility principle (and that means headache in the long term).
The observeValueForKeyPath:… callback should be implemented by the object that called the addObserver:… methods.
So I'm building a program that features the use of the IKImageBrowserView component as a subview in an NSWindow. As a side note, I have a controller object called ImageBrowserController which subclasses NSWindow and is set as the delegate of the NSWindow object of my app.
I have sent IKImageBrowserView the message setCanControlQuickLookPanel:YES to enable it to automatically use the QuickLook functionality to preview image files when the IKImageBrowserView is a first responder to receive key events. Then it took me a while to figure out how to make the IKImageBrowserView a first responder which I finally got working by overriding acceptsFirstResponder inside my ImageBrowserController.
Now I understand that as the delegate to the NSWindow, ImageBrowserController has a place in the responder chain after the event gets triggered on NSWindow. And I understand that as a subview of NSWindow, IKImageBrowserView is in line to be passed events for event handling. What I don't get is where the connection is between the ImageBrowserController being a first responder and the event somehow making it to the IKImageBrowserView. I didn't set NSWindow or IKImageBrowserView as first responders explicitly. So why isn't it necessary for me to implement event handling inside my ImageBrowserController?
EDIT: So after reading the accepted answer and going back to my code I tried removing the acceptsFirstResponder override in my ImageBrowserController and the QuickLook functionality still triggered just like the accepted answer said it would. Commenting out the setCanControlQuickLookPanel:YES made the app beep at me when I tried to invoke QuickLook functionality via the spacebar. I'm getting the feeling that my troubles were caused by user error of XCode in hitting the RUN button instead of the BUILD button after making changes to my code (sigh).
Some of what you are saying regarding the interactions between your objects does not make sense, and it is hard to address your stated question without some background.
As you say, your window delegate has a place at the end of the responder chain, after the window itself. The key point I think you are missing is that GUI elements, such as your IKImageBrowserView, will be at the beginning of the chain, and any one of them in a given window could be the current firstResponder.
When your application gets an event, it passes it off to the key window (which is just the window which currently accepts "key" (i.e., "keystroke") events). That window begins by asking its firstResponder to handle the event. If that object refuses, it passes the event to its own nextResponder, usually its superview, which either handles it or passes it on, until the event has either been handled or passed all the way up to the window object itself. Only then will the window (if it does not handle the event itself) ask its delegate to handle the event.
This means that the connection between the window delegate and the IKImageBrowserView is only through the Responder Chain, and its nature is simply that if the view declines to handle any given event, the delegate may eventually be asked to handle it, if no other object in between them handles it first.
Your window delegate does not need to be a firstResponder. Nor does overriding acceptsFirstResponder on the window delegate have any effect on one of the window's subviews.*
Your window delegate also does not need to (and, indeed should not) be a subclass of NSWindow. All it needs is to be a subclass of NSObject which implements whatever methods from the NSWindowDelegate Protocol you are interested in, and methods to handle any events you might want to catch if they are not handled by other objects.
So, the answer to your explicit question at the end is (and I do not mean this sarcastically): you only need to implement event handling in your window delegate if you want it to handle events itself.
*: IKImageBrowserView already responds YES to acceptsFirstResponder. If there are no other subviews in your window, it will automatically be the firstResponder when your application starts. You can set this initialFirstResponder explicitly in Interface Builder by connecting that outlet on the window to whatever object you want.
I'm a little rusty on my Cocoa, so bear with me on terminology and such.
I want to write something that is essentially a reverse spell checker. As in, if a word is spell correctly, then for random words it changes it to a misspelled version. Harmless prank to play on someone.
So then, my main hitch is that I have no idea how to do this (major problem, I know). I like the way that textedit performs on-the-fly spellchecking, but I'd like to incorporate that sort of behavior into the generic behavior of the NSTextField. Is there some way for an application to be notified whenever a character is input into an NSTextField?
EDIT: My aim is to make this system-wide, as in any NSTextField in the system would get this behavior as a matter of inheritance. I'm open to some serious hacking here.
To answer your question: attach a delegate to the NSTextField control, and implement
- (void)controlTextDidChange:(NSNotification *)aNotification
Note that NSTextField uses the shared field editor to edit text, which is a NSText object. You might be able to manipulate that to do what you want.
I like the idea! This should be fairly easy to do. First you have to decide if you want to use NSTextField or NSTextView. TextEdit.app uses NSTextView which is appropriate for more extensive word processing-type tasks. NSTextField is more for smaller, minimally-formatted chunks of text. There's lots of ways to tackle this, but I'll give you a couple.
For NSTextField, set your controller object to be the delegate for the text field and override the controlTextDidChange: method. Whenever the user types a character into the text field, you'll get this message. You can then modify the field's string to introduce the misspelled word.
For NSTextView, you can activate spell checking and use the text view's delegate method textView:didCheckTextInRange:types:options:results:orthography:wordCount:. You should be able to modify the results of the spell check.
In Cocoa, how do you define a class that sends an action? I want to be able to connect the action to the selector of another object in IB in the style of NSButton. I would prefer not to subclass NSControl if possible.
Give it a property (informal is fine) holding an id, for the target. I'm not sure whether this should retain or not; I'd say no, since the target will normally be the controller that owns the window that (indirectly) owns the view.
Give it a property (informal in fine) holding a SEL, for the action.
Respond to mouseUp: and keyDown: (checking that the key in question is the space bar or return or enter) by sending yourself an accessibilityPerformAction: message, passing NSAccessibilityPressAction.
Respond to the accessibilityPerformAction: message by either sending your action message to your target (NSAccessibilityPressAction) or calling up to super (other), as described in the documentation for that method.
You should also implement the rest of the NSAccessibility protocol while you're at it. Test that work with a mix of the Accessibility Inspector and VoiceOver.
I'm learning how to build programs with Cocoa. I'm using a sample Apple application that records video from a webcam. I'd like to start and stop the video by capturing the key press. I've tried to override the keydown event but I've read that it's not possible in an NSObject. How can I handle this kind of event?
The class of application extends a NSObject class.
This is the code:
- (void)keyDown:(NSEvent *)event {
NSLog(#"Hi there");
NSString *characters = [event characters];
if ([characters length]) {
switch ([characters characterAtIndex:0]) {
case NSUpArrowFunctionKey:
NSLog(#"Key UP");
break;
}
}
}
I've tried to override Keydown event but I've read that It's not possible in an NSObject.
Correct. Only a responder can respond to events.
How can I handle this kind of event?
Implement a responder. Subclassing NSWindow or NSWindowController will work. Make sure you make your actual window or window controller an instance of your subclass.
The Cocoa documentation explains further.
The class of application extends a NSObject class.
Why? Normally, the principal class of the application bundle is NSApplication or a subclass of that—and there aren't many good reasons to subclass NSApplication.
PS: What's a very good book to start learn MacOS Programming?
I didn't learn by the Hillegass book, myself (I stuck to Apple's docs), but it's a very popular recommendation and I have read it and can tell you it's good.
From the Cocoa Event-Handling Guide - The Responder Chain:
The responder chain is a linked series of responder objects to which an event or action message is applied. When a given responder object doesn’t handle a particular message, the object passes the message to its successor in the chain (that is, its next responder).
When you press a key the window receives the keyDown event. Then it dispatches the event to the first responder, that usually is the control with a blue bezel around its border (try to click on the address field in Safari or Firefox, when it's blue-bezeled then it has first-responder status).
If the first responder does not eat the keypress (the Safari address field does eat it when it displays a character) then it passes it down the responder chain to the next responder in the view hierarchy, then to the window and to the window controller as you can see in the Guide. (Take care that the action responder is another story.)
So you have to implement the keyDown: on a view of your window or in the window itself, if it has no views that eat events. The simplest way to test is to override the keyDown: method of an empty window
To put your hands into the inner workings you can even try overriding the sendEvent: method of a window. sendEvent: dispatches the events to the views of the window, and from there you can for example log all the events managed by the window.
Subclassing NSWindow or
NSWindowController will work.
Similarly, you can subclass NSView and override its event-handling methods.
What's a very good book to start learn
MacOS Programming?
Learn Objective-C on the Mac by Dalrymple is really straightforward, covers enough basics and moves fast enough to get you off the ground in a short bit. It touches on everything from Xcode and Interface Builder to OOP and Objective-C practices. Particularly helpful to beginners (IMHO) are the source file organization and Foundation kit chapters.
Best of luck!