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.
Related
SettingsStore.h
#interface SettingsStore : IASKAbstractSettingsStore
{
#public
NSDictionary *dict;
NSDictionary *changedDict;
}
- (void)removeAccount;
#end
menuView.m
-(IBAction)onSignOutClick:(id)sender
{
SettingsStore *foo = [[SettingsStore alloc]init];
[foo removeAccount];
[self.navigationController pushViewController:foo animated:YES];
exit(0);
}
I want to call this removeAccount function from menuView.m. But I am getting error.
How to fix it and call this removeAccount.
There are few mistakes in your Code please find them below.
[foo removeAccount]; Calling this method is correct
[self.navigationController pushViewController:foo animated:YES];
Not correct because SettingsStore is not subclass of
UIViewController only subclass of UIViewController can be pushed to
Navigation controller
exit(0); Calling this method is not
recommended by Apple
You are calling removeAccount correctly from your menuView.m file, but there are several issues with your code:
You are treating foo as though it were a UIViewController, and it's actually a member of the SettingStore class. Does the SettingStore class refer to an actual screen, or is it more a data object (for storing settings?). If it's the latter, you don't want to push it on. You can create it, and use it, but the user doesn't need to see it.
You are calling exit(0); you can remove that line. If you want to remove the menuView.m file from your memory, remove references to it (e.g. from its parent view controller).
The menuView.m file is confusing, as in, is it a view or a viewController. An IBAction I would normally stick in a ViewController file, rather than a view file. Your basic design pattern is MVC (Model / View / Controller). In this case, it seems your SettingStore file is a Model (data), the menuView.m is a View and your code is for the Controller bit.
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
Trying to get a better understand of iOS delegation. I'm using UIImagePickerController as a reference but what's a good code example to use a delegate to dismiss my view controller?
I have a TabBarViewController that calls AViewController and want to use delegation to dismiss AViewController.
In this case, the idea is that the class that presented the new viewcontroller, should also be the one that dismisses it. The AViewController may have no clue how it was presented, so it wants to let the presenter, the TabBarViewController, handle the dismissal in whatever form needed. So, we need to define a protocol, say AViewControllerProtocol, which allows there to be a standard definition of the dismissal call:
This goes in AViewControllerProtocol.h:
#protocol AViewControllerProtocol <NSObject>
#required
- (void) dismissWithDone:(UIViewController *)viewController;
- (void) dismissWithCancel:(UIViewController *) viewController;
#optional
- (void)dismissWithDone:(UIViewController *)viewController andPlaySoundFile:(NSString *)soundPath;
#end
This goes in a file called AViewControllerProtocol.h. Both TabBarViewController.m and AViewController.m will import it. Think of this as a pact that any user of AViewController must agree to before it can utilize it. Similarly, you can't use UITableView without making a pact to observe the UITableViewDelegate and UITableViewDataSource protocols (those are two separate protocols).
Any class which wants to present AViewController, can see from the protocol definition that AViewController expects two required methods in order to be properly dismissed. It also has an optional third method that requests the presenter to play a sound after dismissing.
In order for AViewController to do its piece of this pact, it needs to get and store one piece of information about who the presenter is. That is called the delegate. The delegate is a property defined in the #interface of AViewController, in AViewController.h:
This goes in AViewController.h:
#property (nonatomic, weak) id<AViewControllerProtocol> delegate;
Now, the presenter, TabBarViewController, needs to do its bit. It needs to define the two required methods, plus maybe the optimal one, and it also needs to set the delegate value:
In TabBarViewController.m, in the #implementation:
This goes in TabBarViewController.m:
- (void) dismissWithDone:(UIViewController *)viewController
{
[self saveData:viewController.dataToSave]; // this could be the results that need to be saved
[viewController dismissViewControllerAnimated:YES completion:^{
;
}];
}
- (void) dismissWithCancel:(UIViewController *)viewController
{
// don't save data
[viewController dismissViewControllerAnimated:YES completion:^{
;
}];
}
The delegate value is set where AViewController is first created and/or before it is presented:
This also goes in TabBarViewController.m:
AViewController * aVC = [AViewController.alloc init];
aVC.delegate = self;
aVC.data = ...; // this may be the data you want changed by the VC
[self presentViewController:aVC animated:YES completion:^{
}];
Setting the delegate here is the only connection that the AViewController class has with its presenting viewController - which is the whole point here: child classes really shouldn't have to know a whole lot about the classes that utilize them.
Lastly, the AViewController class needs to add the following in order to call, via the delegate, back to the presenting class - so in AViewController.m:
This goes in AViewController.m:
-(IBAction)userHitButton:(id)sender
{
if (sender == doneButton) {
if ([_delegate respondsToSelector:#selector(dismissWithDone:)]) {
[_delegate dismissWithDone:self];
}
} else {
if ([_delegate respondsToSelector:#selector(dismissWithCancel:)]) {
[_delegate dismissWithCancel:self];
}
}
}
If you are wondering why the calling class has to:
aVC.delegate = self;
It is because there are situations where the class that actually creates a child class, isn't the one that will handle the delegate calls. In that case, instead of self, you put an instance of a class that will handle the delegate callbacks. For example, lets say that you have AViewController, BViewController and CViewController. They all get data from the user that needs to be saved. A class by the imaginary name of ABCDataHandler could be the one that can handle the dismissWithDone: and dismissWithCancel: callbacks and saves the data as needed, and TabBarViewController can stay out of any data handling activity.
That's to put it as simply as I can. I hope I haven't made any typos here :-)
I am making master detail application, i have dynamic Detail ViewController. Detail ViewController are changed.
But in every Detail ViewController I have one common method updateInfo I want to call that method
Here is my code
UINavigationController *nav=[self.splitViewController.viewControllers objectAtIndex:1];
UIViewController *controller=[nav.viewControllers objectAtIndex:0];
[controller updateLastInfo];
But it gives me error no method found.
it will work if i use UIViewController name.
HomeViewController *controller=(HomeViewController)[nav.viewControllers objectAtIndex:0];
[controller updateLastInfo];
But i dnt want to do above things.
I have tried to explain. Please help
You can use id
UINavigationController *nav=[self.splitViewController.viewControllers objectAtIndex:1];
id controller=[nav.viewControllers objectAtIndex:0];
[controller updateLastInfo];
You could subclass UIViewController and make a base DetailViewController class that houses common functionality of your detail view controllers. Then you would make all of your detail view controllers subclass DetailViewController instead of UIViewController. This would be a safe way to do it and would also allow you to add extra functionality to your updateInfo method in the specific detail view controllers.
If you want an unsafe way, you could make your controller object of type id. I wouldn't suggest this approach as it relies on your personal knowledge of the code. If someone else (or yourself down the road) sets it to a view controller that doesn't have that method, the code will still try to run and will crash.
UIViewController doesn't have a method named updateInfo, so the compiler will of course complain when you try to send that message to a pointer that's known only to point to an instance of UIViewController. When you use the class name, like this:
HomeViewController *controller=(HomeViewController)[nav.viewControllers objectAtIndex:0];
you're providing more information to the compiler, using a type cast to tell it "Hey, don't worry, I know for certain that the object I'll get back is a HomeViewController. Since you seem to have several types of view controllers that all have this method, the best thing to do is to declare the updateInfo method in a protocol and then have each of those UIViewController subclasses implement that protocol. So, your protocol declaration would be in a header file and might look like:
#protocol SomeProtocol
- (void)updateInfo
#end
and each class that has an -updateInfo method would just need to declare that it adopts the protocol:
#interface HomeViewController <SomeProtocol>
//...
#end
and then make sure that you have an -updateInfo in your class implementation:
#implementation HomeViewController
- (void)updateInfo {
//...
}
//...
#end
Then, in your code, you can either check that the object conforms to the protocol using -conformsToProtocol: like this:
if ([controller conformsToProtocol:#protocol(SomeProtocol)]) {
UIViewController<SomeProtocol> *c = (UIViewController<SomeProtocol>*)controller;
[c updateInfo];
}
or else just check that the object responds to the selector before calling it:
if ([controller respondsToSelector:#selector(updateInfo)]) {
[controller performSelector(updateInfo)];
}
The other answers you've received (using id or creating a common base class) are also good ones, but to be safe make sure you do some checking before calling your method. For example, you can use -isKindOfClass to make sure that the view controller you get back is in fact an instance of your common base class, and you can use -respondsToSelector: as above to check that an id points to an object that implements updateInfo.
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.