Multicast delegates - multiple classes receiving notifications from UI controls in iOS - objective-c

I understand that UI controls such as UITextField notify of client interactions / events via their delegate, which is defined as a class that supports the required protocol.
I have often found myself wanting to receive notifications of UI event in more than one class, so would want to support multicasting. For example, specifying more than one delegate for a UI control. I am pretty sure that there is no iOS framework feature that supports this. I was wondering if anyone had come up with a decent solution to this problem?

There is a cocoa feature that lets you build multicast delegates with relative ease - it's the Message Forwarding system built into the framework.
Make a class that overrides forwardInvocation:, and return an instance of your object instead of a delegate. This is what is sometimes called a "Trampoline object". The logic inside your forwardInvocation: implementation can now decide which "real" objects should receive the message, and forward the invocation to one or more of them.
As an update to this accepted answer, I created my own multicasting delegate implementation here:
http://www.scottlogic.co.uk/blog/colin/2012/11/a-multicast-delegate-pattern-for-ios-controls/

You could implement your UI control in your class, and then your class will receive notification from this UI, you can send message to another class (using the same delegate technic - implement delegate property in first class, and then in second class implement delegate method from 1-st class).
For example, for UITextField method -textFieldDidEndEditing:
In first class -
1) implement protocol:
#protocol TextControllerDelegate <NSObject>
#optional // Delegate protocols
- (void)textFieldDidEndEditing:(UITextField *)textField;
#end
2) #property (nonatomic, unsafe_unretained, readwrite) id <TextControllerDelegate> delegate;
3)in method - (void)textFieldDidEndEditing:(UITextField *)textField inside the class, call [delegate textFieldDidEndEditing:textField]
In second class:
1) implement object of first class, set delegate to self (to second class).
2) implement method - (void)textFieldDidEndEditing:(UITextField *)textField

One technique to support multicasting is to give your delegating class the following methods:
#interface Delegator : NSObject
- (void)addDelegate:(id<MyProtocol>)delegate;
- (void)removeDelegate:(id<MyProtocol>)delegate;
#end
And store the reference to the delegates in an NSHashTable.
See the implementation here:
http://arielelkin.github.io/articles/objective-c-multicast-delegate/

Related

why must i inherit NSobject instead of NSapplication to implement delegate method on GNUSTEP?

I've seen several Obj-C tutorials. The delegate classes all inherit from NSObject. For example, the applicationDidFinishLaunching delegate method, in some tutorials, it inherited from NSObject but NSApplication to implement it. The reason I don't think it should inherited from NSObject is that I didn't find any delegate protocol declaration in it, but I found that delegate protocol declaration in NSApplication. My Objective-C toy environment is GnuSep.
Here is some code:
#interface browserController : NSObject //here. inheriting from NSObject,but NSObject don'have any protocols declaration about applicationDidFinishLaunching.
{
NSBrowser *browser;
}
#end
#implementation browserController
- (void)menuAction:menuItem
{
..............................
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSWindow *win;
ActiveBrowserDelegate * abd;
WindowDelegate *wd;
NSRect wf = {{100, 100}, {600, 500}};
NSRect bf = {{10, 10}, {580, 350}};
.............................
}
It is called informal protocol (though GNUstep declared it anyway as GSAppDelegateProtocol for documentation purpose) NSApplication will simply check it at runtime if your delegate object will respond to the message, (using -respondsToSelector:) A delegate can be a view, a string, a proxy, anything as long as you make it responds to the selector. You don't need to make your delegate implement every method in such protocol since all verifications would be done at runtime. To make it looks cleaner you could just redeclare -applicationDidFinishLaunching: in #interface though you don't really need to, just make one in the #implementaiton is enough.
A delegate may inherit from anything appropriate. It is usually supposed to implement a certain protocol.
A protocol is a way of implementing a formal communication interface between two classes.
However, it is most unlikly that a delegate will inherit from its communication partner class.
With other words: Protocols are often used to overcome the unavailability of multiple inheritance. (Pretty much like interfaces in Java)
Example: A UIViewController subclass' instance controls a view that contains a UITableView. Rather than subclassing the UITableView for the implementation of its look or data, there are two delegates assigned to the table view object. One delegate serves as provider for custom layout (provides items such as the header view) and another (?) delegate provides the data that is being displayed.
Now, this delegate could be any object, inheriting from NSObject and implementing the two protocols. This object cold then be instanciated by the view controller and assigned to the table.
However, it is common practice that the view controller itself serves as delgate for the table(s) that it controls. That is a good pattern but strictly spoken not required. It could be any object.
Now the custom view contoller inherits from UITableViewController (which already implements the protocols and inherits from ViewController) and serves as delgate for the table view. The table view itself could be any subclass of UITableView. (Although this is a bad example here because subclassing UITableView is normally not advisable)
If the delegate does not need to inherit from any class and just implements the protocol, then it shold at least inherit from the cocoa base class NSObject. That ensures that it inherits all the usual capabilites and behaviour of any object. (init method, copy method, description method etc.) That may be required to work properly with other classes of the framework such as beeing used as an object within an NSArray, NSLog etc.

