How to solve a delegate that is still nil

孤人 提交于 2020-01-06 14:05:08

问题


I am concerned with my 4 view controllers. The first one is the CardWalletViewController which is a table view that is populated by Card Objects that the user has created (the card creation is OK so I won't post the code regarding that). Now, given that this table view is already populated with Card Objects, once the user clicks on a cell, another view controller, named CardDetailsViewController will show up. This will display the current points that the Card has and also the perks. Note that the current points is shown through a UILabel and the perks were a table view's cells. This table view's cell values(perks), are statically typed and are coming from a NSDictionary and then stored in an NSArray (for cell's text display purpose). Now, when I click on a perk, the PerksDetailsViewController will show up. This contains UILabels which show the perk's name and the points it requires. Those UILabels values were coming from the former ViewController. Also, PerksDetailsVC has a button named redeem. Upon clicking that I'll proceed to another view controller, BarCodeViewController, which takes care of the computation.

Now my concern is on how to pass the selected Card, and also the points that corresponds to a selected perk. I've used delegation to do this but then it seems that my delegation object is not being set.

Below is my current code.

CardWalletViewController.h

#import <UIKit/UIKit.h>
#import "Card.h"

@class CardWalletViewController;

@protocol CardWalletDelegate <NSObject>

-(void)cardWalletViewController: (CardWalletViewController *)sender
                withCurrentCard: (Card *) currentCard;

@end

@interface CardWalletViewController : UITableViewController 

@property (nonatomic, strong) NSMutableArray *myWallet;

@property (nonatomic, weak) id <CardWalletDelegate> delegate;

@end  

CardWalletViewController.m

#import "CardWalletViewController.h"
#import "AddCardViewController.h"
#import "Card.h"
#import "CardDetailsViewController.h"

@interface CardWalletViewController ()

@end

@implementation CardWalletViewController

@synthesize delegate = _delegate;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];

    }
    Card *cardDummy = [self.myWallet objectAtIndex:indexPath.row]; //myWallet is an Array where the table cell's values come from
    cell.textLabel.text = cardDummy.name;
    cell.detailTextLabel.text = [NSString stringWithFormat:@"%@", cardDummy.points]; 

    return cell;
}

- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    //this method is responsible for showing the details of a selected card

    CardDetailsViewController *details = [self.storyboard instantiateViewControllerWithIdentifier:@"cardDetails"];


    Card *selectedCard = [self.myWallet objectAtIndex:indexPath.row]; // I want this selected card to be accessible until the user clicks another card or during end of program.

    details.myPoints = [NSString stringWithFormat:@"%@", selectedCard.points];

    [self.navigationController pushViewController:details animated:YES];


    [self.delegate cardWalletViewController:self withCurrentCard:selectedCard];

    //checking purposes
    if (!self.delegate) {
        NSLog(@"delegate is nil");
    }
}

CardDetailsViewController.m

#import "CardDetailsViewController.h"
#import "PerksDetailsViewController.h"
#import "Card.h"

@interface CardDetailsViewController ()

@end

@implementation CardDetailsViewController

@synthesize pointsLabel = _pointsLabel;
@synthesize myPoints  = _myPoints;

@synthesize perks = _perks;
@synthesize datasource = _datasource;
@synthesize datasourcePoints = _datasourcePoints;

-(void)setupArray
{
    self.perks = [[NSMutableDictionary alloc] init];
    [self.perks setObject:@"200" forKey:@"10% Discount"];
    [self.perks setObject:@"100" forKey:@"250Php Off"];

    self.datasource = [self.perks allKeys]; //contains perk's description
    self.datasourcePoints = [self.perks allValues]; //contains perk's required points
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{
    return 2;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

 cell.textLabel.text = [self.datasource objectAtIndex:indexPath.row];
    cell.detailTextLabel.text = [self.datasourcePoints objectAtIndex:indexPath.row];

    return cell;
}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    PerksDetailsViewController *perksDetails = [self.storyboard instantiateViewControllerWithIdentifier:@"detailsOfMyPerks"];

    [self.navigationController pushViewController:perksDetails animated:YES];

    perksDetails.perkDetailsLabel.text = [self.datasource objectAtIndex:indexPath.row];
    perksDetails.pointsLabel.text = [self.perks objectForKey:perksDetails.perkDetailsLabel.text];
}

- (void)viewDidLoad
{

    //show the number of points of the selected Card
    self.pointsLabel.text = self.myPoints;

    [self setupArray];

    [super viewDidLoad];
}

PerksDetailsViewController.m

#import "PerksDetailsViewController.h"
#import "Card.h"
#import "CardWalletViewController.h"
#import "BarcodeViewController.h"

@interface PerksDetailsViewController ()

@end

@implementation PerksDetailsViewController

@synthesize pointsLabel = _pointsLabel;
@synthesize perkDetailsLabel = _perkDetailsLabel;
@synthesize perkDetailText = _perkDetailText;
@synthesize pointsText = _pointsText;
@synthesize delegate = _delegate;
@synthesize pointsRequired = _pointsRequired;


- (IBAction)redeemPressed:(id)sender {
    // get required points of a perk selected
    // cast the NSString value to an NSInteger

     NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
     [f setNumberStyle:NSNumberFormatterDecimalStyle];

     self.pointsRequired = [f numberFromString: (self.pointsLabel.text)];

    NSLog(@"points required by the perk %@", self.pointsRequired);

    [self.delegate perksDetailsViewController:self didPassRequiredPoints:self.pointsRequired];

    if (!self.delegate){
        NSLog(@"delegate is nil");
    }
}

PerksDetailsViewController.h

#import <UIKit/UIKit.h>
#import "BarcodeViewController.h"

