无法让代表在我的观点之间工作

时间:2015-09-08 21:19:25

标签: ios objective-c delegates

我需要从一个视图调用一些方法到另一个视图,我似乎无法让它工作。我做错了什么?

这是我的 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 ];
}

2 个答案:

答案 0 :(得分:1)

假设您想通过委托模式将ViewController中的数据发送到您的设置。在您的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

然后在你的ViewController.h中

#import "Settings.h"

@interface Settings : UIViewController<ViewControllerDelegate, UIPickerViewDataSource, UIPickerViewDelegate>

@end

这基本上说明你的ViewController将包含你在SettingsDelegate协议中定义的委托方法。

所以在你的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

然后在您的Settings.m中,适当的时间到来。你可以打电话:

[self.delegate getDefaults];

答案 1 :(得分: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:

  1. protocol definition must be MySettingsDelegate and not ViewControllerDelegate.
  2. rename mySettings to MySettingsViewController.
  3. MySettingsViewController should not import ViewController. It must use its delegate property to interact with ViewController.

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;
}