Passing delegate to a viewcontroller through a viewcontroller - objective-c

I have three viewControllers in my storyboard and three viewController classes for each of them. From my main viewController, I am opening a navigation viewController in a 'modal' type segue, which is a multi step form and has two views in it. When the user hits 'Finish' on the last (which is second) view, the modal is dismissed and user is back to the main screen.
I am doing this with delegates. and the code for the finish button is also in a delegate and is placed on the main viewController's implementation file. In achieving this I passed the delegate from main view to the navigation's first view, and then from the first view on clicking 'next', I passed the delegate to the second (last) view (which has the finish button).
the passing of delegate from main to navigation's first page is like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"CreateCast"])
{
UINavigationController *navigationController = segue.destinationViewController;
CreateCastStepOneVC *createCastStepOneVC = [[navigationController viewControllers] objectAtIndex:0];
createCastStepOneVC.delegate = self;
}
}
the passing of delegate from navigation's first view to second view is like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ToCastStepTwo"])
{
CreateCastStepTwoVC *createCastStepTwoVC =
segue.destinationViewController;
createCastStepTwoVC.delegate = delegate;
}
}
Things are done well and delegate is doing its job as required. But a warning pops up which is a concern:
Passing '_weak id' to parameter of
incompatible type 'id'
Property declaration in first navigation view is like this:
#property (nonatomic, weak) id <CreateCastStepOneVCDelegate> delegate;
Property declaration is second navigation view is like this:
#property (nonatomic, weak) id <CreateCastStepTwoVCDelegate> delegate;

How have you declared the delegate property on CreateCastStepTwoVC? Also,
are your delegates conforming to a protocol you have defined?
A typical declaration for a delegate property would look something like this:
#property (nonatomic, __unsafe_unretained) id<MyProtocol> delegate;
or if you're not using a protocol (not recommended):
#property (nonatomic, __unsafe_unretained) id delegate;
EDIT:
Having seen your property declarations, you need to change weak to __unsafe_unretained as per this question: Recommended way to declare delegate properties with ARC

You can subclass UINavigationController and add a custom protocol in your subclass. With this approach you will be able to call your delegate from all your view controllers inside your navigation controller. For example, this is the way I used to do that:
#class CustomNavigationController;
#protocol CustomNavControllerDelegate <NSObject>
- (void)editImageController:(CustomNavControllerDelegate *)controller
didFinishPickingMediaWithInfo:(NSDictionary *)info;
- (void)editImageControllerDidCancel:(CustomNavControllerDelegate *)controller;
#end
#interface CustomNavigationController : UINavigationController
#property (nonatomic, weak) id <UINavigationControllerDelegate, CustomNavControllerDelegate> delegate;
#end
In this example I've implemented a similar functionality to a UIImagePickerController. In fact, this is the way the picker is implemented if you look at it's declaration file.

Related

Call method in different controller in objective c

I have a view controller from where I will be showing a window controller using [runmodal]. I have some textfields and button in the modal window. When i click the button i need to call a method in view controller with the collective data from window controller. How can i achieve this? Is there anything to do with custom delegate method? As I am new to Mac dev and objective c some one help me to do.
You can use the delegation pattern. You define a protocol like this:
#protocol DataProviderDelegate <NSObject>
- (NSDictionary *) retrieveData;
#end
implement this protocol in your view controller:
#interface MainViewController () <DataProviderDelegate>
#end
#implementation MainViewController {
...
- (NSDictionary *) retrieveData {
....
}
...
#end
In your window controller you define a delegate property
#interface ModalWindowController : NSWindowController
#property (nonatomic, weak) id <DataProviderDelegate> dataProviderDelegate;
#end
From the main view controller, set that property to self
modalWindow.dataProviderDelegate = self
At this point the modal window controller is able to call any method of the view controller that is defined in the DataProviderDelegate protocol - for instance:
if (self.dataProviderDelegate) {
[self.dataProviderDelegate retrieveData];
}
To dig more in the delegation pattern I suggest to google for it
You can use a delegate for this. In the ModalViewController you will have to implement a delegate that will have a method per action (textfields, buttons) :
ModalViewController.h
#class ModalViewController;
#protocol ModalViewControllerDelegate <NSObject>
- (void)modalViewControllerDelegateButtonPressed:(APPCameraOverlay *)overlay;
- (void)modalViewControllerDelegate:(APPCameraOverlay *)overlay
textFieldEdited:(NSString *)text;
#end
#interface ModalViewController : UIViewController
#property (nonatomic, weak) id <ModalViewControllerDelegate> delegate;
#end
Then, you will be able to call your delegate methods inside your ModalViewController.m :
ModalViewController.m
// The method linked to your button
- (IBAction)actionButtonPressed {
[self.delegate modalViewControllerDelegateButtonPressed:self];
}
// Your textfield method that is called when input has changed
- (void)textFieldDidEndEditing:(UITextField *)textField {
[self.delegate modalViewControllerDelegate:self
textFieldEdited:textField.text];
}
Now, you just have to set your ModalViewController delegate object in the ViewController when showing the modal controller :
ViewController.m
#import "ModalViewController.h"
// We create an extension to the class to implement the delegate protocol
#interface ViewController () <ModalViewControllerDelegate>
#end
#implementation ViewController
// This method gets called by apple when a view controller is showed (modally, pushed or embedded)
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// if the view that is showed is the ModalViewController
if ([segue.destinationViewController isKindOfClass:[ModalViewController class]]) {
ModalViewController *controller = segue.destinationViewController;
controller.delegate = self;
}
}
// You have to implement the delegate methods now :
- (void)modalViewControllerDelegateButtonPressed:(APPCameraOverlay *)overlay {
// Do whatever you want when the button is pressed on the ModalViewController
}
- (void)modalViewControllerDelegate:(APPCameraOverlay *)overlay
textFieldEdited:(NSString *)text {
// Do whatever you want when the textfield is edited
}
#end
Add the view controller as an instance variable to your (subclassed) window controller, and then when the button is clicked you can send a method to the view controller with the collective data from the window controller.