iOS: what is the equivalent of an event listener in Objective-C?

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?

Is it possible to dynamically implement a protocol in Objective-C?

I know that I can extend a class (for example a framework class) using categories, but is it possible to have a class for which you do not control the source code implement one of your custom protocols? I not only want to have it respond to certain messages if sent to an instance, but also, ideally, want objects of that class to return true on runtime type checks when querying for the protocol.
You can define a category that conforms to the protocol, so you'd do something like:
#interface UIWebView (MyGreatExtensions) <UITableViewDelegate>
#end
#implementation UIWebView (MyGreatExtensions)
- (CGFloat)tableView: (UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath *)indexPath {
return 42.0;
}
// ...
#end
A small edge case to consider is that if someone else tries to do the same thing (e.g. a third-party framework also adds the protocol via a category) then you can't guarantee that your version will be used (and of course neither can they).
More on this approach from Mark Dalrymple.
I think this is possible. You can easily create dynamic proxies in Objective-C (this is how NSUndoManager does its magic) by overwriting NSObject's forwardInvocation: method, intercepting all non-recognized messages that an implementor of the protocol would respond to. I have never done this myself, so there might be a more elegant way to do this.
In addition, to fool the runtime checks into agreeing that your object does implement the protocol, you could override NSObject's class method conformsToProtocol: like so:
+ (BOOL)conformsToProtocol:(Protocol *)aProtocol {
if (aProtocol == #protocol(MyDynamicallyImplementedProtocol))
return YES;
return [super conformsToProtocol:aProtocol];
}
There might be more methods that you need to override, one example would be NSObject's instancesRespondToSelector: and the resolve*: methods. Examine the NSObject class reference to find out more.

Creating a new event in Interface Builder

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.

Delegate method seems to be #required where it's declared as #optional

I've created my own Delegate for a ObjC class. The class itself deals with Core Data operations. Delegate methods are used to inform other classes about changes that happened to the datastore. The class that deals with the datastore is called Datastore and it's delegate is called DatastoreDelegate. A UIViewController of mine (ContactsViewController) implements the delegate.
My DatastoreDelegate is declared as followed:
#class Datastore;
#protocol DatastoreDelegate <NSObject>;
#optional
- (void)didAddMessage:(Message *)message;
- (void)didUpdateContact:(Contact *)contact;
- (void)didAddContact:(Contact *)contact;
- (void)didUpdateContact:(Contact *)contact;
- (void)didDeleteContacts;
#end
The weird thing is, my code all worked fine with these methods except for the [didAddMessage:] method. Whenever I try to call this delegate from within the Datastore class I get an error from the ContactsViewController. The error tells me the [didAddMessage:] selector is missing in the ContactsViewController instance (Unrecognized selector sent to instance). How can the selector be missing if it's optional?
I should note that my Datastore class is a Singleton. I'm not sure if this is somehow related to this issue I'm having.
"Optional" means the caller is responsible for checking that a target responds to a given selector. E.g.:
if ([_myDelegate respondsToSelector:#selector(didAddMessage:)])
{
[_myDelegate didAddMessage:theMessage];
}
Did you implement didAddMessage: in your ContactsViewController? It's optional so you aren't forced to implement it, but if you send the didAddMessage: message to ContactsViewController but haven't actually implemented it in ContactsViewController, you'll still get a compiler warning. In other words, #optional just means you don't have to implement it, but the compiler may still give a warning if you haven't implemented it but try to use it.
What you might want to do in Datastore is this:
if ([delegate respondsToSelector:#selector(didAddMessage:)]) {
[delegate didAddMessage:theMessage];
}
rather than just:
[delegate didAddMessage:theMessage];
(You'll still get a compiler warning in the first example, but it's safe to ignore since you're checking at runtime to see if the appropriate method is implemented in the delegate.)