Cannot get delegate to work between my views

走远了吗. 提交于 2019-12-25 14:24:32

问题


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:

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



回答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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!