I am developing a new component to be used in Interface Builder, which subclasses UIPickerView to create an interface that is similar to UIDatePicker but also includes seconds. I would like for the developer who uses my component to be able to drag a new Object from the library, configure it to be a TimePickerView class, and use it from there.
That bit works perfectly, but now I would like to implement some of the events that are available in a standard UIDatePicker, specifically the "Value Changed" event. I am not sure how to declare an event in this manner without using a delegate protocol. I am also not sure how to make XCode aware of events that are available on an object. Therefore, I am really asking three questions:
How can I declare an event, such as the Value Changed event on a UIDatePicker?
How can I fire an event from within my TimePickerView?
How can I make XCode aware of this event so that a developer can easily link an event to a controller?
As this doesn't seem to be easy to do, I suspect there could be a reason for this; therefore I would also appreciate comments on my approach and arguments for why I should create a delegate protocol to make this happen.
In Objective C and cocoa framework there's nothing like declaring an event(as you would do for example in C#), the same behavior of an event is achieved using the delegate pattern(this is how all cocoa class handle events), so to implement a custom events with the delegate pattern you can do in this way, for example :
Protocol and Custom class interface declarations :
#protocol myDelegate <NSObject>
-(void)valueChanged:(double) value;
#end
#interface UAKTestDelegate : UIPickerView
#property(weak) id<myDelegate> delegate;
#end
Somewhere in the implementation (fire the event) :
//Example of firing the event ....
if([[self delegate] respondsToSelector:#selector(valueChanged:)]) {
[[self delegate] valueChanged:1.0];
}
So, this first part of the answer should address the first two questions in your post(create and fire custom events), but the response to the third answer is simply you can't ...
Xcode(in this case interface builder) knows about the events that UIControl(or a subclass of it) implements, but there's no way to tell it about your own custom events.
So i think that in your case (if you can) you could subclass directly UIDatePicker and eventually override valueChanged if you need to.
Related
I'm writing own switch class. I'd like to add a delegate to it - examplary if we have UIImagePickerController we add UIImagePickerControllerDelegate to #interface of some viewcontroller and we can set methods like imagePickerControllerDidCancel:(UIImagePickerController *)picker...
I want to do something similar for my class - it's named HSwitch, so I want to add HSwitchDelegate to #interface of some view controller.
I would like to add to this delegate a method valueWasChanged, that I could set in viewController and which would be called each time when slider changes value.
How can I do that? I didn't do it yet, so... please help me :)
Thanks!
If your class is a switch, presumably it inherits from UIControl. If this is the case, don't introduce the complexity of delegates - use target-action instead, and send actions / register targets as you would with any other control. See the UIControl class reference for details. UIControlEventValueChanged would be a suitable event for your needs.
What is the proper way to accept user input in a view and then transfer it to that view's controller? I know the NotificationCenter is one option, but surely there is a more elegant way to transfer data from a view to its controller?
All help is greatly appreciated and I always accept an answer!
Use the delegate protocol design pattern, or target-action by subclassing UIControl. Think about how a UIButton tells a view controller that it's been pressed. In interface builder, you connect an action - a selector something like touchUpInside: to a target - the view controller that owns it. In non-IB, you directly tell the UIButton what selector and what target to use.
Both methods make sense in different cases. For a UITextField, for example, it makes more sense to use delegation because it's possible for the text field to send you any number of events, such as an event when the user begins editing, ends editing, or types a character.
For a button, it makes more sense to use target-action because there's really only one event expressed in different forms.
For swipes and drags and other gestures, use UIGestureRecognizers.
You're looking for Delegation or a Data Source. You can see more information about this here, Delegation and Data Sources
A brief example of this would be, something along the lines of this:
//MyViewSubclass.h
#protocol MyViewSubclassDelegate
//Implement your delegate methods here.
-(void)didTouchView;
#end
#interface MyViewSubclass {
id<MyViewSubclassDelegate>delegate;
}
#property(nonatomic,assign)id<MyViewSubclassDelegate>delegate;
Of course, #synthesize your delegate in MyViewSubclass.m
Now in the class's header, that you want the delegate of MyViewSubclass to be, you need to conform to the `MyViewSubclassDelegate Protocol.
#import "MyViewSubclass.h"
#interface MyViewController : UIViewController <MyViewSubclassDelegate>
In your #implementation of MyViewController., implement the MyViewSubclassDelegate method of -(void)didTouchView.
When you initialize and create your MyViewSubclass object, you set MyViewController as the delegate:
myViewSubclass.delegate = self // Self being MyViewController.
In your MyViewSubclass, when you're ready to forward any information, or simply want to fire a method you would do [self.delegate didTouchView]
Hope this helps !
You are looking for delegation, where the controller set itselfs as the delegate of the view. You know it from UITableViewDelegate.
Make your view a subclass of UIControl and implement the target/action design pattern - use the sendActionsForControlEvents: method to message the controller.
Often the UIKit objects like UITextField have delegate methods that you can implement to perform your business logic. E.g UITextField has a delegate method called - textFieldDidEndEditing: that gets called after the user has dismissed the keyboard.
In some of my learning I saw someone mention that in your view controller you can have a model and have some sort of listener on the model for changes in it. I don't think I'm using the right names for these, which is probably why my searches haven't turned up anything. Essentially I want to move my server calls from the controllers into my models, but I need some sort of listener on them to know when the call is complete to update my views.
look into delegates
delegates tutorial
or blocks a bit more advanced
basic blocks tutorial
just start with delegates,
you can also use NSNotification NSNotification tutorial but is not recommended as it broadcast to every class, and you might only need to send messages to a specific class not every one
Belong to C# world, i have to go to objective c (for my job).
I think the event equivalent in objective c is this implementation :
Create protocol with all your event's methods :
#protocol MyDelegate <NSObject>
- (void)myEvent;
#end
In your class which should send the event, add :
#interface MyClassWichSendEvent : NSObject
#property (nonatomic, retain) IBOutlet id<MyDelegate> delegate;
#end
Raising the event where you want, for example :
- (IBAction)testEvent:(NSButton*)sender
{
[self.delegate myEvent];
}
Now in your listener class, you should listen the events like this :
Add the protocol to your class that listening
#interface Document : NSDocument<MyDelegate>
In the implementation, on init or in interface builder, you must link delegate of your object instance to listen with self of your class which listen.
In code
-(void)awakeFromNib
{
myObjToListen.delegate = self;
}
In Interface Builder -> IBOutlet from delegate to your listen's class.
And finally, implement your method in your listener class :
- (void)myEvent
{
NSLog(#"i have listen this event !");
}
Sorry for my english, i hope that help people who went from java or C#.
You're looking for KVO - key/value observing:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html
http://nachbaur.com/2011/07/29/back-to-basics-using-kvo/
Delegates + Notifications are good for communicating between objects but they don't automatically send msgs when a value changes (which from your question, that is what you are asking about)
I think you may be looking for NSNotificationCenter which is a way to pass messages to whoever may be listening. So you can send out a notification from an event in your model and just listen for it in your controllers. A cleaner method might be to implement your own protocol with delegates.
Objective C uses delegates
This post has a nice example: How do I create delegates in Objective-C?
I have created a subclass of UIView and I would like to have it publish custom events that show up in Interface Builder (actually Xcode4) the same way that controls like UIButton have a bunch of events in the "Sent Events" area when you right click on a control in the Xcode 4 designer. I know I can use Delegation (via Protocols) or Notification (via the UINotificationCenter) to let the objects using my custom view know when certain things happen, but I would like to know if the "The Target-Action Mechanism" (described in the Cocoa Fundamentals Guide) is appropriate/desirable/possible to use and be integrated with the Xcode designer. Coming from a mostly .NET background, this approach seems to be closely related to the .NET event model and makes the most sense to me.
There's UIControlEventApplicationReserved, which gives you a range of event identifiers that your app can use. However, I don't think there's any way to tell Interface Builder about application-defined events, so you won't get the same support for your events in IB as you find for UIControl's standard events. Instead, you'll have to specify the target and action for each app-defined event in code. (Please, someone correct me if I'm mistaken on this point.) That's not at all difficult, but it is a little different.
A simple way to do this is to extend UIControl instead of UIView this will allow you to add a target to all the default events (same as UIButton etc).
Note: in order for my custom UIControl to handle the events as opposed to the controls I layered on top of it I had to ensure that userInteractionEnabled = NO was set on all the layered controls.
You can do this using an IBOutletCollection. The advantage of this approach is that you can link objects in Interface Builder. The downside is that you can't link directly to IBActions (like UIControl). Here is a clean implementation using a protocol:
ObserverProtocol.h
#protocol ObserverProtocol <NSObject>
- (void)observedObjectChanged:(id)sender;
#end
MyObject.h
#interface MyObject : NSObject
{
#private
IBOutletCollection(id<ObserverProtocol>) NSArray *observers;
}
- (void)objectChanged;
MyObject.m
#implementation MyObject
- (void)objectChanged
{
for (id<ObserverProtocol> observer in observers)
{
if ([observer respondsToSelector:#selector(observedObjectChanged:)])
{
[observer observedObjectChanged:self];
}
}
}
#end
Then you just need to implement ObserverProtocol in classes you want to observe the event (and to do the binding in Interface Builder of course).
What's the best way for registering events for my UIView subclass, so that I can connect them to IBAction-s in interface builder?
Currently I've just got a standard UIView dropped onto my main view and I've set the class to "RadioDial" (my custom class). This displays the view fine, but I have no idea how to get events out of it.
Thanks
Please clarify: do you mean that you would like Interface Builder to offer your view controllers to wire up custom events that your view subclass will be emitting (much like the Button controls allow you to wire up Touch Inside, etc)?
If you need this type of functionality, you will need to use a generalized 'delegate' property on your View combined with a protocol.
#protocol RadioDialDelegate
-(void)dialValueChanged:(id)sender
#end
#interface RadioDial
{
id<RadioDialDelegate> radioDelegate;
}
#property (nonatomic, assign) IBOutlet id<RadioDialDelegate> radioDelegate;
This will allow the controller to wire up to the view (assuming it implements RadioDialDelegate) and receive any events that come out of the view. Alternatively, you can use an untyped delegate and in your View code, use a late bound call:
if([radioDelegate respondsToSelector:#selector(dialValueChanged:)]) {
[radioDelegate dialValueChanged:self];
}
Create a method in your view controller (if nothing else, you should have a RootViewController in you project). Let's say your method is
-(void) buttonClicked { code code code }
In the controller's header file (for example RootViewController.h) you then put:
-(IBAction) buttonClicked;
And in IB you right-click your button/radio dial/whatever. You will see a list of events and you can drag FROM the connector of the event you want your controller to receive, to the object in IB that represents the controler (probably First Responder). This depends on how your IB structure is set up, but it should be straightforward.
Another alternative is to learn how to create UIViews programatically, and forget about IB for the time being. Opinions are divided about whether it's better to learn to use IB at the outset, or whether it's better to learn how to do everything in code and save IB for later. In any case, it's necessary to learn both ways of setting up an interface at some point.