问题
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