Im new to Objective-C & iOS programming so Im very confused about event and action handling. I don't really understand what is the difference between connecting a button in Interface builder with its files owner method (for instance connecting button action pressed with my personal method buttonClicked) and on the other hand creating a delegate which will respond to button events? I am confused because I come from C# (.NET) and there you only handling events via delegation.
Let's create event handling programmatically first. Some assumptions ...
you have class MyViewController
this class does contain property myButton (UIButton *)
you do want to handle events in this class
First step is to create method to handle your button taps:
-(void)onMyButtonTap:(UIButton *)sender {
// My button was tapped, whoa, which one is in sender argument
}
Add tap event handling:
-(void)loadView {
[super loadView];
[self.myButton addTarget:self action:#selector(onMyButtonTap:) forControlEvents: UIControlEventTouchUpInside];
}
What this code does? self in this case is instance of MyViewController class, which is going to handle touch up inside event (addTarget). action is method which will be called when UIControlEventTouchUpInside fires.
So the addTarget:... line adds event handling and when the control event fires, this ...
[self onMyButtonTap:self.myButton];
... will be called automatically.
It's simplified example, it can be more complicated in the real world:
you can have UIButton in separate UIView (good practice, I dislike when people put them in UIViewController)
you can handle multiple buttons taps in one method (distinguish between them by sender argument)
you can create more tap handling methods, one method for one button,
...
And now back to outlets. Outlet is just an automatic glue for addTarget:....
UIButton is subclass of UIControl and here you can see how other events can be handled and what events you can handle.
Delegation is also common practice, but not for UIControl. There's no delegate, etc. You have to do it in this way.
Or you can enhance UIButton to handle events via blocks for example. Here's one way https://gist.github.com/2468899 or you can Google for more.
Related
Right now I am using the delegate, but I suspect it's bad form.
I programatically create an identical UIBarButtonItem in numerous, different view controllers.
My button's target is myAppDelegate, and its action is defined therein.
What is a better way of doing this? Or am I just supposed to copy and paste the identical action into no-matter-how-many view controllers that instantiate the identical bar button?
Okay, now suppose the action is identical in all respects but one: It varies only in that it should send a presentViewController message to the view controller that instantiated the button that sent the action. Thus, in the action, I can send a presentViewController message to sender, which is an instance of the button, but I know no means of reaching the view controller that instantiated that instance of the button.
Each view controller simply needs to set itself as the target of the button when the button is created. The button's action selector will then be sent to the correct controller.
As for the selector itself, create a category on UIViewController to contain the button's action:
#implemenation UIViewController (MKCBarButtonItemAction)
- (void)MKCDoThatThingIDo
{
// Do things.
[self presentViewController:[self theOtherViewControllerWhatINeedToDisplay]
animated:YES
completion:^{
// Do more things.
}];
// Do yet other things.
}
#end
If you've common methods for multiple view controllers defined in AppDelegate file, then you can make call to those actions using delegate object something like,
#define appDelegateObj (AppDelegate *)[UIApplication sharedApplication].delegate;
Where, AppDelegate is your delegate file class name, also define this macro at global so you can access it from anywhere,
Now where ever you want to call any function, just use like [appDelegateObj functionName];
Here's one thing, if you can make class methods into delegate file then, it can used with using class name, like [AppDelegate functionName];
class methods defined like +(void)functionName{...} and it can't access to class instance variables and methods.
I have a UIButton that I create in my sub class ViewController, and add it to my MainViewController.
Now, I added a target method to this button that should push another view controller to my Navigation controller (the one that in the MainViewController).
I know that the method did call when I push the button, but the view wasn't push to the Navigation Controller.
I scanned this drawing - this is the drawing (I also added part of my code):
This is the code I'm using in my button:
(remember it's in a deferent ViewController).
- (void)buttonPressed:(UIButton *)sender
{
Photo_ScreenGlobalView *photo = [[Photo_ScreenGlobalView alloc] init];
[self.navigationController pushViewController:photo animated:YES];
}
Usually I solve these situations with delegation. If there is a view controller which is subordinate to another (i.e. a "sub" view controller) but should have the ability to trigger navigation changes, etc... then it should have a weak pointer back to it's 'parent'. Then the parent VC should implement an appropriately named protocol with a callback for the child to use. The names of these things can be generic, such as #property navigationDelegate and requestNavigationToViewController: or they can be more semantic, such as #property userFormDelegate and userFormDoneButtonPressed:
Generally speaking, a subordinate view controller should not be able to directly modify navigation at it's parent's level; but it can trigger it via more loosely-coupoled interfaces like these.
i came back to let you all know how i actually did it.
after googling a lot found this nice and quick guide how to make DELEGATE
and working with delegate solved all my problems. if you need any help don't hesitate to send me PM.
this is the guide:
http://css.dzone.com/articles/do-not-publishcreating-your
I'm new to iPhone development. I've been reading several questions on how to make a google maps annotation callout window accept line breaks. Every tutorial I've read requires me to fire the mapView:didSelectAnnotationView method. But I have no idea how to trigger this. things I've tried include
putting the method in my MapViewController.m file which extends UIViewController
putting the method in a MapView.m file which extends MKMapView, then have my Mapview element in my storyboard reference it as the class to use
There's so much about xcode, objective c, and iphone development that I don't understand, so i can't tell where my problem lies.
At the moment, my map does plot my desired marker on the desired location. I just need to understand how to fire the mapView:didSelectAnnotationView and mapView:viewForAnnotation functions before I can start customizing the call out box.
Does anyone have step by step instructions on how to trigger these functions?
A bit of background
A few things to note:
You don't call mapView:didSelectAnnotationView. The MKMapView calls that function on it's delegate. In other words, when you set up an MKMapView, you tell it: "hey, listen, anytimme you need to tell me what's happening on the map, go tell this guy, he'll handle them for you". That "guy" is the delegate object, and it needs to implement mapView:didSelectAnnotationView (that's also why its name "did select", ie, it already happened, as opposed to "select"). For a simple case, the delegate is often the UIViewController that owns the MKMapView, which is what I'll describe below.
That method will then get triggered when the user taps on one of your annotations. So that's a great spot to start customizing what should happen when they tap on an annotation view (updating a selection, for instance).
It's not, however, what you want if you want to customize what annotation to show, which is what it sounds like you're actually after. For that, there's a different method just a few paragraphs earlier on the same man page: mapView:viewForAnnotation. So substitute this method if you find that mapView:didSelectAnnotationView isn't what you were looking for.
What you can do
If you got as far as a map with a marker, I'm guessing you have at least:
* a view controller (extendeding from UIViewController, and
* an MKMapView that you've added to the view for that view controller, say named mapView
The method you want to fire is defined as part of the MKMapViewDelegate protocol.
The easiest way to get this wired is to:
make your UIViewController the delegate for you MKMapView
in code, say in your viewDidLoad, of your MapViewController.m you could do mapview.delegate = self, OR
in Interface Builder, you could drag the connection from the the MKMapView delegate property to the file's owner
then, define a method on your UIViewController called mapView:didSelectAnnotationView, declaring it just like the protocol does, in your MapViewController.m file:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view {
// whatever you need to do to your annotation and/or map
}
Good luck!
mapView:didSelectAnnotationView is a delegate method of the map view, you can read about it here:
MKMapViewDelegate Protocol Reference
You don't need to call it, the map view will call it "by it self" and send it to every view/view controller that registered as it's delegate.
What do you need to do
Basically you need to add the MKMapViewDelegate on your .h file, what will look something like this:
#interface someViewController : UIViewController <MKMapViewDelegate>
Then in the .m file, after you instantiate the map view you should add:
mapView.delegate = self;//were self refers to your controller
From this point and on your controller will be able to "receive messages" from the map view which are the methods that you can see on the MKMapViewDelegate reference I linked to.
So to implement the mapView:didSelectAnnotationView you need to add in your .m file
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
//if you did all the steps this methosd will be called when a user taps the annotation on the map.
}
What is happening
What happens in the background is:
The map view has a method (Apple codded) that handles the AnnotationView touch events.
When a touch event take place it sends a "message" to all of it's delegates saying "Hey a user did Select Annotation View on this map, Do with it what ever you need".
usually it looks like that:
[self.delegate mapView:someMapView didSelectAnnotationView:someAnnotationView];
Then every view/controller that assigned itself as a delegate and implemented the method will cal this method.
Good luck
Place *place = [[Place alloc] init];
PlaceMark *placeMark = [[PlaceMark alloc] initWithPlace:place];
[self.mapView selectAnnotation:placeMark animated:YES];
I'm a little confused about raising an event in Objective-C,
I came from C# (.NET) environment and I would like to learn Objective-c and Cocoa programming.
So here's my question:
I have a little application with a NSTextField that I want to use to listen to an event.
What I want to do : When I double click within this control, it raise an event and pop up ex: an NSAlert that displays "Double clicked".
So how can I do that, I'am a visual person so I need some code exemple to show how it's work; Like what should I put within the .h class and within the .m class.
Thanks in advance,
Alex.
You need to read up on the Cocoa Fundamentals and the target/action mechanism. An NSControl (like its NSButton subclass) has a target to which it sends an action with itself as the sender. Not all controls support -doubleAction, but some do.
NSButton/NSButtonCell does not support a double action, so you would need to do some subclassing and override the mouse methods. NSEvent (which is passed into mouse methods) can be queried for its click count to distinguish double-clicks from singles.
Just for the record, it's usually click-and-hold that produces a context menu on OS X and this capability is announced through a down-facing arrow somewhere on the right of the button face. Few people will actually know a menu is there for double-clicking and it's hard to represent this with a symbol on the button face. Consider a click-and-hold trigger for your button's context menu.
I am not sure why you want to do this in a text field, but here is how to do it:
You need to use a subclass that overrides click behavior for double clicks. The header file would look like this:
#import <Cocoa/Cocoa.h>
#interface ClassName : NSTextField {
//Any new instance variables here
}
//Any new methods here
#end
and the implementation file would look like this:
#import "ClassName.h"
#implementation ClassName
- (void)mouseUp:(NSEvent *)event {
//You can also do this with mouseDown:, depending on when you prefer to handle the event
if([event clickCount] == 2) {
//Handle double click here
} else [super mouseUp:event]; //or pass to parent implementation
}
UPDATED: I'm now overriding the NSView keyUp method from a NSView subclass set to first responder like below, but am still not seeing evidence that it is being called.
#implementation svsView
- (BOOL)acceptsFirstResponder {
return YES;
}
- (void)keyUp:(NSEvent *)event {
//--do key up stuff--
NSLog(#"key up'd!");
}
#end
--ORIGINAL POST--
I'm new to Cocoa and Obj-C and am trying to do a (void)keyUp: from within the implementation of my controller class (which itself is of type NSController). I'm not sure if this is the right place to put it, though. I have a series of like buttons each set to a unique key equivalent (IB button attribute) and each calls my (IBAction)keyInput method which then passes the identity of each key onto another object. This runs just fine, but I also want to track when each key is released.
--ORIGINAL [bad] EXAMPLE--
#implementation svsController
//init
//IBActions
- (IBAction)keyInput:(id)sender {
//--do key down stuff--
}
- (void)keyUp:(NSEvent *)event {
//--do key up stuff--
}
#end
Upon fail, I also tried the keyUp as an IBAction (instead of void), like the user-defined keyInput is, and hooked it up to the appropriate buttons in Interface Builder, but then keyUp was only called when the keys were down and not when released. (Which I kind of figured would happen.)
Pardon my noobery, but should I be putting this method in another class or doing something differently? Wherever it is, though, I need it be able to access objects owned by the controller class.
Thanks for any insight you may have.
NSResponder subclasses (such as a custom subclass of NSView) handle key events. They even handle more abstract-level events such as "move left/right", "insert newline", etc. They can then send a message to a controller to respond accordingly.
Wow! I think I've nailed it. I just needed to instantiate my subclass with a NSView/NSWindow reference to the class in IB. Wasn't actually creating an instance of it in the UI. The past several hours down the crapper! Sans that I learned a thing or two along the way. ;)