问题
I need to call some methods from one view to another, and I can't seem to get it to work. What am I doing wrong?
Here's my ViewController.h
#import <UIKit/UIKit.h>
#import "mySettings.h"
@protocol ViewControllerDelegate <NSObject>
@required
- (void) getDefaults;
- (void) updateSubTotal: ( float ) value;
@end
@interface ViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate> {
id <ViewControllerDelegate> delegate;
}
@property (retain) id delegate;
.
. (other declarations)
.
@end
ViewController.m
#import "ViewController.h"
#import "mySettings.h"
@interface ViewController ( )
@property (assign) mySettings *settingsVC;
@end
@implementation ViewController
- ( void ) viewDidLoad {
[ super viewDidLoad ] ;
// Do any additional setup after loading the view, typically from a nib.
self.userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.Just-The-Tip"] ;
[ self getDefaults ] ;
self.arrPercent = @ [ @"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12",@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20",@"21",@"22",@"23",@"24",@"25",@"26",@"27",@"28",@"29",@"30",@"31",@"32",@"33",@"34",@"35",@"36",@"37",@"38",@"39",@"40",@"41",@"42",@"43",@"44",@"45",@"46",@"47",@"48",@"49",@"50" ] ;
self.arrPeople = @ [ @"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12",@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20" ] ;
self.myPicker.dataSource = self;
self.myPicker.delegate = self;
if ( self.bRememberLastBill )
{
self.subtotal = [ self.userDefaults floatForKey:@"sub_total" ] ;
self.strSubTotal = [ NSString stringWithFormat: @"%.2f", self.subtotal ] ;
[ self updateSubTotal:-3 ] ;
}
// BEGIN ENABLE DONE BUTTON FOR NUMPAD
UIToolbar * keyboardDoneButtonView = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 32)];;
keyboardDoneButtonView.items = [NSArray arrayWithObjects:
[[UIBarButtonItem alloc] initWithTitle:@"Clear" style:UIBarButtonItemStyleDone target:self action:@selector(clearClicked:)],
[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
[[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(doneClicked:)], nil
];
[keyboardDoneButtonView sizeToFit];
self.field_SubTotal.inputAccessoryView = keyboardDoneButtonView;
// END ENABLE DONE BUTTON FOR NUMPAD
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 16, 60)];
self.field_SubTotal.rightView = paddingView;
self.field_SubTotal.rightViewMode = UITextFieldViewModeAlways;
self.settingsVC = [[mySettings alloc] init];
self.settingsVC.delegate = self ;
[ self animate ] ;
}
- ( void ) getDefaults
{
// lots of stuff here
}
- ( void ) updateSubTotal: ( float ) value
{
// even more code here
}
mySettings.h
#import <UIKit/UIKit.h>
#import "ViewController.h"
@interface mySettings : UIViewController {
}
@property (nonatomic, assign) id delegate;
.
. (a bunch of declarations)
.
@end
mySettings.m
#import "mySettings.h"
#import "ViewController.h"
@interface mySettings ( )
@end
@implementation mySettings
@synthesize delegate;
- ( void ) viewDidLoad {
[ super viewDidLoad ] ;
// Do any additional setup after loading the view.
//self.userDefaults = [ NSUserDefaults standardUserDefaults ] ;
self.userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.Just-The-Tip"] ;
[ self getDefaults ] ;
self.textDefaultTax.text = [ NSString stringWithFormat:@"%.3f", self.default_tax ] ;
self.textDefaultTip.text = [ NSString stringWithFormat:@"%.f", self.default_tip ] ;
// BEGIN ENABLE DONE BUTTON FOR NUMPAD
UIToolbar * keyboardDoneButtonView = [[UIToolbar alloc] init];
[keyboardDoneButtonView setItems:[NSArray arrayWithObjects:
[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
[[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(doneClicked:)],
nil]];
[keyboardDoneButtonView sizeToFit];
self.textDefaultTip.inputAccessoryView = keyboardDoneButtonView;
self.textDefaultTax.inputAccessoryView = keyboardDoneButtonView;
// END ENABLE DONE BUTTON FOR NUMPAD
[ self animate ] ;
}
// a few methods later
- (IBAction)up_default_exclude_tax:(id)sender {
self.bExcludeTax = self.switchExcludeTax.isOn;
[ self setDefaults ] ;
[ self.delegate getDefaults ];
[ self.delegate updateSubTotal:-3 ];
}
回答1:
Frankly, you have almost confused me :). The way you are implementing delegate design pattern and connecting view controllers is not the suggested/documented way of doing it.
Lets proceed step by step.
Step 1: MySettings (like UITableViewController) implements some feature and relies on its delegate to implement others.
So, following points to take away from here:
- protocol definition must be
MySettingsDelegate
and notViewControllerDelegate
. - rename
mySettings
toMySettingsViewController
. MySettingsViewController
should not importViewController
. It must use itsdelegate
property to interact withViewController
.
This is how my MySettingsViewController
would look like post these changes.
MySettingsViewController.h
`
@protocol MySettingsViewControllerDelegate <NSObject>
@required
- (void) getDefaults;
- (void) updateSubTotal: ( float ) value;
@end
@interface MySettingsViewController : UIViewController {
}
@property (nonatomic, assign) id delegate;
.
. (a bunch of declarations)
.
@end`
Step2: You don't need to call @synthesis on properties any more. Also, before calling a method on delegate it is always good practice to do a nil check. Please see how my MySettingsViewController.m
would look like. I have also added some NSLog statement for you to verify if controls comes there or not.
MySettingsViewController.m
@implementation MySettingsViewController
- ( void ) viewDidLoad {
[ super viewDidLoad ] ;
// Do any additional setup after loading the view.
//self.userDefaults = [ NSUserDefaults standardUserDefaults ] ;
self.userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.Just-The-Tip"] ;
[ self getDefaults ] ;
self.textDefaultTax.text = [ NSString stringWithFormat:@"%.3f", self.default_tax ] ;
self.textDefaultTip.text = [ NSString stringWithFormat:@"%.f", self.default_tip ] ;
// BEGIN ENABLE DONE BUTTON FOR NUMPAD
UIToolbar * keyboardDoneButtonView = [[UIToolbar alloc] init];
[keyboardDoneButtonView setItems:[NSArray arrayWithObjects:
[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
[[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(doneClicked:)],
nil]];
[keyboardDoneButtonView sizeToFit];
self.textDefaultTip.inputAccessoryView = keyboardDoneButtonView;
self.textDefaultTax.inputAccessoryView = keyboardDoneButtonView;
// END ENABLE DONE BUTTON FOR NUMPAD
[ self animate ] ;
}
// a few methods later
- (IBAction)up_default_exclude_tax:(id)sender {
self.bExcludeTax = self.switchExcludeTax.isOn;
[self setDefaults];
NSLog(@"Action taken");
if (self.delegate && [self.delegate respondsToSelector:@selector(getDefaults)]) {
NSLog(@"Calling getDefaults");
[self.delegate getDefaults];
}
if (self.delegate && [self.delegate respondsToSelector:@selector(updateSubTotal:)]) {
NSLog(@"Calling updateSubTotal:");
[self.delegate updateSubTotal:-3];
}
}
Step 3: Now, that my support class ready, lets use it. Time to write ViewController class. Not sure why you added delegate in header of ViewController as well, we literally do not need it here. This is how my ViewController.h
will look like:
ViewController.h
@interface ViewController : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate, MySettingsViewControllerDelegate> {
}
.
. (other declarations)
.
@end
Steps 4: Now comes the main part. Please ensure you hold a strong reference to MySettingsViewController
. This is how my ViewController.m
would look like:
ViewController.m
@interface ViewController()
@property (nonatomic, strong) MySettingsViewController *settingsVC;
@end
- ( void ) viewDidLoad {
[super viewDidLoad ] ;
// Do any additional setup after loading the view, typically from a nib.
self.userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.Just-The-Tip"] ;
[ self getDefaults ] ;
self.arrPercent = @ [ @"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12",@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20",@"21",@"22",@"23",@"24",@"25",@"26",@"27",@"28",@"29",@"30",@"31",@"32",@"33",@"34",@"35",@"36",@"37",@"38",@"39",@"40",@"41",@"42",@"43",@"44",@"45",@"46",@"47",@"48",@"49",@"50" ] ;
self.arrPeople = @ [ @"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12",@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20" ] ;
self.myPicker.dataSource = self;
self.myPicker.delegate = self;
if ( self.bRememberLastBill )
{
self.subtotal = [ self.userDefaults floatForKey:@"sub_total" ] ;
self.strSubTotal = [ NSString stringWithFormat: @"%.2f", self.subtotal ] ;
[ self updateSubTotal:-3 ] ;
}
// BEGIN ENABLE DONE BUTTON FOR NUMPAD
UIToolbar * keyboardDoneButtonView = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 32)];;
keyboardDoneButtonView.items = [NSArray arrayWithObjects:
[[UIBarButtonItem alloc] initWithTitle:@"Clear" style:UIBarButtonItemStyleDone target:self action:@selector(clearClicked:)],
[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
[[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(doneClicked:)], nil
];
[keyboardDoneButtonView sizeToFit];
self.field_SubTotal.inputAccessoryView = keyboardDoneButtonView;
// END ENABLE DONE BUTTON FOR NUMPAD
UIView *paddingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 16, 60)];
self.field_SubTotal.rightView = paddingView;
self.field_SubTotal.rightViewMode = UITextFieldViewModeAlways;
self.settingsVC = [[MySettingsViewController alloc] init];
self.settingsVC.delegate = self ;
[ self animate ] ;
}
- ( void ) getDefaults
{
// lots of stuff here
}
- ( void ) updateSubTotal: ( float ) value
{
// even more code here
}
I believe this must solve your issue and would bring clarity to delegate pattern. You also need to ensure that while MySettingsViewController
is active and calls its delegate, ViewController
also stays in memory.
Edit: If you are using storyboard
Please ensure you are setting the delegate correctly on the right object. Do not instantiate your own object.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"SegueRegistrationUserAction"]) {
[(MySettingsViewController *)segue.destinationViewController setDelegate:self];
}
}
Final Edit (Fixed in Code):
So, I took a look at your code and found the issue. It was apparently different objects issue. You are using storyboard and also creating a mySettings
object in code as well. When you connect via storyboard, you should use the object created from storyboard and avoid creating your own. When I changed the below method in your ViewController.m
, it fixed the issue.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
self.settingsVC = (mySettings *)segue.destinationViewController;
self.settingsVC.delegate = self;
}
回答2:
Assuming you want to send data from your ViewController to your Settings via the delegate pattern.In your Settings.h:
#import <UIKit/UIKit.h>
@protocol SettingsDelegate
@required
- (void) getDefaults;
- (void) updateSubTotal: ( float ) value;
@end
@interface Settings : UIViewController
@property (weak, nonatomic) id<ViewControllerDelegate> delegate;
@end
Then in your ViewController.h
#import "Settings.h"
@interface Settings : UIViewController<ViewControllerDelegate, UIPickerViewDataSource, UIPickerViewDelegate>
@end
This basically says that your ViewController is going to contain the delegate methods that you defined in the SettingsDelegate protocol.
So in your ViewController.m:
@interface ViewController ()
@end
@implementation ViewController
- (void) viewDidLoad{
viewController.delegate = self; //somehow get a copy of your viewController instance and set the delegate. This doesn't have to be in viewDidLoad, but it needs to happen sometime.
}
- (void) getDefaults{
//Do stuff. You probably want to change the return type to something other than void if we are actually getting defaults here.
}
- (void) updateSubTotal: ( float ) value{
//Do stuff.
}
...
@end
Then in your Settings.m, when the appropriate time comes. you can call:
[self.delegate getDefaults];
来源:https://stackoverflow.com/questions/32467416/cannot-get-delegate-to-work-between-my-views