Hi this is my first question
look my code. I dont understand why my array becomes null after changing view
FirstView:
I try tu use a delegate but it's not working. my delegate is never call.
h:
#interface CaisseViewController : UIViewController <testDelegate>
#property (nonatomic, retain) NSMutableArray *testArray;
-(void)autrePaiementChoisie:(AutrePaiementCaisseTableViewController*)controller selectPaiement:(NSString *)paiement;
#end
m:
#synthesize testArray;
- (void)viewDidLoad
{
testArray = [[NSMutableArray alloc]initWithObjects:#"TEST 1",#"TEST 2", nil];
}
- (IBAction)autre:(id)sender {
NSLog(#"testArray %#",testArray); // OK !
[self performSegueWithIdentifier:#"autre" sender:self];
}
-(void)autrePaiementChoisie:(AutrePaiementCaisseTableViewController*)controller selectPaiement:(NSString *)paiement {
[controller dismissViewControllerAnimated:YES completion:nil];
NSLog(#"TEST ARRAY %#",testArray); // Is NULL
}
//function of my delegate
-(void)sendString:(NSString *)aString {
NSLog(#"string %#",aString); //dont work ! never called
}
SecondView: declare the delegate
h:
#protocol testDelegate <NSObject>
-(void)sendString:(NSString*)aString;
#end
#property (nonatomic,assign) id<testDelegate>delegate;
m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *paiement = [self.paiementArray objectAtIndex:indexPath.row];
[self.delegate sendString:[paiement valueForKey:#"nom"]]; //talk with my delegate
// CaisseViewController *caisseView = [[CaisseViewController alloc]init];
//[caisseView autrePaiementChoisie:self selectPaiement:[paiement valueForKey:#"nom"]];
}
The reason testArray is nil is that in the second view controller you are creating a second instance of CaisseViewController in didSelectRowAtIndexPath which does has not executed viewDidLoad (and even if it did you would have a separate copy of the testArray).
As part of loading and moving to the second view controller you should pass the instance of the first view controller to it (saving it as a weak pointer like a delegate). You can use that instance in the second view controller's didSelectRowAtIndexPath to call back to the first one.
Your code should be throwing an error for this line.
testArray = [[NSMutableArray ...
You haven’t defined testArray in this method and are assigning something to it. Try changing testArray to self. testArray in all of your code.
Here :
CaisseViewController *caisseView = [[CaisseViewController alloc]init];
[caisseView autrePaiementChoisie:self selectPaiement:[paiement valueForKey:#"nom"]];
You instantiated a CaisseViewController and call its method (autrePaiementChoisie:selectPaiement:) even before the viewdidload. Your testArray isn't instantiated at the time you print it. Maybe you should use 'lazy loading' for your testArray instead of initializing it in viewDidLoad.
Here is how it works, put this code in the firstVC.m :
-(NSMutableArray *) testArray
{
if (!_testArray) {
_testArray = [[NSMutableArray alloc]initWithObjects:#"TEST 1",#"TEST 2", nil];
}
return _testArray
}
Related
I have no idea how I should correctly name the title but I know exactly what my problem is (I will eventually edit the title later).
I am pretty new to Objective-C and I am still learning.
So, I have a class that contains a tableView (I will call it ClassA) and another with a normal UIView (ClassB). What I want to do, is to let a button appear when a row is selected.
I created in my ClassB.h file:
+(id)sharedInstance;
#property (retain, nonatomic) IBOutlet UIButton *btn;
-(void) showBtn :(BOOL) show;
And in my ClassB.m file:
#synthesize btn;
static ClassB *this = nil;
(+id) sharedInstance {
if(!this) {
#synchronized (self) {
this = [[ClassB alloc] init];
}
}
return this;
}
-(void)viewDidLoad {
[self showBtn:NO] //because I only want to let it appear when a row is selected.
[self.view addSubview:btn];
}
-(void) showBtn :(BOOL) show { // I called this method in classA.
if (show == NO) {
btn.hidden = YES;
} else {
btn.hidden = NO;
}
}
So when I launch my app, the button is hidden and stays hidden when I select a row. I debugged, and found that btn is nil when I called the method in ClassA. After some research, I found that the method is called for another instance, so here my question, what can I do, to get it called for the right instance?
EDIT
Here part of my ClassA.m
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row = [indexPath row];
[[ClassB sharedInstance] showBtn:YES];
}
Observation: The ClassB is a UIViewController which is wrong. UIViewControllers have viewDidLoad.
Implementation Suggestion:
The correct implementation for the requirement would be that you create a custom cell with a button. Hide the button in awakeFromNib method. in didSelectRowAtIndex set the cell.button.isHidden = YES.
This should alone take care of the requirement mentioned above.
I am calling the block from second class which has been declared and maintained in first class.
In ViewController.h
#property (copy) void (^simpleBlock)(NSString*);
In View Controller.m
- (void)viewDidLoad {
[super viewDidLoad];
self.simpleBlock = ^(NSString *str)
{
NSLog(#"Hello My Name is: %#",str);
};
}
In SecondViewController.m
In ViewDidload
ViewController *VC = [[ViewController alloc]init];
VC.simpleBlock(#"Harjot");//bad execution error
Please suggest me some solutions because the code is giving me bad execution error.
How can i call the block in any another way?
It's the correct way of run the block. However if you try to run a block that is nil you'll have a crash - so you should always check that it's not nil before calling it:
ViewController *vc = [[ViewController alloc] init];
if (vc.simpleClock) {
vc.simpleBlock(#"Harjot");//this will not get called
}
The reason why in your case the block is nil is because you set it in viewDidLoad - however viewDidLoad is not called until its view is ready to go on screen. For testing purposes try to move the assignment from viewDidLoad to init and this should work:
- (instancetype)init
{
self [super init];
if (self) {
_simpleBlock = ^(NSString *str)
{
NSLog(#"Hello My Name is: %#",str);
};
}
return self;
}
Here is my .h file
#import <UIKit/UIKit.h>
#interface PersonViewController : UIViewController
#property(strong,nonatomic) NSString *personTitle;
And here is my .m file
#interface PersonViewController ()
#property (weak, nonatomic) IBOutlet UILabel *titleView;
#end
#implementation PersonViewController
//stuff …
-(void)setPersonTitle:(NSString *)personTitle
{
[self.titleView setText:personTitle];// also self.titleView.text=personTitle
[self.titleView setNeedsDisplay];
NSLog(#"The title shoud match as %# :: %#",personTitle,self.titleView.text);
}
-(NSString *)personTitle
{
return self.titleView.text;
}
//… more stuff
#end
The logging shows that the value is (null) for self.titleView.text whereas personTitle prints the appropriate value.
I remember doing this same thing a number of times and it worked. Any ideas why it’s failing this time?
update I use storyboard to set my scenes. And I am using xcode-5 and iOS-7
update: how I call
The user clicks a button, leading to a push segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"enter prepare for segue.");
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
if ([segue.identifier isEqualToString:the_identifier_for_person]) {
NSLog(#"segue to person is progressing“);
if ([segue.destinationViewController isKindOfClass:[PersonViewController class]]) {
NSLog(#"segue to person destination is a match");
PersonViewController *aPerson = (PersonViewController *)segue.destinationViewController;
aPerson.personTitle=((MyItem*)self.allItems[indexPath.row]).title;
NSLog(#"segue to person is done");
}
}
}
This sounds like you forgot to wire up your UILabel in the storyboard. Can you confirm that self.titleView is not null?
View controllers create their views on demand, but can spot that only via a call to view. When the view is loaded, your outlets will be populated.
Either call view to force loading or keep the string in abeyance until you get viewDidLoad.
(aside: prior to iOS 6, views would also be released in low-memory situations so the idiomatic thing is to store the string and populate on viewDidLoad)
Having accepted another answer, I wanted to show the pattern that I actually used to solve the problem, in case someone else comes looking. This pattern is best practice (yes, I forgot it for a long moment there).
#pragma mark - update UI
-(void)setPersonTitle:(NSString *)personTitle
{
_personTitle=personTitle;
if (self.view.window) [self updateUI];//only if I am on screen; or defer to viewWillAppear
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self updateUI];
}
-(void)updateUI
{
self.titleView.text=self.personTitle;
}
It is always important to update the ui when the data has changed, which is why I must make the call inside setPersonTitle. But because the IBOutlets are not yet set when I set personTitle in prepareForSegue, then I must also make the call inside viewWillAppear.
Do you actually call the -(void)setPersonTitle:(NSString *)personTitle method?
It seems that you aren't calling it correctly which would result in the title being null.
After reviewing the prepareForSeque it is clear that you are not calling the method. You are actually just changing the #property named personTitle.
In the viewDidLoad you should have it so that self.titleView.text = self.personTitle;
In the iOS application, I need to pass an object from one view controller to another and then switch to the new view controller. The goal is to simply relay the information. However, the object becomes NULL and is devoid of any information when the next view showed up. what? could be the reason?
Below is part of my code.
#pragma mark - Table view delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
BIDCourse *course = self.courses[indexPath.row];
NSString *sub = course.subject;
BIDSubjectMainTwoViewController *subjectController=[[BIDSubjectMainTwoViewController alloc] init];
subjectController.title = sub;
subjectController.course=course;
[self.navigationController pushViewController:subjectController animated:YES];
}
BIDCourse *course is my custom subclass to NSObject, which has some NSStrings, and it's not nil, but when I pass it onto the next view controller, it becomes NULL.
I have met a problem like you , when I pass a object to another viewController , it has been released.
I think this is a defect of ARC , ARC think your subjectController may be no use , so it release the subjectController. Make your subjectController as a property , so ARC will not release when you pass it to another viewController.
such as :
#pragma mark - Table view delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
BIDCourse *course = self.courses[indexPath.row];
NSString *sub = course.subject;
_subjectController=[[BIDSubjectMainTwoViewController alloc] init];
_subjectController.title = sub;
_subjectController.course=course;
[self.navigationController pushViewController:_subjectController animated:YES];
}
Could the problem lie in BIDSubjectMainTwoViewController.m?
subjectController.title = sub;
subjectController.course = course;
in your BIDSubjectMainTwoViewController.m file, did you #synthesize your properties?
#synthesize title = _title;
#synthesize course = _course;
You could also pass the variables along with an init function ala:
BIDSubjectMainTwoViewController.h
- (id)initWithTitle:(NSString *)title
andCourse:(BIDCourse *)course;
BIDSubjectMainTwoViewController.m
- (id)initWithTitle:(NSString *)title
andCourse:(BIDCourse *)course
{
self = [super init];
if (self) {
// Custom initialization
_title=title;
_course = course;
}
return self;
}
I encountered this similar behavior. My solution was to make a copy of the object. For your case:
BIDCourse *course = [self.courses[indexPath.row] copy];
And make sure you implement copyWithZone in the BIDCourse .m fike.
-(id)copyWithZone:(NSZone*) zone
Try this:
BIDSubjectMainTwoViewController.h
NSString *strTemp;
#property(nonatomic,retain) NSString *strTemp;
-(void) setValuestrTemp : (NSString *) str;
.m file
#synthesize strTemp;
// Write setter method
-(void) setValuestrTemp : (NSString *) str {
self.strTemp = str;
}
and use strTemp in viewWillAppear.
//Now use it
BIDSubjectMainTwoViewController *subjectController=[[BIDSubjectMainTwoViewController alloc] init];
[subjectController setValuestrTemp:sub];
[self.navigationController pushViewController:_subjectController animated:YES];
Use
NSString *sub = [course.subject copy];
subjectController.course=[course copy];
implement delegate in BIDCourse
-(id)copyWithZone:(NSZone*) zone
I am trying to make a calculator app, but when I press enter nothing is pushed into the array. I have a class called CaculatorBrain where the pushElement method is defined, however (for now) I defined and implemented pushElement method in the view controller.
When I log the operand object as it is typed in the console when enter is pressed the contents of array is nil! Why is that?
#import "CalculatorViewController.h"
#import "CalculatorBrain.h"
#interface CalculatorViewController ()
#property (nonatomic)BOOL userIntheMiddleOfEnteringText;
#property(nonatomic,copy) NSMutableArray* operandStack;
#end
#implementation CalculatorViewController
BOOL userIntheMiddleOfEnteringText;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(NSMutableArray*) operandStack {
if (_operandStack==nil) {
_operandStack=[[NSMutableArray alloc]init];
}
return _operandStack;
}
-(CalculatorBrain*)Brain
{
if (!_Brain) _Brain= [[CalculatorBrain alloc]init];
return _Brain;
}
- (IBAction)digitPressed:(UIButton*)sender {
if (self.userIntheMiddleOfEnteringText) {
NSString *digit= [sender currentTitle];
NSString *currentDisplayText=self.display.text;
NSString *newDisplayText= [currentDisplayText stringByAppendingString:digit];
self.display.text=newDisplayText;
NSLog(#"IAm in digitPressed method");
}
else
{
NSString *digit=[sender currentTitle];
self.display.text = digit;
self. userIntheMiddleOfEnteringText=YES;
}
}
-(void)pushElement:(double)operand {
NSNumber *operandObject=[NSNumber numberWithDouble:operand];
[_operandStack addObject:operandObject];
NSLog(#"operandObject is %#",operandObject);
NSLog(#"array contents is %#",_operandStack);
}
- (IBAction)enterPressed {
[self pushElement: [self.display.text doubleValue] ];
NSLog(#"the contents of array is %#",_operandStack);
userIntheMiddleOfEnteringText= NO;
}
It looks like the operand stack is never initialized.
When you directly access _operandStack you don't go through -(NSMutableArray*) operandStack, which is the only place where the operand stack is allocated and initialized. If the array isn't allocated you can't put anything in it, which is why it logs the contents as nil.
I'd recommend using either self.operandStack (which uses the method that checks if _operandStack is nil) everywhere except inside the -(NSMutableArray*) operandStack method, or allocating the operand stack in your viewDidLoad.