ModalViewController delegate confusion - iphone

I need to present a modal view controller and be notified when it is dismissed or notified that I need to dismiss it, looking here I am still confused:
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ModalViewControllers/ModalViewControllers.html#//apple_ref/doc/uid/TP40007457-CH111-SW14
I have my mainViewController and myModalView controller and I have the following code that needs to be implemented but not sure where - first up delegate protocal:
#protocol DataSyncDelegate <NSObject>
-(void) doneWithSync;
#end
which controller.h does this go in? I am assuming my modalViewController.h
second is my implementation:
-(void) doneWithSync {
[self dismissModalViewControllerAnimated:YES];
}
which controller.m does this go in? I am assuming my mainViewController.m
I also have the delegate properties that needs to be aded:
id delegate;
#property (nonatomic, retain) id delegate;
which controller.m does this need to go in? I am assuming my modalViewController.h
and here is how I am presenting the modalViewController from my MainViewController:
DataSyncViewController *dataSyncViewController = [[DataSyncViewController alloc] initWithOptions:FALSE];
dataSyncViewController.delegate = self;
[self presentModalViewController:dataSyncViewController animated:NO];
[dataSyncViewController release];
As of right now this gives me the following error:
-[DataSyncViewController setDelegate:]: unrecognized selector sent to instance 0x5952e20
What am I missing here?
EDIT - HERE IS MY MODAL VIEW CONTROLLER .H
#import <UIKit/UIKit.h>
#protocol DataSyncDelegate
-(void) doneWithSync;
#end
#interface DataSyncViewController : UIViewController {
id <DataSyncDelegate> delegate;
}
#property (nonatomic, retain) id <DataSyncDelegate> delegate;
#end
EDIT - MAIN VIEW CONTROLLER .H AND .M
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "DataSyncViewController.h"
#interface LoginViewController : UIViewController <DataSyncDelegate>{
}
#end
HERE IS THE CREATION OF THE MODAL:
DataSyncViewController *dataSyncViewController = [[DataSyncViewController alloc] initWithOptions:FALSE];
dataSyncViewController.delegate = self;
[self presentModalViewController:dataSyncViewController animated:NO];
[dataSyncViewController release];
HERE IS MY IMPLEMENTATION OF THE DELEGATE:
-(void) doneWithSync {
[self dismissModalViewControllerAnimated:YES];
}
And now everything looks to wire up correctly in the compiler but I get the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[DataSyncViewController setDelegate:]: unrecognized selector sent to instance 0x59e4b40'

Your main view controller IS the delegate and should implement the protocol. Your modalView has a delegate that it calls when it is being dismissed.

Related

Custom Delegate Does Not Receive a Call

