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
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.
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
}
Firstly, I've already tried to search for solutions online but none works for me and I'm thinking since I'm using ECSlidingViewController to navigate around the app, I can't utilise the prepareForSegue method thus, my problem may need a different approach.
I have a class called viewInits which holds properties in the .h file that I want allow other classes to set and get it's values. In this case, the property is an NSString *navBarTitle.
In ClassA, I have a tableView:didSelectRowAtIndexPath: method, where I
Create an ViewInits class object - *viewInits.
I then set the setNavBarTitle: to the value of [self.MenuRowsArray objectAtIndex:indexPath.row].
In the next line, I did an NSLog to check and yes, viewInits.navBarTitle now holds the value I desire.
In ClassB's viewDidloadMethod, similarly, I created a ViewInits object - *viewInits and did an NSLog check for viewInits.navBarTitle. But it returns (null). What seems to be the problem here?
Here is the code for how I'm trying to pass the NSString. What am I doing wrong?
viewInit .h
#interface ViewInits : NSObject
#property (strong, nonatomic) NSString *navBarTitle;
#end
ClassA.m tableView:didSelectRowAtIndexPath: method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *identifier = [self.MenuRowsArray objectAtIndex:indexPath.row];
UIViewController *newTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
// *---------- Assign identifier to NSString viewInits ----------*
ViewInits *viewInits = [[ViewInits alloc] init];
[viewInits setNavBarTitle:identifier];
NSLog(#"%#", viewInits.navBarTitle);
// *---------- Assign identifier to NSString viewInits ----------*
[self.slidingViewController anchorTopViewOffScreenTo:ECRight animations:nil onComplete:^
{
CGRect frame = self.slidingViewController.topViewController.view.frame;
self.slidingViewController.topViewController = newTopViewController;
self.slidingViewController.topViewController.view.frame = frame;
[self.slidingViewController resetTopView];
}];
}
ClassB.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// *========== ECSlidingViewController ==========*
self.view.layer.shadowOpacity = 0.75f;
self.view.layer.shadowRadius = 10.0f;
self.view.layer.shadowColor = [UIColor blackColor].CGColor;
if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]])
{
self.slidingViewController.underLeftViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Menu"];
}
[self.view addGestureRecognizer:self.slidingViewController.panGesture];
// *========== ECSlidingViewController ==========*
ViewInits *viewInits = [[ViewInits alloc] init]; // Create ViewInit class object
self.navBar.topItem.title = viewInits.navBarTitle;
NSLog(#"%#", viewInits.navBarTitle); // <<--- This always ends up null. What's wrong?
}
Your help are much appreciated. Thank you.
If you want to use ViewInit as a common store of settings it should be a singleton so that all other instances in the app can get it. Currently you're creating a new instance each time you want to use it, so the new instance doesn't have any of your previous settings.
Aside, I know what the sliding view controller is, I ask about it because you may be using it incorrectly. If you have a view controller which is the current top view controller and it changes the top view controller (class A might be doing this, not sure) then the reference self.slidingViewController will stop working part way through your code.
Having some difficulty getting my delegate to "work".
It works when the ClassA pushes ClassB onto the stack via UINavigationController, but ClassB does not call the delegate method (in ClassA) when ClassB is pushed by a different ClassC.
This method from within ClassA works:
- (void)openViewController:(NSString *)title:(int)dataType:(NSArray *)currentData {
ClassB *nextController = [[ClassB alloc] initWithNibName:#"ClassBView" bundle:nil];
nextController.delegate = self;
nextController.title = title;
nextController.dataType = dataType;
nextController.currentData = currentData;
nextController.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:nextController animated:YES];}
How do I get this method, from ClassC, to properly designate ClassA as the delegate for ClassB???
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ClassB *nextController = [[ClassB alloc] initWithNibName:#"ClassBView" bundle:nil];
nextController.delegate = ??????????
nextController.title = [self.tableData objectAtIndex:indexPath.row];
nextController.dataType = self.dataType;
nextController.currentData = [NSArray arrayWithArray:[self.dictionary objectForKey:[self.tableData objectAtIndex:indexPath.row]]];
nextController.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:nextController animated:YES];}
Appreciate any assistance. Cheers.
I feel like knowing more about the view controller hierarchy in the app would help, but it sounds like you'll need to add a reference to ClassA in ClassC that it could pass on to ClassB. So (in ClassC) nextController.delegate = self.ClassARef
I'm implementing a uinavigationcontroller. The first view is a uitableview (imagine the Contacts app) with a list of names.
The second view is the person profile.
So, when I click a person in the uitable, it's suppose to load his profile.
How do I pass the person data to the second view?
In didSelectRowAtIndexPath I do:
ContactView * varContactView = [[ContactView alloc] initWithNibName:nil bundle:nil];
varContactView.title = [[contactsArray objectAtIndex:indexPath.row] name];
[varContactView initWithPerson:[contactsArray objectAtIndex:indexPath.row]];
[navigationController pushViewController:varContactView animated:YES];
In the interface of ContactView I've got:
Person * person;
And then:
#property (nonatomic, retain) Person * person;
-(void) initWithPerson:(Person *)newperson;
And in .m:
#synthesize person
-(void) initWithPerson:(Person *)newperson{
person = [[Person alloc] init];
person = newperson;
}
However, when I try to access person in ContactView, it says always EXC_BAD_ACCESS.
What is wrong here?
Instead of:
[varContactView initWithPerson:[contactsArray objectAtIndex:indexPath.row]];
you can simply use:
varContactView.person = [contactsArray objectAtIndex:indexPath.row];
That will make use of person property setter and assign the given object as varContactView's data. The default implementation of that setter is (in case of retain property):
- (void)setPerson:(Person *)newPerson
{
if (person != newPerson) {
[person release];
person = [newPerson retain];
}
}
That's what you're trying to achieve in -initWithPerson: method. That method is not needed, as its functionality is covered by person property setter. BTW, remember to release person property in -dealloc method of your view controller:
- (void)dealloc
{
[person release];
[super dealloc];
}
The bad access exception might be caused by something else in your code, though...
In .m file, change the code as given below.
-(void) initWithPerson:(Person *)newperson
{
self.person = newperson;
}