How to add textfield data to cells - objective-c

I have an array which I'm using to populate 2 cells with text for now. In a different view I have a textfield, a textview etc..
How can I move the data input by user in the textfield into an array in a different controller.
here's what I have:
- (void)viewDidLoad
{
tabledata = [[NSArray alloc] initWithObjects:#"Franklin", #"delossantos", nil];
[super viewDidLoad];
}
Here is the view controller of the textfields:
NewEntryController.m
#import "NewEntryViewController.h"
#implementation NewEntryViewController
#synthesize titleTextfield;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
#pragma mark - View lifecycle
- (void)viewDidUnload
{
[self setTitleTextfield:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
#end

I guess there are two options:
Separate your data in another NSObject class and make it a shared data model for both of your view controllers. Write to it when user inputs something.
(Really bad option) Keep everything as is, make sure the array in the first controller is publicly accessible and simply get the data from another controller.
Also, you declare an immutable array, you won't be able to change it. You will have to either recreate or declare it as mutable.
More on the first option
In order to create a shared model, you have to create a separate class.
Add some property to store data there, e.g.
#property (strong, nonatomic) NSMutableArray *userStrings;
Add #synthesize and initialise it in the init method of your new class.
Create an instance of it and assign it to some property in both view controllers, say, to:
#property (strong, nonatomic) NSObject *dataModel;
After you assign it, you will be able to access it from inside your view controllers. There, when your user inputs something, you will need to update contents of your dataModel by adding, changing or removing elements in the model's array. It will be done in some similar way:
[self.dataModel setUserStrings:[NSMutableArray arrayWithObjects: [self.textField text], [self.textView text], nil]];
// or
[[self.dataModel userStrings] addObject:[self.textField text]];
This way you can have a shared datasource.
Possible easier solution
It may be the case that you don't need the model to be shared simultaneously between two controllers, and maybe you open second one only after user has finished inputing and you create it from your first controller.
In that case you don't have to create and share the model, you can just create an array based on user input and pass it in your second controller. You would create that second controller and set it's property tableData. When your next controller appears it'd just read from that property and show the data.
If you need more info on such approaches, I'd suggest you watch some video tutorials on using MVC approach in Objective-C. For example, Stanford's course on iOS Programming is awesome. It is available for free in the iTunes U.

Related

Loading an array on a sub view with data from main view

My application has two views. Main view allows to enter details of a product and stores to an array in the second view. Whenever I switches to second view, all product details should be loaded to a table view.
The problem is that whenever I try to add a product to the array, it saves data but replaces previous data. So all the time array is stored with only one product info. I assume the array is getting initialised every time when I add a product. Here is the code,
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
productArray =[[NSMutableArray alloc] init];
}
return self;
}
I tried to allocate array in viewDidLoad method and many other places but nothing helped.
Please help.
Thanks
I'm not exactly sure if I understand your question, but:
If you want to add an object to an array, you should set the array as NSMutableArray and call the addObject method on it so that the array will now contain your new object.
If you want to pass the array from your main view to your second view, you should do it in your segue setup. Basically you have a #property in your second view, and in the performSegue method in your main view, you set the #property to whatever array you wish it to be.
You cannot use segue without storyboard (at least i don't know how). So if you want to use xib, you need to do it in the traditional way:
SomeViewController *viewController = [[SomeViewController alloc] initWithNibName:#"nib" bundle:nil];;
viewController.array = self.array; // the array you want to pass to your new view
So in your alloc-init thing, you only instantiated your array, but you didn't seem to pass the data to it?

Changes not reflected across view when using binding in cocoa

I am creating some sample applications to understand the concepts of view navigation, binding etc in cocoa.
Here is the scenario:
I have a window that has a tab view(2 tabs) in MainMenu.Xib.
I have a text field in the first tab and label in the second tab. I want both of them to reflect the same value and I want to do this using binding. Also, I don't want to use the views provided to me along with the tab view.
These are the steps I have done.
The view of each tab view item is set separately in the applicationDidFinishLaunching: method using the following code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
//initialize view controllers
view1=[[ViewTab1 alloc] initWithNibName:#"ViewTab1" bundle:nil];
view2=[[ViewTab2 alloc] initWithNibName:#"ViewTab2" bundle:nil];
//set views
[[[myTabView tabViewItems] objectAtIndex:0]setView:view1.view];
[[[myTabView tabViewItems] objectAtIndex:1]setView:view2.view];
}
myTabView is the outlet reference of the tab view from MainMenu.xib in AppDelegate.
ViewTab1 is the name of the first view controller (and the xib).
ViewTab2 is the name of the second view controller (and the xib).
ViewTab1 has one single text field (and an associated label). I have bound this to a variable(name) declared in AppDelegate.
ViewTab2 has a label. I have bound this also to the same variable in AppDelegate.
The variable, 'name' is initialized in the init method of AppDelegate.
AppDelegate.h
....
NSString *name;
....
#property(strong) ViewTab1 *view1;
#property(strong) ViewTab2 *view2;
#property (assign) IBOutlet NSTabView *myTabView;
#property (strong) NSString *name;
....
AppDelegate.m
....
#synthesize myTabView;
#synthesize view1,view2;
#synthesize name;
....
- (id)init {
self = [super init];
if (self) {
name=#"dummy";
}
return self;
....
Apart from this I haven't done any coding in my program.
In the ViewTab1.xib I got an object and made it an instance of AppDelegate and then connected the delegate reference of the Application object(NSApplication) to the same object. (I hope this is the right way of getting the AppDelegate object.)
I did the same in ViewTab2.xib
Then I bound the text field in ViewTab1 and label in ViewTab2 to this variable in AppDelegate.
When I run the program both the text field and label shows "dummy". But when I change the value in the text field, its not reflected in the label in the second tab( i.e. ViewTab2).
Please tell me what I'm doing wrong.
How to establish binding to the same App delegate object from any loaded Nib?
Yes, I know this frustrated situation as described in question... after many weeks and hundreds pages of documentation for KVO - Notifications - Bindings I think there is one very simple solution for that.
As we can find in some information sources the nib-loading process produce new instances of members... and we need to use binding connection to the old one.
Note that bindings made in InterfaceBuilder are redirect to these new instances automatically after loading nib
Why not redirect the pointer of App delegate to the old instance?
In method where you loads your nib you can test which object is app delegate before and just after nib load.
If the new one isn’t the same as the previous one you can redirect it as you want.
This simple example works for me in Xcode3 under 10.5.8 with target to OSX10.5 / i386:
// ***** SOMEWHERE IN DEFAULT APP-DELEGATE.m IMPLEMENTATION
- (IBAction) createOtherWindowFromNib: (id)sender
{
// ensure that app delegate is set as you want...
[NSApp setDelegate:self];
NSLog(#"APP-DELEGAT **** CREATE-TEST-WINDOW ***** WHO IS APP-DELEGATE BEFORE NIB LOAD: %# ", [[NSApp delegate] description]);
// we can bind members of the nib to this controller over proxy object named "File’s Owner"
NSWindowController *otherWinCapo = [[NSWindowController alloc] initWithWindowNibName: #"OtherTestWindow"];
NSLog(#"APP-DELEGAT **** CREATE-TEST-WINDOW ***** WHO IS APP-DELEGATE AFTER NIB LOAD: %# ", [[NSApp delegate] description]);
// make some test for delegates before/after here if you need ...
// usually your bindings made inside "OtherTestWindow.xib" by IB doesn’t works in this moment
// ... and some redirection if needed
[NSApp setDelegate:self];
// afer that the bind made in IB inside "OtherTestWindow.xib"
// referred to (proxy object) "Application.delegate.myBOOL" (Bind to:Application, Model Key Path:delegate.myBOOL)
// react to changes of myBOOL placed in default app delegate object as expected
// simultaneously in every open instance of "OtherTestWindow.xib"
[otherWinCapo showWindow: otherWinCapo.window]; // we need populate the window instance on screen to see it
}
I think the problem is that the objects in your xibs that you set to the app delegate class create 2 different instances of the app delegate, so changing the value of the text field changes the value of name in one instance but not in the other. That's what you're doing wrong, unfortunately, I can't think of a solution at this time.
Have you turned on 'Continuously Updates Value' in the NSTextField controls?
See this example.

Is it possible to pass data as part of NSNotifications?

Scenario, in context of a card game:
User moves a card on the screen. As a result of a move, coordinates of the card change. If card is found to be in some specific location, we'd like to make sure Card object (Model) is updated to include those coordinates.
But View should not be talking to the Model directly .., so
Instead of updating Card directly, View is going to notify its Controller that "Card has landed". Upon receiving this notification, i'd like for a Controller to update Card's location instead of the View (Controller updates Model)
Question 1:
Am i thinking about this type of scenario correctly?
Question 2:
Is it possible to send data to a controller along with a notification?
You do not need NSNotifications for your scenario: a straightforward delegate-based approach should do.
The view should define a delegate interface, and provide a non-retaining delegate property. The controller should implement the delegate interface, and set itself as view's delegate. The view would then notify its delegate without even knowing that it notifies the controller. The controller would then pass the notification along to the model.
#protocol CardDelegate
-(void)cardHasLanded:(SOCard*)card atPosition:(SOPosition*)pos;
#end
#interface MyView
#property (weak, nonatomic,readwrite) id<CardDelegate> delegate;
#end
#implementation MyViewController
-(id)init { // This should be in your designated initializer
self = [super init];
if (self) {
MyView *view = [[MyView alloc] init];
view.delegate = self;
self.view = view;
}
return self;
}
-(void)cardHasLanded:(SOCard*)card atPosition:(SOPosition*)pos {
// Update the model
}
#end
#implementation MyView
#synthesize delegate;
-(void) doSomething {
// ...
if (cardHasLanded) {
[delegate cardHasLanded:card atPosition:pos];
}
// ... more code
}
#end
That's what the userInfo dictionary and object of NSNotification are used for. In this case, it's the object you want. For example:
// In your model header file
extern NSString * const CardMovedNotification;
// In your model implementation file
NSString * const CardMovedNotification = #"CardMoved";
...
[[NSNotificationCenter defaultCenter] postNotificationName:CardMovedNotification object:theCardThatMoved];
Your obversers then can get the card via [notification object]. If you need to pass more information, you'd create a dictionary and pass that as userInfo, via postNotificationName:object:userInfo:. The observer then can query it via [notification userInfo].
I agree that no notifications are needed. Usually for UI object coordinates I won't use a model object because the coordinates are coupled to the UI object (or else where will you draw it ?). What you do need to do is register your controller to the touch end event passing in the UI object where the touch was made and updating (in the responding controller method) the object's coordinates if they indeed within the desired location. (as done with buttons and other UI objects)

How to present a view controller from another view controller

I am trying to open a ViewController from within another ViewController if certain conditions are met. The code seems to run without error but the view is never shown. I am new to xcode 4 /ios 5 so I must be missing something.
Here is the code responsible for opening the second viewcontroller:
CreateUserViewController *createUserController = [[CreateUserViewController alloc] initWithNibName:#"CreateUserView" bundle:[NSBundle mainBundle] keyWrapper:keyChainWrapper];
[self presentViewController:createUserController animated:YES completion:nil];
In my project I have a xib called, "CreateUserView". I have added a view controller to this xib and assigned it to, "CreateUserViewController".
Also I noticed in the apple documentation that is shows setting the delegate of the viewcontroller to be presented. But it seems that no property called, "delegate" is on the viewcontroller object. Is this documentation old? This is the document I am trying to use (section 9-1):
View Controller Programming
Can someone give me a hint? Thanks..
edit Adding Custom Constructor
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil keyWrapper:(KeychainItemWrapper *)keyWrapper
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if(self){
[self setKeyChainWrapper:keyWrapper];
}
return self;
}
Regarding CreateUserView.xib: you don't want to put a CreateUserViewController object in the nib. You want to set the custom class of the File's Owner placeholder to CreateUserViewController. Then you need to connect the view outlet of File's Owner to the top-level view in the nib.
Regarding the delegate property: The UIViewController class doesn't have its own delegate property. The idea is that you add a delegate property to your subclass of UIViewController. The delegate provides a way for your presented view controller to pass custom information back to the presenting view controller.
Why would you want to do that? Let's consider the code you posted. I'll assume you have a UserListViewController that shows a list of User objects, and has a "Create new user" button. When the user touches the "Create new user" button, you create a CreateUserViewController and present it.
The user interacts with the CreateUserViewController to set the attributes of the new User object - name, rank, hairstyle, etc. Then he touches a "Done" button. Your CreateUserViewController creates the new User object and puts it in the database. Then it needs to dismiss itself, so the UserListViewController's list of User objects will appear again.
But you want the User list to include the newly created User object and you want to scroll the list so that the new User is on the screen. So you need a way to have your CreateUserViewController tell the UserListViewController about the newly created User object. This is where the delegate comes in.
You define a protocol like this:
#protocol CreateUserViewControllerDelegate
- (void)didCreateUser:(User *)user;
#end
and you give your CreateUserViewController a delegate property:
#interface CreateUserViewController
#property (weak, nonatomic) id<CreateUserViewControllerDelegate> delegate;
// ...
When your CreateUserViewController's "Done" button is touched, you notify your delegate of the new User:
- (IBAction)doneButtonWasTouched:(id)sender {
User *user = [self createUser];
[self.delegate didCreateUser:user];
[self dismissViewControllerAnimated:YES completion:nil];
}
In your UserListViewController, you adopt and implement the protocol:
#interface UserListViewController <CreateUserViewControllerDelegate, UITableViewDelegate, UITableViewDataSource>
// ...
#end
#implementation UserListViewController
- (void)didCreateUser:(User *)user {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.users count] inSection:0];
[self.users addObject:user];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation: UITableViewRowAnimationAutomatic];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition: UITableViewScrollPositionNone animated:YES];
}
and when you need to present a CreateUserViewController, you set the new controller's delegate to the UserListViewController:
- (IBAction)createUserButtonWasTouched:(id)sender {
CreateUserViewController *createUserController = [[CreateUserViewController alloc] initWithNibName:#"CreateUserView" bundle:[NSBundle mainBundle] keyWrapper:keyChainWrapper];
createUserController.delegate = self;
[self presentViewController:createUserController animated:YES completion:nil];
}
In iOS5 the method for pushing new view controllers was really changed around quite a bit from iOS4 and Xcode 3. In summary, storyboards are now used to create your application view controller flow. Even though you may use standalone .xib files to build an application it is much less common in iOS5.
Anyway, the main method for pushing new view controllers onto the screen is done using segues. Check out this tutorial for an introduction: http://www.raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-1
It does a good job on explaining how to create a storyboard and use segues. You can still present view controllers in code "the old way" but it is much much less common now with the introduction of these new technologies. There are also some absolutely awesome tutorials on iTunes U - search for CS193P. It's the Stanford Introductory class to Objective-C and programming for iOS. This should get you started and maybe help you think of a way to push your createUserController in a way more up to speed with iOS5.
UPDATE
I just wanted to add. If you configure your program to use storyboards and segues you can use the method performSegueWithIdentifier:sender: to perform the segue to your createUserController view if the proper conditions are met. See the Apple API for UIViewController for information on how to use this method.

Managing multiple view controllers and data

My app has a main screen that the user always starts at, and from which I want to display other views programmatically. I set up the app identically to the approach in "Beginning iPhone Development" Ch. 6, which is to use a RootViewController that loads in other view controllers.
The book uses a button to trigger loading the next view controller, but in my app I need to swap controllers at the end of function calls and to share data (processed UIImages, etc) between views. I am not using a tab bar or navigation controller.
What I'm wondering is, should I just make my MainViewController the root controller and presentModalViewControllers from there? I'd like to keep the root model but I don't quite understand how to hook it all up and share data. I've seen posts that mention using protocols and notifications but I haven't wrapped my head around it yet. Any advice is appreciated.
What you want to do is add a Cocoa property in your main view controller that references the object instances which you want to share with subordinate view controllers.
For example, if we want to share an NSArray, we specify its property in the main view controller header:
#interface MainViewController : UIViewController {
NSArray *myArray;
}
#property (nonatomic, retain) NSArray *myArray;
#end
In the implementation, add the #synthesize directive and remember to release the array in -dealloc:
#implementation MainViewController
#synthesize myArray;
...
- (void) dealloc {
[myArray release];
[super dealloc];
}
#end
You also want to add this property to view controllers that are subordinate to the main view controller, in the exact same way. In their headers, specify the same variable name and property description.
In your main view controller, when you are ready to push a subordinate view controller, you set the subordinate view controller's property accordingly just before pushing:
- (void) pushSubordinateViewController {
SubordinateViewController *subVC = [[SubordinateViewController alloc] initWithNibName:#"SubordinateViewController" bundle:nil];
subVC.myArray = self.myArray; // this sets the sub view controller's myArray property
[self.navigationController pushViewController:subVC animated:YES];
[subVC release];
}
Likewise in your subordinate view controller, it will need to set its subordinate's array property accordingly, just before it pushes its own sub-sub-view controller.
By setting references in this way, each view controller is pointed at the same array, containing the desired elements.
To use the array, just call self.myArray, e.g. [self.myArray objectAtIndex:index] to get an object at a given index.