UIPopoverControllerDelegate delegate methods not being called

I have a screen with a button. Clicking the button will display a popover with two buttons. I want to be able to use the following method when the popover is dismissed:
#pragma mark - UIPopoverControllerDelegate
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
NSLog(#"OrderViewController.m -popoverControllerDidDismissPopover start");
}
The popover view has its own class:
#import <UIKit/UIKit.h>
#import "MenuItemModel.h"
#interface SelectedItemsOptionsViewController : UIViewController
#property (weak) id <UIPopoverControllerDelegate> delegate;
#property (strong, nonatomic) MenuItemModel *item;
#end
The class which creates the popover has:
#interface OrderViewController : UIViewController <PageViewControllerDelegate,
UITableViewDataSource, UITableViewDelegate, ServiceConnectorDelegate,
UIPopoverControllerDelegate>
So the OrderViewController - wants to know when SelectedItemsOptionsViewController has been dismissed.
I have the method declared (first block of code) and I am setting the delegate as:
SelectedItemsOptionsViewController *destViewController = (SelectedItemsOptionsViewController *)segue.destinationViewController;
popSegue = (UIStoryboardPopoverSegue *)segue;
[destViewController setDelegate:self];
destViewController.item = toDisplay;
So I am setting the delegate OK as far as i'm aware... the delegate property is #syntheized in th SelectedItemsOptionsViewController.m and well, its driving me a bit crazy.
I could be misunderstanding something here but looks like you're assigning the delegate for a UIViewController class, but where are you assigning the popOver its delegate, in the storyboard?

Pass a reference to ViewController in prepareForSegue

