i'm not new to OOP but i'm very new to objective c and i've been having trouble with reaching model from two different view controllers, one to set it the other to get the data. Here's my model:
#import <Foundation/Foundation.h>
#interface ModelUnit : NSObject{
NSString * nickname;
int total;
}
- (void)setTotal:(int)newTotal;
- (void)setName:(NSString *)nick;
- (NSString *)getName;
#end
#import "ModelUnit.h"
#implementation ModelUnit
- (void)setTotal:(int)newTotal{
total = newTotal;
}
- (void)setName:(NSString *)nick{
nickname = nick;
}
- (NSString *)getName{
return nickname;
}
#end
Here's how i try to set nickname in the initial viewcontroller:
//.h
#interface introViewController : UIViewController{
ModelUnit * modl;
}
-(ModelUnit *) modl;
-(IBAction)nickEntered:(UITextField *)sender;
#end
//.m
-(ModelUnit *) modl{
if(!modl){
modl = [[ModelUnit alloc] init];
}
return modl;
}
- (IBAction)nickEntered:(UITextField *)sender{
[[self modl] setName:[sender text]];
ViewController *vew = [[ViewController alloc] initWithNibName:nil bundle:nil];
[self presentViewController:vew animated:NO completion:Nil];
}
And here is how i try to receive and display it in the last viewcontroller:
- (void)viewDidLoad
{
[super viewDidLoad];
introViewController *pnt = [[introViewController alloc] initWithNibName:nil bundle:nil];
[display setText:[[pnt modl] getName]];
}
But it always prints it as null when i NSlog it to the console. I know it's a very novice question but i'm completely stuck. Thanks for any help.
It's because each of your UIViewController instances are referencing different instances of your model class (actually, a non-instance in the second case).
Make an #property in your "last" view controller (class name of "ViewController" it would seem), something like:
#property (strong, nonatomic) Model *model;
Then, when pushing your second view controller, set its model property, like this:
ViewController *vew = [[ViewController alloc] initWithNibName:nil bundle:nil];
vew.model = self.modl;
[self presentViewController:few animated:NO completion:NULL];
That way, you're passing around the same instance of your Model class. The way you're doing it, you're trying to access a property that was never initialized, which is why you're getting nil.
If you're not new to OOP, you should quite easily see the mistake you're making. You're trying to retrieve a value out of an object you just initialized.
Where you need to pass the data is in the first object as you initialized the second object.
Between this line:
ViewController *vew = [[ViewController alloc] initWithNibName:nil bundle:nil];
And this line:
[self presentViewController:vew animated:NO completion:Nil];
You're still in the first object and you hold a reference to the second object. Give the ViewController class (a better name and) a NSString property to hold the data the getName method returns. And then set that property between the two lines I posted.
Also, Objective-C doesn't name their getters with get.
Related
Through delegation I created a view controller that takes the input from textfields and passes it back and adds it to an nsmutablearray, successfully adding a row for it. whenever i navigate away from the tableview my newly appended object just disappears. Here is some code to give you a better idea of what i might be doing wrong.
TableViewController.h -
#interface TableViewController : UITableViewController<UITableViewDelegate, UITableViewDataSource>
#property(strong,nonatomic)NSMutableArray *codeList;
#property(strong,nonatomic)NSMutableArray *codeArray;
#end
TableViewController.m -
#interface TableViewController ()
#end
#implementation MCTableViewController
#synthesize codeList;
#synthesize codeArray;
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.codeList = [NSMutableArray arrayWithObjects:#"Array",
#"Pointer",
#"Class",
#"Protocol",
#"Delegate",
nil];
self.codeDescArray = [NSMutableArray arrayWithObjects:
#"Array Description",
#"Pointer Description",
#"Class Description",
#"Protocol Description",
#"Delegate Description",
nil];
)
In the same class I programmatically created a button that moves to the view where data is supposed to be passed back. code below. This is done through delegation.
- (void)addNewCodeButtonPressed {
AddNewCodeVC *addVC = [[AddNewCodeVC alloc] init];
addVC.dataDelegate = self;
UINavigationController *navBar = [[UINavigationController alloc]initWithRootViewController:addVC];
[self.navigationController presentViewController:navBar animated:YES completion:nil];
}
Here is where the delegate logic is created in AddNewCodeVC.h -
#import <UIKit/UIKit.h>
#import "TableViewController.h"
#class AddNewCodeVC;
#protocol addNewCellData <NSObject>
- (void)sendDataToTableView:(NSString*)code codeDesc: (NSString*)desc;
#end
#interface AddNewCodeVC : UIViewController<UITextFieldDelegate> {
__weak id dataDelegate;
}
#property(weak,nonatomic)id<addNewCellData>dataDelegate;
#property(strong,nonatomic)UITextField *codeTextfield;
#property(strong,nonatomic)UITextField *descTextfield;
#end
finally here is the AddNewCodeVC.m -
#import "AddNewCodeVC.h"
#interface AddNewCodeVC ()
#end
#implementation AddNewCodeVC
#synthesize dataDelegate;
- (void)viewDidLoad {
[super viewDidLoad];
self.codeTextfield.delegate = self;
self.descTextfield.delegate = self;
//Programmatically created both textfields, nothing special
}
//"saveNewCode" is action for another button i created
- (void)saveNewCode {
sendDataToTableView:self.codeTextfield.text codeDesc:self.descTextfield.text];
[self.dataDelegate sendDataToTableView:self.codeTextfield.text codeDesc:self.descTextfield.text];
NSLog(#"CODE: %#", self.codeTextfield.text);
NSLog(#"DESC: %#", self.descTextfield.text);
[self dismissViewControllerAnimated:YES completion:nil];
}
The code works but nsmutable array wont hold the passed values if i navigate away from the TableViewController. I think this is because my main view controller programmatically segues to the tableview controller and creates a new instance of it, so that might have some effect? I'll leave some code below just in case it is relevant.
MainviewController.m -
- (void) tableViewBtnPressed:(UIBarButtonItem *)sender {
TableViewController *tableVC = [[TableViewController alloc] init];
//This for another delegate I created, not relevant
tableVC.selectedDataDelegate = self;
UINavigationController *navBar = [[UINavigationController alloc]initWithRootViewController:tableVC];
[self.navigationController presentViewController:navBar animated:YES completion:nil];
}
Hopefully this code is enough to illustrate the problem, i hope someone has an idea whats going on with the NSMutableArray, and why it isn't holding any new objects that are passed into it, any help is appreciated.
I use this code, but "it works" doesn't happen.
DetailViewController.h
[#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#protocol ProtocolNameDelegate
-(void)DoSomething;
#end
#interface DetailViewController : UIViewController {
id<ProtocolNameDelegate> _delegate;
}
#property (strong, nonatomic) id<ProtocolNameDelegate> _delegate;
DetailViewController.m
#synthesize _delegate;
- (void)viewDidLoad
{
[super viewDidLoad];
[_delegate DoSomething];
}
MasterViewController.h
#interface MasterViewController : UITableViewController <ProtocolNameDelegate>
MasterViewController.m
-(void)DoSomething
{
NSLog(#"It works");
}
I think i have to add something like:
MasterViewController* mvc = [[MasterViewController alloc] init];
mvc._delegate = self;
But it gives an error, and by the way will it create another instance of MasterViewController?
Instead of
MasterViewController* mvc = [[MasterViewController alloc] init];
mvc._delegate = self;
write this,
DetailViewController* svc = [[DetailViewController alloc] init];
dvc._delegate = self;
You made mistake in implementation.
Abstract of implementation should be.
Create Protocol in DetailVC.
Create Property for Delegate, Synthesize, and make call.
Import DetailVC in MasterVC and include delegate in MasterVC.h
Implement protocol method in MasterVC.m
Create instance of DetailVC and assign DetailVCObj.delegate = self;
In MasterViewController.m, you need to allocate and intialise DetailViewController somewhere
DetailViewController* dvc = [[DetailViewControlleralloc] init];
dvc._delegate = self;
Also, because you have written [_delegate doSomething] in
DetailviewController's viewDidLoad method,
it means you must set dvc._delegate = self; in MasterViewController.m
before loading dvc's view (before addSubview or anything that loads view).
I have been searching all morning how to do this. I have 2 View Controllers. From the root View Controller (ViewControllerA - which is a table view controller) you can go push to the second view controller (ViewControllerB).
In the ViewControllerB, there are two fields: contacts & textBody. When the user is done they can click on "Add". This will then go back to ViewControllerA. What I am trying to do now, is for every time that process occurs, all the information from ViewControllerB the user just added goes into a cell in ViewControllerA. The user can then add as many cells as they like.
What I can't do however, is get the information across the view controllers. I have been looking all morning at using the app delegate, singletons??, protocols, sharing properties, etc! But I am still stuck.
What I want to do, but can't, is for every time the user clicks "Add" on ViewControllerB, contacts & texts are put into an array. This array is then put into another array which holds all the smaller arrays which the user has created? If you have an ideas, or links to similar/sample code or tutorials, that would be much appreciated!
Try this using the delegate method as follows
Download Sample Project with XIBs
Download Sample Project With Storyboard
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController {
NSMutableArray *dataArray;
}
- (void)passData:(NSMutableArray *)array;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "ChildViewController.h"
#implementation ParentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
dataArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSMutableArray *)array {
[dataArray addObject:array];
NSLog(#"Data Passed = %#",dataArray);
}
#end
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController {
NSMutableArray *tempArray;
}
#property (strong, nonatomic) IBOutlet UITextField *txtContact;
#property (strong, nonatomic) IBOutlet UITextField *txtTextBody;
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
#implementation ChildViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
tempArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)viewDidUnload {
[self setTxtContact:nil];
[self setTxtTextBody:nil];
[super viewDidUnload];
}
#end
With Storyboard
If you are using storyboard then create a ParentViewController segue ChildViewController and give it a identifier in my sample it showChildView
Then use the following code to set the delegate
// Calling the segue to go to the child view and setting up the delegate.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showChildView"]) {
ChildViewController *childVC = segue.destinationViewController;
childVC.delegate = self;
}
}
Then to dismiss back to the ParentViewController use the following code (from my sample)
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self.navigationController popToRootViewControllerAnimated:YES];
}
I would recommend using a singleton instance of your NSMutableDictionary as they have bailed me out of your exact situation multiple times (including custom frameworks and UITabBarControllers). Here is an example I'm currently using to implement a singleton. This methodology is also ARC-safe as well
mySingleton.h
#import <Foundation/Foundation.h>
#interface mySingleton : NSObject {
}
+ (NSMutableDictionary *) myMutableDict;
#end
mySingleton.m
#import "mySingleton.h"
#implementation mySingleton
+ (NSMutableDictionary *)myMutableDict
{
static NSMutableDictionary *singletonInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singletonInstance = [[NSMutableDictionary alloc]init];
});
return singletonInstance;
}
#end
As long as you include mySingleton.h in all of your view controllers you can access the data via [mySingleton myMutableDict]. For example: [[mySingleton myMutableDict] setObject:myObject forKey:myKey];
Good luck!
If the information is really "global" - it has only one instance across the whole app - then you should create a singleton as DB80Buckeye suggested.
If the information is something that truly belongs to ViewController1 and you want it to be modified in ViewController2 (ie ViewController2 is really part of ViewController1, it just happens to be on another screen), then you should pass that as part of the constructor of ViewController2.
-(void)view_controller_1_that_push_view_controller_2_onto_the_stack {
ViewController2* vc2 = [[ViewController2 alloc] initWithInformation:your_information];
[self.navigationController pushViewController:vc2 animated:YES];
}
#interface ViewController2
-(id)initWithInformation:(YourInformationClass*)info;
#end
Another way is to use notifications.
There are two ways to go here. The standard pattern for doing this is delegation. You don't need a singleton. ViewControllerA manages and lists your data. ViewControllerB doesn't need to know anything about all of that data so there's no reason to expose it via a singleton, etc.
Create a delegate protocol in ViewControllerB's header file. Something like this:
#protocol ViewControllerBDelegate
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody;
#end
Now, specify that ViewControllerA will implement the delegate protocol in its header:
#interface ViewControllerA : UIViewController <ViewControllerBDelegate>
Don't forget to import ViewControllerB.h at the top of ViewControllerA's header.
In ViewControllerA's implementation, implement the delegate method you specified in the protocol:
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody {
[self.someArray addObject:[[SomeObject alloc] initWithContact:contact body:textBody]];
[self.tableView reloadData];
}
That's obviously just an example -- not sure how you're managing your data structure and it's probably better to insert the cell someplace that makes sense.
Declare a delegate reference in ViewControllerB's header:
#property (weak, nonatomic) id<ViewControllerBDelegate> delegate;
When you present ViewControllerB, set ViewControllerA as the delegate.
ViewControllerB *b = [[ViewControllerB alloc] init...];
b.delegate = self;
In the selector triggered by the add button in ViewControllerB, call back on the delegate before popping the view controller off the navigation stack:
[self.delegate addContact:contact withBody:text];
where contact and text are the values the user entered.
One could also use a block instead of a delegate but the principle is the same -- have the second view controller only be responsible for taking input, in your case, and pass it back to the view controller managing the data.
Alternatively for delegate suggest using the following:
ViewControllerA.h:
#property (nonatomic, strong) ViewControllerB* viewControllerB;
In ViewControllerA.m
if (!self.viewControllerB)
{
self.viewControllerB = [[ViewControllerB alloc] initWithNibName: #"ViewControllerBr" bundle: nil];
}
[self.navigationController pushViewController: self.viewControllerB
animated: YES];
...
- (void) viewWillAppear: (BOOL) animated
if (self.viewControllerB)
{
NSString* contact = self.viewControllerB.contact;
NSLog(#"%#", contact);
}
...
I'm trying to accomplish something incredibly simple. I am trying to programmatically push to a viewController when the only item in my collection view is pushed. Nothing happens. I believe there is more than one problem in my tangled mess. My understanding of the basics of arrays is clearly anything but. If I put an NSLog line inside my if statement below, I get nothing when pushing my lone item. Here is my didSelectItemAtIndexPath method:
NSMutableArray *itemApp = [model.viewControllers objectAtIndex:indexPath.row];
if (itemApp == 0) {
NSLog (#"This does not appear")
TableViewController *ctc = [[TableViewController alloc] initWithStyle:UITableViewStylePlain];
[self.navigationController pushViewController:ctc animated:YES];
}
model is defined in viewDidLoad:
model = [[SimpleModel alloc] init];
SimpleModel is mentioned in the .m implementation:
#implementation smileController;
{
SimpleModel *model;
}
viewControllers is property of the SimpleModel class, along with its friend, apps:
#property (nonatomic, strong) NSMutableArray *apps;
#property (nonatomic, strong) NSMutableArray *viewControllers;
Here is the SimpleModel.m
- (id)init
{
if (self = [super init])
{
self.apps = [NSMutableArray arrayWithObjects:#"1", nil];
self.viewControllers = [NSMutableArray arrayWithCapacity:self.apps.count];
TableViewController *tvc = [[TableViewController alloc] init];
[self.viewControllers addObject:tvc];
}
return self;
}
In SimpleModel.m you populate the viewControllers array with a single TableViewController.
Given this, when the first block of code you posted should be more like this:
TableViewController *itemApp = [model.viewControllers objectAtIndex:indexPath.row];
if (itemApp) {
NSLog (#"This should appear")
[self.navigationController pushViewController:itemApp animated:YES];
}
This assumes you want to push the view controller you obtain from the model.viewControllers property.
Please note that itemApp can only be nil if model or model.viewControllers are nil.
My problem is quite simple, but as a beginner, I'm lost :D
I have to view controller :
view one call view 2 like this :
self.FacebookTypeRequest =#"favoris";
FaceBookViewController *viewcontrol = [[FaceBookViewController alloc]initWithNibName:#"FaceBookViewController" bundle:[NSBundle mainBundle]];
viewcontrol.title = #"FaceBook";
[self.navigationController pushViewController:viewcontrol animated:YES];
[viewcontrol release];
How can I send my string facebookTypeRequest to my view controller 2 ?
Make a property on your second view controller (FaceBookViewController) like this in the .h file :
#interface FaceBookViewController {
...
NSString *facebookTypeRequest;
...
}
#property (nonatomic, copy) NSString *facebookTypeRequest;
and in the .m file put
#implementation FaceBookViewController
#synthesize facebookTypeReqeust;
and remember to put in your dealloc method
- (void) dealloc {
[facebookTypeRequest release];
// release other properties here as well
[super dealloc];
}
Then, you can just set it like this :
self.FacebookTypeRequest = #"favoris";
FaceBookViewController *viewcontrol = [[FaceBookViewController alloc] initWithNibName:#"FaceBookViewController" bundle:nil];
viewcontrol.title = #"FaceBook";
viewcontrol.facebookTypeRequest = self.FacebookTypeRequest; //!< This is the line :)
[self.navigationController pushViewController:viewcontrol animated:YES];
[viewcontrol release];
Now, inside your FaceBookViewController you have the facebookTypeRequest.
Hope that helps.
NB It's generally bad practice to use captial letters to start the name of a property i.e. self.FaceboookTypeReqeust should really be self.facebookTypeRequest.