@class PerksDetailsViewController;

@protocol PerksDetailsDelegate <NSObject>

- (void)perksDetailsViewController:(PerksDetailsViewController *)sender
             didPassRequiredPoints: (NSNumber *) requiredPoints;

@end

@interface PerksDetailsViewController : UIViewController
{
    NSString *perkDetailText;
    NSString *pointsText;
    IBOutlet UILabel *perkDetailsLabel;
    IBOutlet UILabel *pointsLabel;
}

@property (nonatomic, retain) IBOutlet UILabel *perkDetailsLabel, *pointsLabel;
@property (nonatomic, retain) NSString *perkDetailText, *pointsText;
@property (nonatomic, weak) NSNumber *pointsRequired;

@property (nonatomic, weak) id <PerksDetailsDelegate> delegate;

@end

Now this ViewController is the one that implements the two delegate methods

BarCodeViewController.h

#import <UIKit/UIKit.h>

@interface BarcodeViewController : UIViewController 

@property (nonatomic, strong) NSNumber *myPoints;

@end

BarCodeViewController.m

  #import "BarcodeViewController.h"
    #import "CardWalletViewController.h"
    #import "Card.h"
    #import "PerksDetailsViewController.h"

    @interface BarcodeViewController () <CardWalletDelegate, PerksDetailsDelegate>

    @end

    @implementation BarcodeViewController

    @synthesize myPoints = _myPoints;
    - (void)perksDetailsViewController:(PerksDetailsViewController *)sender didPassRequiredPoints:(NSNumber *)requiredPoints 
    {
        self.myPoints = requiredPoints;
    }

    - (void)cardWalletViewController:(CardWalletViewController *)sender withCurrentCard:(Card *)currentCard 
    {
        Card *myCurrentCard = currentCard;
        [myCurrentCard.pointsToDeduct addObject:self.myPoints];

        NSLog(@"contents :%@", [myCurrentCard.pointsToDeduct description]);
    }

@end

回答1:


First of all, your delegate object property declaration

@property (nonatomic, strong) id <PerksDetailsDelegate> delegate;

should be changed from strong to weak, since your class doesn't own it.

Second, you never set the delegate object for your class. That's the reason why it is nil. My guess is that BarCodeViewController (your PerksDetailsDelegate protocol implementing class) should be an instance variable of the CardDetailsViewController and you missed to set it as a delegate of the perksDetails object in didSelectRowAtIndexPath.

Edit.

You should have an instance of BarCodeViewController in the CardDetailsViewController class (named bcvController, for the example code below):

CardDetailsViewController.m

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    PerksDetailsViewController *perksDetails = [self.storyboard instantiateViewControllerWithIdentifier:@"detailsOfMyPerks"];
    [perksDetails setDelegate:bcvController]; // bcvController should be created before this line
    [self.navigationController pushViewController:perksDetails animated:YES];

    perksDetails.perkDetailsLabel.text = [self.datasource objectAtIndex:indexPath.row];
    perksDetails.pointsLabel.text = [self.perks objectForKey:perksDetails.perkDetailsLabel.text];
}

Edit 2

BarCodeViewController.h

#import <UIKit/UIKit.h>
#import "CardWalletViewController.h"
#import "Card.h"
#import "PerksDetailsViewController.h"

@interface BarcodeViewController : UIViewController <CardWalletDelegate, PerksDetailsDelegate>

@property (nonatomic, strong) NSNumber *myPoints;

@end

BarCodeViewController.m

#import "BarcodeViewController.h"

//    @interface BarcodeViewController () 
//
//    @end

@implementation BarcodeViewController

@synthesize myPoints = _myPoints;
- (void)perksDetailsViewController:(PerksDetailsViewController *)sender didPassRequiredPoints:(NSNumber *)requiredPoints 
{
    self.myPoints = requiredPoints;
}

- (void)cardWalletViewController:(CardWalletViewController *)sender withCurrentCard:(Card *)currentCard 
{
    Card *myCurrentCard = currentCard;
    [myCurrentCard.pointsToDeduct addObject:self.myPoints];

    NSLog(@"contents :%@", [myCurrentCard.pointsToDeduct description]);
}

@end

Edit 3

If you want to display a BarcodeViewController, you should use this object's bcvController.

CardDetailsViewController.m

#import "CardDetailsViewController.h"
#import "PerksDetailsViewController.h"
#import "Card.h"

@implementation CardDetailsViewController

@synthesize pointsLabel = _pointsLabel;
@synthesize myPoints  = _myPoints;

@synthesize perks = _perks;
@synthesize datasource = _datasource;
@synthesize datasourcePoints = _datasourcePoints;
@synthesize bcvController = _bcvController; // new instance variable

// other functions removed for clarity

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    PerksDetailsViewController *perksDetails = [self.storyboard instantiateViewControllerWithIdentifier:@"detailsOfMyPerks"];
    [perksDetails setDelegate:bcvController];
    [self.navigationController pushViewController:perksDetails animated:YES];

    perksDetails.perkDetailsLabel.text = [self.datasource objectAtIndex:indexPath.row];
    perksDetails.pointsLabel.text = [self.perks objectForKey:perksDetails.perkDetailsLabel.text];
}

- (void)viewDidLoad
{

    //show the number of points of the selected Card
    self.pointsLabel.text = self.myPoints;

    [self setupArray];

    [self setBcvController:[self.storyboard instantiateViewControllerWithIdentifier:@"myBarcodeVC"]]; // use setter to retain object properly

    [super viewDidLoad];
}


来源:https://stackoverflow.com/questions/10275562/how-to-solve-a-delegate-that-is-still-nil

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