I been developing a custom UIAlertView class to have an completion block. Is it ok to have a delegate reference to itself?? For example:
PYAreaAlertView.h
#interface PYAreatAlertView : UIAlertView
#property (nonatomic, copy) CompletionBlock completion;
- (id)initWithCompletion:(CompletionBlock)completion;
#end
PYAreaAlertView.m
#interface PYAreaAlertView () <UIAlertViewDelgate>
#end
#implementation PYAreaAlertView
- (id)initWithCompletion:(CompletionBlock)completion
{
self = [super init];
if (self)
{
_completion = completion
self.title = #"Add Area"
self.message = #"";
self.delegate = self // Is this ok??
}
return self
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *buttonTitle = [alertView buttonTitleAtIndex:buttonIndex];
if ([buttonTitle isEqualToString:#"Done"] && self.completion)
self.completion();
}
Beware, that you are also exposing the delegate property to the user of your class, since PYAreatAlertView is a UIAlertView. So your logic of executing a completion block may not work in case user provides different delegate.
Also, just a word of caution: It is recommended that UIAlertView should not be subclassed.
From UIAlertView docs,
The UIAlertView class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.
In such scenarios, you are better off creating a custom UIView and replicating the alert view appearance. Here you can implement the delegation and/or completion block design.
EDIT:
In theory, a delegate is an object that acts on behalf of, or in coordination with, another object when that object encounters an event in a program. So when you set the delegate of your class as self you defeat the purpose of delegation pattern.
Hope that helps!
self.delegate = self is completely ok and a pretty common pattern when subclassing framework methods. delegate is almost always a weak reference so you're not getting a circular reference.
This is perfectly legal because your class conforms to the <UIAlertViewDelegate> protocol.
This is also a good idea because instead of out-sourcing your delegate to another class, you can keep all of it in house.
One contrary to this technique though, is that fact that it breaks the Model-View-Controller style. Having your view (subclasses of UIView) dealing with logic breaks the MVC paradigm. You might try creating a new class called MyAlertViewDelegate : NSObject <UIAlertViewDelegate>, instantiating it, then assigning it as your delegate. This keeps your MVC intact by separating the Model and the View.
Related
I have a textView "TexV" which have a custom class "TexV_Class" inherited from UITextView and I have a viewController "VC" with custom class named "VC_Class"
Now how can I make both classes "TexV_Class" and "VC_Class" delegate and make them work together? Is it even possible that same delegate method (eg. textViewDidChange) in BOTH classes runs (leaving the sequence of running for now)
I although had made both classes delegate but only one runs (that of VC_Class having textView delegate methods run)
You can't. The delegate mechanism works by having a single callback object, if you want more than one item to react based on the delegate you can go around this in one of two ways:
1- Fire a notification on one of your delegate so that the other delegate can act accordingly
2- set a custom delegate on TexV_Class that conforms to the method of UITextView that the VC_Class wants to adopt, and have TexV_Class call this delegate from it's delegate callback.
I suggest you 3 ways to do this:
1) Use NSNotificationCenter (the pattern help 1 object communicate one-to-many objects)
2) Use multicast delegate pattern. Implementation detail, you can refer this http://blog.scottlogic.com/2012/11/19/a-multicast-delegate-pattern-for-ios-controls.html
3) Use Proxy Design pattern. (This way I choosen)
class MyTextView.h
#protocol NJCustomTextViewDelegate <NSObject>
- textViewShouldBeginEditing:
- textViewDidBeginEditing:
- textViewShouldEndEditing:
- textViewDidEndEditing:
#end
#property (nonatomic, weak) id< NJCustomTextViewDelegate >textViewDelegate;
Use this:
in MyTextView.m
self.delegate = self;
- (void)textViewShouldBeginEditing:(UITextView)textView
{
// Handle business logi
// .... Do your logic here
if ([self.textViewDelegate responseToSelector:#selector(textViewShouldBeginEditing:)])
{
[self.textViewDelegate textViewShouldBeginEditing:self];
}
}
In MyViewController.m
MyTextView textView = ....
textView.textViewDelegate = self;
In my non-ARC iOS code, I use the following pattern: a delegate proxy class that forwards a single delegate message to some other class, then releases self. Here's an example for UIAlertView:
#interface AlertCallback : NSObject
<UIAlertViewDelegate>
{
NSObject *m_Sink;
SEL m_SinkSel;
}
-(id)initWithSink:(id)Sink SinkSel:(SEL)Sel;
#end
#implementation AlertCallback
-(id)initWithSink:(id)Sink SinkSel:(SEL)Sel
{
if(self = [super init])
{
m_Sink = Sink;
m_SinkSel = Sel;
}
return self;
}
- (void)alertView:(UIAlertView *)av didDismissWithButtonIndex:(NSInteger)n
{
//Call the callback
[m_Sink performSelector:m_SinkSel withObject:#(n)];
[self autorelease];
}
#end
//And finally usage:
AlertCallback *del =
[[AlertCallback alloc]
initWithSink:self
SinkSel:#selector(OnIAmSure:)];
UIAlertView *av = [[UIAlertView alloc] initWithTitle:nil
message:#"Are you sure?"
delegate: del
cancelButtonTitle:#"No"
otherButtonTitles: #"Yes", nil];
The idea here is that the proxy object will stay alive until the user taps a button, at which point the proxy will invoke its host's method and commit suicide. I'm using a similar pattern for action sheet and connections.
This doesn't translate to ARC. The delegate on the UIAlertView is weak. With only a weak ref to it, the AlertCallback with be released right away.
I can see several ways to overcome this. The callback can hold a reference to self (a deliberate ref loop) and nil it when the delegate message comes. It's also possible to derive a class from UIAlertView, implement the delegate protocol, and make it designate self as the delegate - but overriding the init method would be tricky; I don't know how to override a variadic method, passing an unknown number of parameters to the superclass. Finally, I could build a category on top of UIAlertView, specifying self as delegate, and use objc_setAssociatedObject for extra data items. Clunky, but it might work.
Is there a preferred/recommended way to implement this pattern under ARC?
Your first solution, keeping a self reference, works fine - see for example Manual object lifetime with ARC.
If you cannot, or do not wish to, modify the class to manage its own lifetime then a standard solution is to use associated objects. This is a standard runtime feature which effectively allows the lifetime of one object to be linked to that of another. In your case you can associate your delegate to your UIAlertView, effectively making the delegate reference strong rather than weak. Many questions on SO deal with associated objects, for example see Is it ever OK to have a 'strong' reference to a delegate?.
HTH
I'm really going crazy on this.
But let me explain to you my little project first:
I have an Custom UITableView TDStartTableView.
Also in there I have some methods implemented for rendering the table. No problem there.
Inside of one TableViewCell there is a button.
When that Button is clicked it triggers this method:
- (void)triggerPush {
[self.delegate pushNextView];
}
self.delegate is specified in the .h file of TDStartTableView like this:
#property (nonatomic, weak) id<TDStartTableViewDelegate> delegate;
Also, the reference is set in my UITableViewController:
self.tableView.delegate = self;
So essentially what I'm trying to do is: Create a custom UITableView with Buttons etc. and then listen on the events from a ViewController that is implementing that UITableView and the protocol
So because the protocol forces me to implement pushNextView this method is in my UIViewController:
- (void)pushNextView {
NSLog(#"This works");
}
To this point everything works just fine, no problem there!
But now comes the tricky part.
I create a segue from my UIViewController to a new ViewController. I connect them via a segue and name the segue appropriately. pushToSecondStep.
Now one would think, that when I change the implementation of pushNextView to this
- (void)pushNextView {
[self performSegueWithIdentifier:#"pushToSecondStep" sender:self];
}
it works. But what I get is:
'Receiver (<TDFirstStepTableViewController: 0x8dc97d0>) has no segue with identifier 'pushToSecondStep''
Please help, I'm going crazy :D
The problem was, that I overwrote a constructor of TDStartTableView.
The proper form is that you implement all constructors, so that Objective-C can instantiate them all by itself:
- (id) initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
return self;
}
Also when you implement a custom UITableView Widget you shouldn't use UITableViewController but UIViewController.
Also you don't initialize your custom UITableView yourself, Storyboard already does that for you, so if you want to set special variables for it like numberOfRows then just declare a property and then set it via a setter-method outside and call [tableView reloadData].
Also, thanks https://stackoverflow.com/users/1095089/shubhank for helping me in the chat :)
I have few viewControllers who use some alertbox, instead of having a delegate in every controller, i would like to create a class like "alertboxDelegate" for that, and link all my alertview to this delegate.
How can i do that?
Thank you
Depending on what you use your alert views for, it probably doesn't make sense to put all the delegate behavior for the whole app in a single class. Before you do this, you should make sure you're following single responsibility principle.
If you're sure you want to, you'd need to define a class and have it implement UIAlertViewDelegate.
// AlertViewDelegate.h
#interface AlertViewDelegate <UIAlertViewDelegate>
#end
// AlertViewDelegate.m
#implementation AlertViewDelegate
#end
In your view controller where you're presenting an alert view, you'll need to create an instance of this class, but it also has to be retained. The alert view itself won't do this, since delegates are weak references. You can use Objective-C associated objects to retain it, which will cause the delegate to be released when the alert view itself is released.
- (void)presentAlert
{
AlertViewDelegate delegate = [[AlertViewDelegate alloc] init];
UIAlertView *alert = [UIAlertView ...];
alert.delegate = delegate;
objc_setAssociatedObject(alert, "RetainedDelegate", delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[alert show];
}
I would strongly recommend against using this pattern, though, and implementing the delegate in the presenting view controller.
I am developing and iOS app for iPad. I have an UIView subclass and I'd like to call a method of the ViewController from that subclass. I've tried to code a delegate but it doesn't work. Is that a good solution or I have to do it another way?
Try block, here is the sample:
MyView.h
#interface MyView: UIView
...
#property (nonatomic, copy) void(^myEventCallBack)(id someData);
...
#end
MyView.m
(How to call block sample)
...
- (IBAction)buttonTapped:(UIButton *)sender {
if (self.myEventCallback) {
self.myEventCallback(self.someImportantData);
}
}
...
in your UIViewController:
self.myView = [[MyView alloc] initWithFrame:someFrame];
// THIS IS VERY IMPORTANT, USE __weak COPY OF YOUR UIViewController OBJECT (owner of object which contains block) INSIDE BLOCK TO PREVENT RETAIN CIRCLE, CAUSE BLOCK RETAINS ITS CONTENT
__weak MyViewController *self_ = self;
self.myView.myEventCallback = ^(id someData){
[self_ doSomeProcessingWithData:someData];
};
also block can be used with return value, sample:
#property (nonatomic, copy) BOOL(^shouldStartProcessing)(void);
self.myView.myEventCallback = ^BOOL{
return (self_.state == MyViewControllerStateReady);
};
In general the problem of communication between conceptually "distant" objects is tricky one, but it is the heart of Cocoa programming. Getting a reference from one instance to another is crucial! I discuss the problem in general here:
http://www.apeth.com/iOSBook/ch13.html#_instance_visibility
Here's one possibility. If this UIView instance is in the interface, then either its nextResponder is the view controller or it is the subview of a superview whose nextResponder is the view controller. Moreover, the view hierarchy parallels the responder chain. So simply walk up the responder chain until you come to the view controller.
UIResponder* r = someView; // the view instance, living in the interface
while (![r isKindOfClass:[UIViewController class]])
r = [r nextResponder];
Now r is the view controller. Now cast to what you know is the actual view controller's class, and you will be able to call methods on it:
MyViewController* vc = (MyViewController*)r;
Here's my book's summary of the responder chain:
http://www.apeth.com/iOSBook/ch11.html#_the_responder_chain
However, there are many other possibilities. As someone has already suggested, you could set up lines of communication by means of NSNotification (shoutcasting); it's ugly, but it does work, and it's intended in part to cover just this sort of tricky situation.