I have an NSTableView where I'd like to be able to highlight 1 or more rows, and then hit the delete key to delete them, or hit ⌘+C to copy them to the pasteboard.
I've found a suggestion to subclass the NSTableView and then code up a method for
-(void)copy:(id)sender
The documentation says that subclassing the NSTableView is rarely necessary. Instead, use the dataSource or delegate, or subclass a subcomponent. The delegate and dataSource protocols don't handle menu commands or keyboard short cuts. If I try to subclass anyways, I run into a problem where the subclass needs to send a message to the delegate, but my custom methods aren't part of the protocol, so the compiler complains. Rather than fight the design pattern, I'd rather know what the "correct" approach to this problem is.
I solved my problem by declaring the NSTableView's parent window's delegate as the NSTableView's controller (which is also its dataSource and delegate). Now it receives menu actions as part of the responder chain.
Related
Ok, so I have this issue where I need to get access to the headers/footers displayed in a UITableView. Moreover, I need to do this from a subclass of UITableView (so I can't simply assign tags to the view from the UITableView Delegate). UITableView does keep an array of visible headers and footers but it provides no access to those arrays even to the subclass, which I personally think is asinine.
I need this so that I can provide a custom drag-n'-drop insertion/move user interface. I'm trying to get it to work almost exactly like Apple's own rearranging interface, but of course, with my own custom implementation (like the ability to drag from another table). Everything works perfectly except for the headers/footers.
At the moment I'm trying to hack it by iterating through all the subviews of UITableView. I'm pretty sure that the only subviews of UITableView is: backgroundView, UITableViewCells, and Headers/Footers. So I just need to determine which are UITableViewCells (very easy), which is the background view (also easy) and which are headers/footers (not so easy). I'm pretty sure I can do it this way but it's definitely a hack and I'd rather find a better way if possible.
UPDATE
I'm overriding the - (void) setDelegate:(id<UITableViewDelegate>)delegate method and checking to see if the delegate responds to the appropriate selectors to generate headers/footers (and set BOOL's appropriately). This is making it a lot easier to determine what a particular subview is since the progression of header -> cells -> footer is predictable.
You say you can't use UITableView delegate methods, but did you consider letting the UITableView subclass object be its own delegate? Create a separate property on the subclass called realDelegate and pass any other delegate calls through to that.
I believe that UIView objects can delegate control to views in iOS programming. However, I thought this was already handled by an IBAction associated with an IBOutlet corresponding to the UIView.
In that case, what does it mean to drag a blue line (while pressing Ctrl) from the delegate circle of the UIView in my xib to File Owner?
Isn't the UIView already delegating to my view controller via an IBAction? The code seems to work if I don't specify anything for my UIView's delegate.
"Isn't the UIView already delegating to my view controller via an IBAction? The code seems to work if I don't specify anything for my UIView's delegate".
It looks like you're confusing the idea of the Target-Action Mechanism with delegation. While you could in a way think of target-action as involving “delegation”, it would only be in the most general sense of the word in that it’s the target (generally a controller) that handles and implements the action (the message). However, a Cocoa programmer would probably refrain from explaining the target-action mechanism by using that particular word – delegation – as it would be too easy to confuse it with actual Delegation.
When you “wire up” buttons and controls in the nib file to call IBAction methods in your controller object, that’s just the target-action mechanism at work. As you saw, the code works even if you "don't specify anything for my UIView delegate”, since target-action doesn’t involve delegation (in the Cocoa-sense of the word).
Delegation is generally used as an easier alternative to using subclassing to implement the desired behavior. For example, instead of having to create your own custom subclass of UITableView in order to have it know what data you want it to display, you can simply use the plain UITableView class, set its delegate to a controller class, and have the controller class implement the required/desired designated delegate methods. Using subclassing to accomplish that could potentially get complicated, since when you subclass, you need to have a full understanding of the inheritance chain and how all of the superclasses work.
File's Owner is just a fancy way of disguising the name of the class that the XIB is taking it's references from. When you drag the delegate outlet to your class, it's the equivalent of this line:
[self.myControl setDelegate:self];
When you drag while pressing CNTRL from an IBOutlet or IBAction to a UILabel, UIButton or something else, you are binding events or outlets to your controller.
A delegate sort of a controller that must implement specific functions depending on which type of delegate it is. For example, the UIViewController, which implements the UIViewDelegate protocol, can implement
-(void)viewDidLoad
the YourApplicationDelegate, which implements UIApplicationDelegate must implement
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
So a delegate is just a set of functions which define the behavior of an object, the classes that implement them can change this behavior.
I have subclassed UIScrollView and added my own custom delegate (currently called delegate). I want to add functionality such as notifying a user when an image is tapped and perform loading like the table view.
However, If I define my own custom delegate, I can not set the parent delegate for responding to events such as scrollViewDidScroll (I want the subclass to receive these events). Should I rename the delegate in my subclass? Should I be using super.delegate in some form? Does a standard exist for doing this? Thanks!
Does your custom delegate just extend the methods of UIScrollViewDelegate? If it does, just override the methods -scrollViewDidScroll etc and forward the messages onto your own delegate.
Maybe I've misunderstood your question, so I apologize if I have.
As a newcomer to Cocoa, I am struggling to understand why the generic NSResponder subclasses implement key events the way they seem to do.
In my program, I have an NSWindow subclass which takes up the whole screen, and must necessarily handle key events. There are several major commands which can change the whole state of the program (e.g. pause a timer when the user hits the spacebar) which it does not make sense to have subviews like an NSTextField handle.
It seems to me that the delegate (controller) should get these events. Instead, I find I have to either write a bunch of messy glue code to have the window (via its keyDown: and interpretKeyEvents: selectors) notify the controller, or I have to just move a bunch of controller code to the NSWindow subclass itself.
This is messy and my gut tells me I'm missing something. Is there a cleaner solution?
If you've set it up correctly, the NSWindow's delegate will receive the messages. Cocoa uses the responder chain to forward messages from the first responder -- the key view for key messages, and the view that was clicked/hovered/etc. for mouse messages -- back through the superviews, up through the window, and eventually to the window's delegate. There's a pretty good diagram of the typical responder chain on Apple's site.
You really should never have to subclass NSWindow unless you're implementing some fancy window drawing or something else along those lines. Cocoa provides the NSWindowController class to behave as a controller for a window and its contents.
The usual pattern is to subclass NSWindowController and add your IBOutlets to it, and then use a NIB to lay out your window contents. You make your NSWindowController subclass the class of the File's Owner proxy in Interface Builder. And you also assign the window's delegate to the window controller so that it can become part of the responder chain. Finally, to create windows, you use NSWindowController's initWithWindowNibName: method, which automates loading the NIB with a new window controller as the file's owner.
I'd recommend reading up on window controllers in the Cocoa documentation, because they provide the missing link you're looking for.
Here's a simplification:
I have an application with several buttons. If it is the first time the application is launching, I want to do some special things. In my AppController class, which is a delegate of NSApp, I use the delegate method -applicationDidFinishLaunching: to perform the test. After I've detected that it is the first time, I first want to access some IBOutlets. Then, I'd like to be able to get mouse events for each button, so that I can do other things.
I can't figure out want to do with the classes. I'd like to make a new class (FirstLaunch) for the first launch, but I'm not sure what to call from AppDelegate. Also, to get mouse events, shouldn't I be a sublass of the buttons, and considering that I have multiple buttons, I'm confused. I could probably tackle these issues one-by-one, but taken all together, they're confusing me.
Broken down, I need to access & manipulate IBOutlets I have set in IB, determine when buttons are clicked (and which button was clicked). I'd like to be able to do this from another class so as to not clutter up the AppDelegate.
Thanks for the help!
To be more clear, what I'm actually trying to do is to use Matt Gemmel's MAAttachedWindow to put up a help bubble by a button. When the button is clicked clicked, the bubble disappears and another one is put somewhere else. The bubbles will be attached to controls in the main window.
I'm guessing you want to show some additional user interface on the first launch? If it's a separate window, I'd advise creating a subclass of NSWindowController. Add a new NIB file for the first-run user interface to your project and change the class of the File's Owner object to FirstLaunch. Control-drag a wire from the File's Owner delegate onto the window to connect it with the window outlet.
You create IBOutlets by adding an instance variable to the class. If your app will only run on Leopard or higher, it's better to declare your outlets like this:
#interface FirstLaunch : NSWindowController {
NSTextField *myTextField;
}
#property (nonatomic, retain) IBOutlet NSTextField *myTextField;
#end
In Interface Builder, you'll control-drag a wire from the File's Owner onto the control to associate it with that outlet. Make sure that you release your reference to each IBOutlet in your class's dealloc method (if you're not using garbage collection) or else your app will leak memory.
Buttons send action messages when they're clicked, so you'll need to provide an action method for the button to call. You do that by declaring a method with a signature like this:
- (IBAction)myButtonClicked:(id)sender;
In Interface Builder, you'll control-drag a wire from the button onto your window controller and choose the myButtonClicked: method.
To make all this work, you'll need to create an instance of the window controller and tell it to load the NIB file at runtime. So, in your AppDelegate class, when you've determined that this is the first launch, you'll do this:
FirstLaunch *firstLaunchController = [[FirstLaunch alloc] initWithWindowNibName:#"nameOfNibFile"];
[firstLaunchController show:self];
You'll probably want to keep the reference to the window controller in an instance variable instead of a local variable like I've done here. And, depending on your application, it may make more sense to show this as a sheet. But once you've made it this far, you'll be able to figure out how to do that on your own.
Then, I'd like to be able to get mouse events for each button, so that I can do other things.
Don't worry about the mouse. There may not even be a mouse (think of the ever-popular tablet-Mac rumor).
I'd like to make a new class (FirstLaunch) for the first launch, but I'm not sure what to call from AppDelegate.
You make your own methods here. You'll probably make it a singleton*; then, you'll implement a method named something like runFirstLaunchPanel:, which will be an action method (more on those in a moment):
- (IBAction) runFirstLaunchPanel:(id)sender;
Instantiate the object in the nib, then, from your app delegate, call the action method with nil as the sender.
The reason to put the object in your nib and make the method an action method is that this makes it easy to hook up a menu item to it, so that the user can re-run the first-launch panel at a later time. (For example, if it's a Starting Points window, you might connect the New menu item to this action instead of the default one.)
*Yes, I've seen the articles about singletons, and I agree with them. In a case like this, it's OK.
Also, to get mouse events,
This is the wrong way of thinking about it. What you need to do is set your button up to send a message to your controller to make the controller (probably AppDelegate) do something. The message you want the button to send is an action message.
Implement an action method in the object that owns the nib containing the window with the buttons. Declare this method in the class's header, then connect the button to it in IB by right-clicking on your controller and dragging from the correct action method's circle to the button.
This is called the target-action paradigm, and it insulates controller responsibilities (doing things) from the views that ordered them. Because each action method does only one thing, you can have a button, a menu item, and even another controller (your app delegate, above) send the same action message, and the receiving controller won't have to care which control is sending the action, because it already knows what it has to do.
shouldn't I be a sublass of the buttons,
No. You very rarely create subclasses of anything other than NSObject (or, for model objects in Core Data, NSManagedObject) in Cocoa.
Note that I said “rarely”, not “never”. You will have to make the occasional subclass, especially if you want to create custom or customized views and cells (and, maybe, customized windows). However, subclassing is not necessary in Cocoa to the degree that (I hear) it is in some other frameworks on other platforms.
and considering that I have multiple buttons, I'm confused.
The target-action paradigm means you don't have to create one button subclass per button. One controller class implements all the actions, and the stock buttons, because you've hooked them up in IB, simply tell the controller “do this”.
Broken down, I need to access & manipulate IBOutlets I have set in IB,
Probably not. At least, not as much as you think you do.
determine when buttons are clicked (and which button was clicked).
Nope. The buttons will worry about being clicked; you just worry about setting them up to send, and then responding to, their action messages.