My custom delegate does not receive a call. Here is my setup. ViewController has a SliderView object which is a subclass of UIScrollView:
SlideView.h
#import <UIKit/UIKit.h>
#protocol SlideViewDelegate <NSObject
#required
-(void) didTapImageData:(NSMutableArray*)imageData atIndex:(int)index;
#end
#interface SliderView : UIScrollView<UIGestureRecognizerDelegate> {
__weak id<SlideViewDelegate> slideViewDelegate;
}
#property (nonatomic, weak) id<SlideViewDelegate> slideViewDelegate;
#end
SlideView.m
#import "SliderView.h"
#implementation SliderView
#synthesize slideViewDelegate;
- (void) handleTap:(UITapGestureRecognizer *)recognizer{
NSLog(#"tapped");
[[self slideViewDelegate] didTapImageData: imageData atIndex:0 ];
}
ViewController.h
#interface ViewController : UIViewController <
UIScrollViewDelegate,
SlideViewDelegate>{
SliderView *thumbGalleryView;//delegate and reference are set in XCode
}
ViewController.m
#import "ViewController.h"
#implementation ViewController
-(void)didTapImageData:(NSMutableArray*) imageData atIndex:(int)index{
NSLog(#"NOT WORKING HERE");
}
So ViewController never receives a call at the method above. The thumbGalleryView is linked to ViewController and delegate is set to ViewController too. SlideView's handleTap is printing message fine but [[self slideViewDelegate] didTapImageData: imageData atIndex:0 ]; is ignored. Why?
You have set the delegate which is ivar of scroll view.
You have to set the slideViewDelegate to ViewController
Edited
Add IBOutlet
IBOutlet __weak id<SlideViewDelegate> slideViewDelegate;
Then from your xib connect slideViewDelegate to ViewController
Also remember to change the class of scroll view to SliderView
Added Image for clarity
Double-check where you set your delegate - if your delegate is nil at the time -handleTap: calls your -didTapImageData:atIndex: method, nothing will happen.

how to get its own view controller inside itself?

#interface RecentPhotosViewController () <PhotoViewControllerDelegate>
- (void)viewDidLoad
{
[super viewDidLoad];
[[self.tabBarController.viewControllers objectAtIndex:1] setDelegate:self];
}
The RecentPhotosViewController is a tableviewcontroller which implements a delegate.
I want to set self(RecentPhotosViewController) as the delegate in vieDidLoad(), but when i tried to type:self.setDelegate it turned out self doesn't have this setDelegate method, then i tried this:[[self.tabBarController.viewControllers objectAtIndex:1] setDelegate:self];(this tableview is one of the viewcontrollers of the tabBarControllers.
Then i got an error:[RecentPhotosViewController setDelegate:]: unrecognized selector sent to instance 0xc939960. I don't know how to fix this.
Do this:
#interface RecentPhotosViewController
//etc.
#property (nonatomic, assign) id delegate;
#end
#implementation RecentPhotosViewController
// etc.
#synthesize delegate;
#end

Calling protocol method with multiple subview hierarchy

I am fairly new to Obj-C and learning about using protocols and delegates.
I have no trouble following examples to implement a protocol/passing data when there are only two views, however, I am getting an "unrecognized selector" error when I try to call a method when I have several subviews.
For example, in a scenario where I have
FirstViewController
SecondViewController
ThirdViewController
I would like ThirdViewController to call back to the FirstViewController.
Generic code would be something like:
in FirstViewController.h
#interface FirstViewController : UIViewController <MyProtocol>
in firstViewController.m
//present a second controller which will control settings for the app
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:#"secondViewController" bundle:nil];
secondViewController.delegate= self;
secondViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController: secondViewController animated: YES];
and later
-(void) aMethod{
//carry out some action here
}
in secondViewController.m
//present a third controller...maybe a table view for selecting music
ThirdViewController *thirdViewController = [[ThirdViewController alloc] initWithNibName:#"thirdViewController" bundle:nil];
thirdViewController.delegate= self;
thirdViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController: thirdViewController animated: YES];
in ThirdViewContoller.h
//Create a protocol to implement options back on the firstViewController
#protocol MyProtocol;
#interface thirdViewController
{
IBOutlet UIButton *aButton;
}
#property (nonatomic, weak) id<MyProtocol> delegate;
-(IBAction) callMethod:(id)sender;
#end
#protocol MyProtocol <NSObject>
- (void) aMethod;
#end
in ThirdViewController.m
#synthesize delegate;
-(IBAction) callMethod:(id)sender{
[self.delegate aMethod];
}
When running it appears that the message is only sent back to secondViewController and not to the firstViewController because the error is:
-[SecondViewController aMethod:]: unrecognized selector sent to instance 0x19d620
I think there is something fundamental concept with setting delegate outlets that haven't learned yet, or the structure of the program is wrong.
There are numerous examples of code using only two view that work well here, but i haven't found much info on a multiple views. I apologize in advance if my program design is really incorrect.
You need SecondViewController to conform to your protocol:
#interface SecondViewController : UIViewController <MyProtocol>
You're trying to call a method on the second view controller that only exists in the first. If you want to communicate back to the first view controller, then you'll have to define a second protocol to do that.
This is a really good academic exercise to learn about the capabilities and limitations of protocols, but you should also notice the conflict in naming. Try to be as descriptive as possible when naming your protocols. Ideally, you'd have a set of header files that looked like this:
#interface FirstViewController : UIViewController <SecondViewControllerDelegate>
#end
And the second view controller:
#protocol SecondViewControllerDelegate <NSObject>
-(void)someSecondViewControllerDelegateMethod;
#end
#interface SecondViewController : UIViewController <ThirdViewControllerDelegate>
#protocol (nonatomic, weak) id <SecondViewControllerDelegate> delegate;
#end
And finally, the third view controller:
#protocol ThirdViewControllerDelegate <NSObject>
-(void)someThirdViewControllerDelegateMethod;
#end
#interface ThirdViewController : UIViewController
#protocol (nonatomic, weak) id <ThirdViewControllerDelegate> delegate;
#end
So the second view controller could implement -(void)someThirdViewControllerDelegateMethod and it could look like:
-(void)someThirdViewControllerDelegateMethod
{
[self.delegate someFirstViewControllerDelegateMethod];
}
And that's how the third view controller could call back to the first; it sort of cascades and passes on the message.

XCode: Call action in main view from modal view

I am trying to call an action (changeMainNumber) in a main view controller from a modal view controller. The action should change the UILabel mainNumber to 2. In ViewController.h, I have:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
IBOutlet UILabel *mainNumber;
}
#property (nonatomic, retain) UILabel *mainNumber;
-(IBAction)changeMainNumber;
ViewController.m:
#import "ViewController.h"
#implementation ViewController
#synthesize mainNumber;
- (IBAction)changeMainNumber:(id)sender {
mainNumber.text = #"2";
}
The next view controller is the modal view controller.
ModalViewController.h:
#import <UIKit/UIKit.h>
#class ViewController;
#interface ModalViewController : UIViewController {
}
-(IBAction)callChangeMainNumber:(id)sender;
and ModalViewController.m:
#import "ModalViewController.h"
#implementation ModalViewController
- (IBAction)callChangeMainNumber {
ViewController *viewController = [[ViewController alloc] init];
[viewController changeMainNumber];
}
With this setup the app keeps crashing when callChangeMainNumber is called and I can't figure out what is wrong. Any help you can provide is appreciated!
The code you posted from your ModalViewController is not referencing your ViewController. You are creating a new one in your code. The best solution to your problem would be to make your ViewController a delegate to the ModalViewController.
So in your ModalViewController.h file you should have this code above your #implementation.
#protocol ModalViewControllerDelegate
- (void)shouldChangeMainNumber;
#end
Then in your #implementation of the header have:
#property (nonatomic,assign)IBOutlet id <ModalViewControllerDelegate> delegate;
Now in the .m file where you have your IBAction method, tell the delegate that you want it to change the main number.
- (IBAction)callChangeMainNumber {
[self.delegate shouldChangeMainNumber];
}
Then in your ViewController.m file you need to set yourself as the delegate of the ModalViewController, usually in viewDidLoad is a good place to put it. So create a property in your header for the ModalViewController first and synthesize it, then add this to viewDidLoad.
self.modalViewController.delegate = self;
and finally you need to implement the delegate method in your .m file somewhere
- (void)shouldChangeMainNumber {
mainNumber.text = #"2";
}