I have 2 view controllers, call them ViewController1 and ViewController2. A modal segue is invoked from ViewController1 when I want to load ViewController2. I have a method in ViewController1 that needs to be called at some point when ViewController2 is showing. My idea is to have a property in ViewController2 that is a reference to ViewController1 so that I can get access to the method.
#property (strong, nonatomic) ViewController1 *vc1Reference;
This property would be set in the prepareForSegue method like so:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // 1
if ([[segue identifier] isEqualToString:#"sequeToVC2"]) { // 2
ViewController2 *vc2 = segue.destinationViewController; // 3
vc2.vc1Reference = (ViewController1*)segue.sourceViewController; // 4
}
}
However line 4 gives me this error: Implicit conversion of an Objective-C pointer to 'int *' is disallowed with ARC.
How am I supposed to set the reference?
You are on the right track, but the correct way to do this is to use a delegate.
You declare a delegate property in your vc2 #interface:
#property (nonatomic, weak) id <vc2delegate> delegate //[1] (in vc2.h)
And you set the delegate in your prepareForSegue:
vc2.delegate = self; //[2] (in vc1.m)
('self' is the correct reference for vc1, from vc1)
In vc2 you define a protocol, which is the method that you expect vc1 to respond to from vc2. Put this in vc2.h, above your #interface
#protocol vc2delegate //[3] (in vc2.h)
- (void) delegateMethod;
#end
Then you have to ensure you implement that method in vc1. Also you need to let vc1 know to conform to the delegate. Import vc2 into vc1.h, and on your #interface line in vc1 add the protocol name in angle brackets:
#import vc2.h
#interface vc1 <vc2delegate> //[4] (in vc1.h)
This arrangement allows vc2 to pass a method to vc1 without having to #include vc1 or know anything else about it.
more detail...
This is the correct form of your
#property (strong, nonatomic) ViewController1 *vc1Reference;
Note the use of weak reference. You don't want to make a strong reference as you don't really want to have anything to do with the delegate except to know it can handle methods you specify in your protocol. The delegate is often the object that created the delegator, creating a strong reference back in the other direction can cause memory leaks as neither object can go out of existence.
this is the correct form of your line:
vc2.vc1Reference = (ViewController1*)segue.sourceViewController;
Note that we are NOT using type/casting in 1 or 2. For maximum code reuse/decoupling we dont want to make any suppositions on the type of object at either end of the segue.
I am assuming that your 'prepareForSegue' is in vc1. If it is not then the line would look like this:
vc2.delegate = segue.sourceViewController
This is the protocol declaration. It goes in the header file for vc2. vc2 is publishing it's expectations of any object that chooses to become its delegate. vc2 will be sending messages according to this protocol so any delegate needs to respond in the correct way. You can guard against failure in vc2 by using this kind of message-passing
if (self.delegate respondsToSelector:#selector('delegateMethod')
{
[self.delegate delegateMethod];
}
(that is an example of the kind of message passing you would use in vc2 to communicate back to vc1. you can obviously pass paremeters and get returned results back if need be)
this is a helper for the compiler which can issue you with warnings if you fail to implement the protocol.
Finally somewhere in your object definition you need to implement the method:
- (void) delegateMethod
{
// someaction;
}
I ran into something similar the other day. I ended up creating a delegate for vc2, and using
vc2.delegate = self;
in the segue instead. Would this solve your problem? If you need help setting up the delegate, let me know and I'll do my best to help!
Just add a delegate to your the ViewController that you are accessing the delegate on, XCode / Obj-C seems to do the right thing afterwards.
Before:
#interface ViewController2 : UIViewController
#end
After:
#interface ViewController2 : UIViewController
#property (nonatomic, weak) id <UIPageViewControllerDelegate> delegate;
#end

Change ViewControllers & Pass Data in iOS

I'm trying to have my app take a picture then pass the image onto another view for editing but I can't seem to figure out how to change views, how to add an "ID" to a view in the storyboard or how to pass data between views.
The communication between two UIViewControllers needs to be managed manually, however, if you are using storyboards to create your app, there's some things that you need to take into account.
Let's say you have FirstViewController and SecondViewController(Lets assume you have everything set up in your Storyboard). FirstViewController will pass a UIImage to SecondViewController and they will look something like this.
#interface FirstViewController : UIViewController
- (IBAction)transitionToNextViewController;
#property (retain, nonatomic) UIImage *image;
#end
#implementation FirstViewContoller
- (IBAction)transitionToNextViewController;
{
[self performSegueWithIdentifier:#"SegueIdentifier"];
}
#end
And:
#interface SecondViewController : UIViewController
#property (retain, nonatomic) UIImage *image;
#end
You are probably wondering how are you supposed to pass the image to the SecondViewController. Well, when using storyboards, your UIViewControllers will receive a call to their method prepareForSegue:sender: . All you have to do is set the image property for the second UIViewController there.
#implementation FirstViewController
- (IBAction)transitionToNextViewController;
{
[self performSegueWithIdentifier:#"SegueIdentifier"];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
SecondViewController *secondViewController = (SecondViewController *)segue.destinationViewController; // You have to cast it
secondViewController.image = self.image;
}
#end
and that's it. To better understand Storyboards please read the apple docs here.

How do I assign a delegate in a view for a control contained in a child view?

I have a view controller which contains a UISearchBar
#interface TradeFindHeaderViewController_iPhone : UIViewController {
UISearchBar *searchBar;
}
#pragma mark - Properties
#property (nonatomic, retain) IBOutlet UISearchBar *searchBar;
#pragma mark - Methods
-(void) configureSearchBar;
#end
This controller is then initialized and stored in a property of another controller.
#interface TradeFindViewController_iPhone : TradeFindViewController<UISearchBarDelegate> {
TradeFindHeaderViewController_iPhone *_headerController;
}
#pragma mark - Properties
#property (nonatomic, retain) TradeFindHeaderViewController_iPhone *headerController;
#end
I want this TradeFindViewController_iPhone to receive the UISearchBar delegate events so I assign it's delegate
-(void)configureTableHeader{
self.headerController=[[TradeFindHeaderViewController_iPhone alloc]initWithNibName:#"TradeFindHeaderView_iPhone" bundle: nil];
self.headerController.searchBar.delegate=self;
self.tableView.tableHeaderView=self.headerController.view;
}
However, the UISearchBar delegate events are not being called. Have I assigned the delegate properly given the UISearchBar is in the contained view?
I would probably implement a multi-level delegate system. Your TradeFindHeaderViewController_iPhone class would register as the delegate for the UISearchBar, and would then call a delegate method in your TradeFindViewController_iPhone class.
This solution helps to keep the whole design very modular, and also prevents things breaking (changing the name of objects) across classes.
This should solve your issue with the delegate methods not being called.
Hope this was of some help.
Josh