If I name a widget in Interface Builder, and then I write a method that receives click events on that button:
- (IBAction)btnTouchDown:(id)sender
{
// how can you identify the button here,
// if several different buttons map
// their "Touch" event to this same function?
// I know you can look at its text but that seems really clumsy
// can I somehow get its INTERFACE BUILDER NAME?
// I named each uniquely in interface builder,
// under "Identity"/"Name"
// Or is my only recourse to tie EACH BUTTON to its own handler function?
}
Click on the button in interface builder, press CMD+2 (Or in info dialog, second tab) and drag the circle from Touch up inside to File's Owner from the yourView.xib window and then it'll let you select one of the methods which has the - (IBAction) method:(id)sender signature. Choose the one you want to associate with that button :)
You say you don't want to make a method for each button, but actually that is the best way in my opinion, rather than having one huge monolithic method which handles each of the buttons and checks each time to see which one of them it is. Using the process I mentioned above, you can specifically tie a handler method to a particular button, keeping things nice, tidy, and logical.
If you really want to do that though, you can also test for the tag number.
you have to define the button as an instance variable in your view controller and then use the IBOutlet pragma to make it known to IB. So for example you would do this in your view controller:
UIButton IBOutlet *mybutton;
After defining it like this it will show up in your IB connections where you can associate it with the appropriate button
Related
Short answer is inside the title :)
Explaining: in my MainMenu.xib I have only the Main Menu of the application, that must be same for all NSWindows I open. There is one particular NSWindowController that has, let me say, all answers about when menu item must be enabled (via cases on selector in validateUserInterfaceItem) and what to do with all actions. When NSWindow associated with that NSWindowController is currently focused, there is no problem, but as I focus on another NSWindow all menus are grayed.
I have this flow now: MainMenu is created by reference to it as Main nib into info.plist, then AppDelegate do some init stuff and create MainWinController with MainWindow, at some point MainWinController creates 1+ DetailsWinController with DetailsWindow. AppDelegate manage my custom menu by calling each time functions from MainWinController.
So, how can I force the responder chain to query always that particular NSWindowController (MainWinController)?
You don't give many details to go on, and it isn't clear what you are trying to achieve.
My first reaction is that if you want share Menu items why are you creating multiple windows rather than Views within your MainWindow?
Assuming both your MainWindow and DetailsWindow implement the same selectors you could direct Menu Actions to the First Responder
To add the DetailsWinController in the InterfaceBuilder drag an NSObject from the Object Library, then in Identity Inspector change its class to your DetailsWinController class.
If your Main Menu has different items for the DetailsWindow just connect these to the actions in that instance.
NOTE if you so this you should NOT create the DetailsWinController in code.
If you really want to do this in code you will need to add actions and targets to your menu in code.
I understand the concept of MVC. At least I hope I do but there is a particular situation that I have encountered recently that makes me think that I actually have no idea.
Problem:
I am trying to create a class that when applied to an existing button adds the functionality of editing that button's title. It does so by dynamically generating a UITextField that will in turn be populated with the Button's new name. I hold the logic of adding the UITextField in a class called CustomAnimation and initializing it in the following way:
CustomAnimation *yar = [[CustomAnimation alloc] initWithButton:customButton];
The problem that I encounter is when I am trying to dynamically create a UIButton from a custom class like CustomAnimation and try to assign a target/action to it. Since the UIButton is actually generated inside the ViewController FROM the CustomAnimation class the former should get a pointer to the parent ViewController and then set it as the target.
[classButton addTarget:viewController action:#selector(dostuff:) forControlEvents:UIControlEventTouchUpInside];
All is good except one thing - dostuff method should reside inside the ViewController in order to be visible for that button and here everything turns into a hack.
What is the correct approach when you want to generate temporary UI elements from inside a Class instead of the Controller?
Thank you
There are lots of ways you can look at MVC in terms of granularity. If I understand your question correctly you are trying to implement MVC at a single control level which is - in my opinion - too finely grained. I try to aim for a more coarse approch where a controller manages a chunk of UI controls, e.g. a view or a window.
You could argue that the editability of the buttons label is bahavior that belongs to the button (encapsulated by it), hence its implementation is or should be part of the overall implementation of the button. Once you accept that, you could implement this as a subclass of UIButton and move the code that is in CustomAnimation to your subclass of UIButton. Or encapsulate CustomAnimation in your subclass of UIButton, whichever is more appriopriate.
I know the differences between IBAction and IBOutlet, but I don't know when I should use one over the other.
For example, I'm trying to make a calculator. I have ten buttons and one label to display a result. Must I create ten IBOutlets in my interface? If not, why not?
An IBOutlet is a connection to an object, and an IBAction is a connection to a method to be invoked as a callback. For example, you might have ten buttons and one label, but you might connect to the label using an IBOutlet (so you can update its value), and a single function - (IBAction)buttonPressed:(id)sender; that is connected to the onTouchUpInside event of each of the ten buttons. The buttonPressed: method's implementation will then need to inspect the sender to figure out what its value is.
You should not need to have an IBOutlet for each button if this is all you need to do.
Ok, IBOutlets and IBActions serve two separate purposes.
IBActions are basically methods that can be connected to UI elements through IB. They provide ways for your object to be notified when something has happened with the UI. They also provide the sender argument when they are called so that you can access the UI Element that sent the message from within the method.
An IBOutlet on the other hand offers a way to get a reference to the UI element within your code at any point, it is used when you need to change aspects of the UI.
For your situation you don't really need to have IBOutlets for the buttons because you don't need to change anything about them, you just need to be notified when they have been pressed.
As a note, if you have that many buttons, and you for some reason needed a way to access them from within your code to change something about them i would not recommend using 10 IBOutlets. Instead, i would use the viewWithTag: method, and set each buttons tag accordingly so that you don't have to go to the trouble of creating IBOutlets for each one.
In your case, I would create one IBOutlet for the label, and one IBAction for the buttons. The IBOutlet for the label would be to update the text when the user pressed a button. Your IBAction would look something like this:
-(IBAction)digitPressed:(UIButton *)sender{
//First you have to check if there is currently any text in the label
//If there is not, the below line of code is performed
NSString *textWithDigit = [[yourLabel.text] stringByAppendingString: sender.titleLabel];
//This line updates the label's text
label.text = textWithDigit;
}
I only put in the code relevant to IBActions and IBOutlets (you need an IBOutlet for the label in order to update the text, and you need the IBAction to change the label's text to the digit pressed); there is much more code needed (code needed to check if the user is currently in the middle of typing a number, code for operations like +, -, *, /), but this was just a start.
Hope this helps!
I have the following code:
AXWindowController *controller = [[AXWindowController alloc]
initWithWindowNibName:#"ActivateWindow"];
[controller showWindow:nil];
How do I use controller to make changes to the window ActivateWindow (eg. changing text on the window, etc.)? Step by step instructions would be appreciated. Thanks!
Well, none of the actual window elements are handled in your piece of code, your code just initializes and shows the window. (I've just had a quick scan through, so I'm assuming that it works)
If you want to, for example, display text in a window, the simplest way is to use an NSTextField.
Very simple instructions:
Drag and drop a Label item from the Interface Builder library into your window.
Drag and drop a button item from the Interface Builder library into your window.
In XCode, in your window controller, create an IBOutlet for your label, e.g. messageLabel
In XCode, in your window controller, create an IBAction for your label, e.g. changeLabel
In Interface Builder, drag and drop an "object" into your document. (Shortcut = CMD + 0)
Under the Identity tab, change the class to AXWindowController. (Shortcut = CMD + 6)
Ctrl + drag from the object to the label, choose the outlet messageLabel.
Ctrl + drag from the button to the object, choose changeLabel.
Now, to display text when you press the button, you would add code to the changeLabel IBAction to do so.
e.g. [messageLabel setTitleWithMnemonic:#"blah"];
And Finally, if you want it to automatically display text, you might as well just change the label content in Interface Builder / place the code in the windowDidLoad method in your controller.
That to me is pretty much the simplest way for you to do it. However, I recommend you read some books / tutorials on Cocoa and Objective-C before delving into harder stuff.
You'd programmatically make changes to your window in the code for your window controller, of course. Open AXWindowController.m and do whatever it is you're wanting to do. It's hard to give a more definite answer without knowing exactly what you're trying to do (if you just want to create the interface, use Interface Builder) or what your experience level is.
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.