Xcode: Does the delegating object HAVE to send a message to the delegate object?

I'm trying to assign SecondViewController as a delegate object of FirstViewController (if I understand correctly). However FirstViewController doesn't send any messages to SecondViewController.
Am I allowed to pretend as though SecondViewController did get a message from FirstViewController and respond to the FirstViewController? (Note: My SecondViewController is in charge of a view that has a button. When I press the button on my SecondViewController's view I want it to tell the FirstViewController to update its view)
FirstViewController.h
#import <UIKit/UIKit.h>
#protocol FirstViewControllerDelegate <NSObject>
#optional
- (void) setAnotherLabel;
#end
#interface FirstViewController : UIViewController {
IBOutlet UILabel *label;
id <FirstViewControllerDelegate> delegate;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#property (nonatomic, assign) id <FirstViewControllerDelegate> delegate;
- (void) pretendLabel;
- (void) realLabel;
#end
FirstViewController.m
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize label;
#synthesize delegate;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void) setAnotherLabel;
{
label.text =#"Real";
[self.view setNeedsDisplay];
}
- (void) pretendLabel;
{
label.text =#"Pretend";
[self.view setNeedsDisplay];
}
- (void) realLabel;
{
[self setAnotherLabel];
}
- (void)viewDidLoad
{
[super viewDidLoad];
label.text=#"Load";
[self pretendLabel];
}
...
#end
SecondViewController.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "FirstViewController.h"
#interface SecondViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate, FirstViewControllerDelegate>
{
UIImage *image;
IBOutlet UIImageView *imageView;
}
- (IBAction) sendPressed:(UIButton *)sender;
- (IBAction) cancelPressed:(UIButton *)sender;
#end
SecondViewController.m
#import "SecondViewController.h"
#implementation SecondViewController
- (IBAction) sendPressed:(UIButton *)sender
{
FirstViewController *fvc = [[FirstViewController alloc] init];
[fvc setDelegate:self];
//how do I find out if I'm actually the delegate for FirstViewController at this point?
[fvc realLabel];
self.tabBarController.selectedIndex = 0;//switch over to the first view to see if it worked
}
There are a few issues with this and what appears to be a bit of confusion.
I assume that FirstViewController and SecondViewController are in separate tabs in the tab bar controller.
In the sendPressed: method, you're creating a new instance of FirstViewController - this is not the same FirstViewController that is in your tab bar controller and why calling realLabel has no effect.
The second point is that you appear to misunderstand how delegation works - there is no reason for a delegate in the code you posted.
Good read for getting to grips with delegates: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/CommunicateWithObjects.html
As for a solution to your problem there are a few options:
Post a notification from SecondViewController that FirstViewController is observing (lots of information available on the net regarding notifications).
Get the specific instance of FirstViewController within the self.tabBarController.viewControllers array and call the method from there. Something like...
- (IBAction) sendPressed:(UIButton *)sender
{
for(UIViewController *controller in self.tabBarController.viewControllers)
{
if([controller isKindOfClass:[FirstViewController class]])
{
FirstViewController *firstViewController = (FirstViewController *)controller;
[firstViewController realLabel];
}
}
self.tabBarController.selectedIndex = 0;//switch over to the first view to see if it worked
}
There are more options available than this, but the above will give you a good start into researching the best approach for your need.
Hope this helps.