There are two view controllers.One to add items,the other one to display(table view).All items are stored in an NSMutableArray.But every time I unwind to add item and when I go back to the table view, there is only the newest item left.
Code:
- (void)viewDidLoad {
[super viewDidLoad];
[self addData];
}
- (void)addData {
if (!self.items) {
self.items = [[NSMutableArray alloc] init];
}
[self.items addObject:self.textFromFirst];
}
// textFormFirst is an NSString which received from the previous view controller
add view controller
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
UINavigationController *nav = [segue destinationViewController];
SecondController *second = [nav.viewControllers objectAtIndex:0];
second.textFromFirst = [self getText]; // get inputed string
self.aTextField.text = #"";
}
You can share an NSMutableArray between your two view controllers. Just use a property:
MainViewController:
// .m
#interface MasterViewController ()
#property (nonatomic, strong) NSMutableArray *items;
#end
#implementation MasterViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.items = [NSMutableArray array];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"ShowDetail"]) {
DetailViewController *controller = (DetailViewController *)[segue destinationViewController];
controller.items = self.items;
controller.textFromFirst = #"This is a test";
}
}
#end
AddViewController:
// .h
#interface DetailViewController : UIViewController
#property (nonatomic, copy) NSString *textFromFirst;
#property (nonatomic, strong) NSMutableArray *items;
#end
// .m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self addData];
}
- (void)addData {
[self.items addObject:self.textFromFirst];
}
These is a sample project: https://www.dropbox.com/s/ymum4zivgi0z688/TestUnwindSegue.zip?dl=0
I do not know why are you using array in AddViewController. Maybe you are prefer use unwind segue, like so:
// MainViewController.m
- (IBAction)saveItem:(UIStoryboardSegue *)segue {
DetailViewController *detailVC = segue.sourceViewController;
[self.items addObject:detailVC.textFromFirst];
}
In the case, you don't need to share an NSMutableArray to AddViewController.
How are you persisting the NSMutableArray between view controllers? If you need to persist one array across your entire application I would suggest you use a singleton. A singleton is an object that each instance has only 1 memory address, thus is always the same object. You could accomplish this by doing the following:
1) Press CMD+N and select "Cocoa Touch Class" / subclass NSMutableArray
2) Go to that class' .m file and add the singleton in init
static YourNSMutableArray *highlander;
#implementation YourNSMutableArray
- (instancetype)init {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
highlander = [super init];
});
return highlander;
}
3) Now whenever you create a new instance in any of your view controllers like this
if (!self.items) {
self.items = [YourNSMutableArray new];
}
[self.items addObject:self.textFromFirst];
self.items will always have the stored self.textFromFirst because any YourNSMutableArray you create in your app will always be the same object.
Related
I am trying to pass Array from one UIViewController to other viewcontroller.
I tried using property (viewcontroller1.array).
I tried notifications (posting notification and adding observer ).
I tried NSUserDefaults
all are returning a null array.
in ViewController 1 from array is to passed
#interface demopreseViewController () {
NSArray* nameArr;
}
- (void)viewDidLoad {
[super viewDidLoad];
nameArr = [NSArray arrayWithObjects: #"Jill Valentine", #"Peter Griffin",nil];
NSLog(#"%#",nameArr);
}
- (IBAction)PresentAction:(id)sender {
ViewController2 *aviewcontroller = [self.storyboard instantiateViewControllerWithIdentifier:#"aa"];
aviewcontroller.array1 = nameArr;
[self presentViewController:aviewcontroller animated:YES completion:nil];
}
In viewController 2 Where array is to be passed
under ViewController2.h
#property (strong, nonatomic) NSArray *array1;
under ViewController2.m
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"parsed%#",_array1);
}
Array Data will be passed
Define the property for Your Arrays
#Property(nonatomic, retain) NSArray *arrayName;
Secondly
ViewControllerClassName *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"StoryboardIdentifier"];
controller.array = arrayName;
I am very new to IOS programming and I started with a list application. I used the default Master Detail View template in Xcode, and I am trying to edit that to do a to do list or grocery list.
The idea is to be able to tap the + button on the MasterViewController and it seque to the Add screen, input the information there, tap the Save button and it go back to the MasterViewController with the information input in the add screen populating the Table in the MasterViewController. Then if you tap on the table cell that has been added it will seque to the detailViewController and just show the information.
I have spent a lot of hours searching and reading and I am just not getting what to do. I thought this would be an easy application, but I am getting beat! Any tips or help would be appreciated!
I have three view controllers and an Item Class:
the Master controller that is my table of items;
the detail View controller that will just show details of the table row;
and an add view controller, which I am trying to put all the information in to save to my master controller table.
The seque that goes to my Add view controller is called add
The seque that goes to my Detail view controller is called showDetail
I then have the MasterViewController.h :
#import <UIKit/UIKit.h>
#import "Items.h"
#import "AddViewController.h"
#interface MasterViewController : UITableViewController
#property NSMutableArray *items;
#end
MasterViewController.m
#import "MasterViewController.h"
#import "DetailViewController.h"
#import "Items.h"
#interface MasterViewController ()
#property NSMutableArray *itemsarray;
//#property NSMutableArray *stores;
//#property NSMutableArray *prices;
#end
#implementation MasterViewController
- (void)awakeFromNib {
[super awakeFromNib];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;//this is the edit button on the master controller, top left
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Segues
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"add"]) {
}
if ([segue.identifier isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
DetailViewController *destViewController = segue.destinationViewController;
destViewController.item = [_itemsarray objectAtIndex:indexPath.row];
destViewController.quantity = [_itemsarray objectAtIndex:indexPath.row];
destViewController.store = [_itemsarray objectAtIndex:indexPath.row];
}
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.itemsarray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
Items *toDo = _itemsarray[indexPath.row];
cell.textLabel.text = toDo.name;
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.itemsarray removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
#end
DetailViewController.h
#import <UIKit/UIKit.h>
#include "Items.h"
#interface DetailViewController : UIViewController
#property (strong, nonatomic) id detailItem;
#property (weak, nonatomic) IBOutlet UILabel *item;
#property (weak, nonatomic) IBOutlet UILabel *quantity;
#property (weak, nonatomic) IBOutlet UILabel *store;
#end
DetailViewController.h
#import "DetailViewController.h"
#import "Items.h"
#interface DetailViewController ()
#end
#implementation DetailViewController
#pragma mark - Managing the detail item
- (void)setDetailItem:(id)newDetailItem {
if (_detailItem != newDetailItem) {
_detailItem = newDetailItem;
[self configureView];
}
}
- (void)configureView {
// Update the user interface for the detail item.
if (self.detailItem) {
//this is what the text box for name of item will show. it updates
self.item.text= [self.detailItem name] ;
//self.quantity.text= [self.detailItem quantity] ;
//self.store.text= [self.detailItem store] ;
}
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
AddViewController.h
#import <UIKit/UIKit.h>
#include "Items.h"
#import "MasterViewController.h"
#interface AddViewController : UIViewController
#property (strong, nonatomic) id detailItem;
#property (weak, nonatomic) IBOutlet UITextField *item_text_box;
#property (weak, nonatomic) IBOutlet UITextField *quantity_text_box;
#property (weak, nonatomic) IBOutlet UITextField *store_text_box;
#end
AddViewController.m - This is where I am not sure what to do. I am using the save button to call the insertNewObject function, which is where I don't know how to send this information back to the MasterView Controller (at least this is where I think I have the problem, Im very new, so not sure)
#import "AddViewController.h"
#import "MasterViewController.h"
#interface AddViewController ()
#end
#implementation AddViewController
- (void)viewDidLoad {
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:#selector(insertNewObject:)];
//this is the add button at the top right of master controller
self.navigationItem.rightBarButtonItem = saveButton;
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)insertNewObject:(id)sender {
//this is what happens when we press our save button
//this is what happens when the add button is pushed
if (!self.itemsarray) {
self.itemsarray = [[NSMutableArray alloc] init];
}
[self.itemsarray insertObject:[NSDate date] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
#end
Items.h
#import <Foundation/Foundation.h>
#interface Items : NSObject
#property NSString *name;
#property NSString *store;
#property NSString *section;
#property float price;
+ (Items *)createItemWithName:(NSString *)name andPrice:(float)price andStore: (NSString *)store andSection: (NSString*)section;
#end
Items.m
#import "Items.h"
#implementation Items
#synthesize name = _name;
#synthesize store = _store;
#synthesize price = _price;
#synthesize section = _section;
+ (Items *)createItemWithName:(NSString *)name andPrice:(float)price andStore:(NSString *)store andSection:(NSString*)section{
// Initialize Item
Items *item = [[Items alloc] init];
// Configure Item
[item setName:name];
[item setPrice:price];
[item setStore:store];
[item setStore:section];
return item;
}
#end
This is not homework, I have an app idea and wanted to get some of the basics - this app will resemble a part of what I want to do. Thanks!
I suggest creating a class that will act as your data model instead of juggling data in your controller properties. One way of doing this is to create a singleton object and have the controllers talk to it when they want to add or retrieve items.
Using this strategy, a showDetail segue would only need to tell the data model which item number had been selected and the detail controller would call a selectedItem (or some name) method to retrieve it.
Similarly, the add controller would just update the data model and the master controller would get the new information by referencing the model inside its table delegate/datasource methods.
There are ways to do what you're trying to do by using delegates or notifications, but I think having a class/object that's responsible for the application's data is easier to understand and easier to work with when the app becomes more complex.
You can use any one of the following methods
Delegation
Blocks
Notification
Since you are new to iOS,i suggest you to go with delegation. It's easy and simple
Please go though following links
Using Delegation to Communicate With Other View Controllers
Simple Stackoverflow answer
im new to iOS and its developing .there i have used iOS delegates to pass values in between view controllers
1. Essentialinfocontroller - its got TableView
2. detailcontroller -
i want to pass values of Essentialinfocontroller to detailcontroller
to do that i used delegates but nothing print on console please help me.
Essentialinfocontroller.h
#import <UIKit/UIKit.h>
#protocol sendTestData <NSObject>
-(void)sendDataToA:(NSArray *)array;
#end
#interface Essentialinfocontroller : UIViewController<UITableViewDataSource,UITableViewDelegate>
#property (weak, nonatomic) IBOutlet UITableView *info;
#property (nonatomic,strong) NSDictionary * courses;
#property (nonatomic, strong)NSArray *coursekeys;
#property(nonatomic,strong) NSString* customeLink;
#property (nonatomic,retain)NSArray * array;
#property(nonatomic,assign)id delegate;
#end
Essentialinfocontroller.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row==0){
[self giveValuestoNSmutableArray];
[delegate sendDataToA:array];
}
}
-(void)giveValuestoNSmutableArray
{
array = [NSArray arrayWithObjects:#"Jill Valentine", #"Peter Griffin", #"Meg Griffin", #"Jack Lolwut",
#"Mike Roflcoptor", #"Cindy Woods", #"Jessica Windmill", #"Alexander The Great",
#"Sarah Peterson", #"Scott Scottland", #"Geoff Fanta", #"Amanda Pope", #"Michael Meyers",
#"Richard Biggus", #"Montey Python", #"Mike Wut", #"Fake Person", #"Chair",
nil];
}
detailcontroller.h
#import <UIKit/UIKit.h>
#import "Essentialinfocontroller.h"
#interface detailcontroller : UIViewController <sendTestData>
#end
detailcontroller.m
#import "detailcontroller.h"
#interface detailcontroller ()
#end
#implementation detailcontroller
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
Essentialinfocontroller * acontollerobject=[[Essentialinfocontroller alloc] initWithNibName:#"Essentialinfocontroller" bundle:nil];
acontollerobject.delegate=self; // protocol listener
[self.navigationController pushViewController:acontollerobject animated:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)sendDataToA:(NSArray *)array
{
for (NSString *string in array) {
NSLog(#"%#", string);
}
}
#end
You've got the relationship between controllers and delegates a little backwards. A delegate is used to pass information back. Your tableViewController is the detailController's delegate, and detailController shouldn't be allocating an EssentialInfoController inside it. You're basically creating a whole new EssentialInfoController that's different from the first one.
The easiest way to pass data is just to set it when the detailController is allocated. Also, since you're using storyboards, you have to use (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender to allocate the detailController.
Here's what I would do:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row==0){
[self performSegueWithIdentifier:#"OpenDetailsController"];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
DetailController *controller = (DetailController *)[segue destinationViewController];
NSArray *array = <your array here>;
[controller setArray:array];
}
In your storyboard, set the Storyboard Segue Identifier for that segue to "OpenDetailsController".
A delegate function is defined through a protocol, and you would have the DetailController call its delegate like this:
[self.delegate doSomething];
And in the EssentialInfoViewController you have to define the delegate function
-(void)doSomething {
}
i've been trying to implement this protocol for several hours and it doesn't seem to work for some reason. Basically i have a split view which has a view controller and a table controller, one class holds these two together. The main class creates an instance of the table and runs perfectly, but if i select a cell i want the view controller to react. So i wanted to create a protocol for when a table cell is selected it will do something in the main class.
TableSplitViewController, this is the main class:
#interface TableSplitViewController : UIViewController <updateView>
{
ChildrenTableViewController *firstController;
IBOutlet UITableView *firstTable;
IBOutlet UITableViewCell *tablecell;
NSString *name;
}
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) IBOutlet UILabel *childnamelabel;
#end
THis is the TableSplitViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
if (firstController == nil) {
firstController = [[ChildrenTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
}
[firstTable setDataSource:firstController];
[firstTable setDelegate:firstController];
firstController.view = firstController.tableView;
// Do any additional setup after loading the view.
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"ShowChildrenDetails"]) {
ChildrenDetailViewController *detailViewController = [segue destinationViewController];
NSIndexPath *myIndexPath = [firstController.tableView indexPathForSelectedRow];
detailViewController.childrenDetailModel = [[NSArray alloc]
initWithObjects: [firstController.childname objectAtIndex:[firstController.index row]], nil];
}
}
- (void) setNameLabel:(NSString *)sender
{
// self.name = sender;
NSLog(#"ran");
}
This is the ChildrenTableViewController.h:
#protocol updateView <NSObject>
#required
- (void) setNameLabel:(NSString *)sender;
#end
#interface ChildrenTableViewController : UITableViewController
{
NSIndexPath *index;
id <updateView> delegate1;
}
#property (nonatomic, strong) NSMutableArray *childname;
#property (nonatomic, strong) NSIndexPath *index;
#property (retain) id delegate1;
#end
This is the critical part of ChildrenTableViewController.m:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[[self delegate1] setNameLabel:[self.childname objectAtIndex:[indexPath row]]];
NSLog(#"rannn");
As you can see in the last code i'm trying to call the method using the protocol function. It doesn't seem to work for some reason, i've put in NSLOG and it doesn't even run the setNameLabel method at all. :( Will appreciate any help offered :)
In the code above I cant see you setting the delegate as so:
- (void)viewDidLoad
{
[super viewDidLoad];
if (firstController == nil) {
firstController = [[ChildrenTableViewController alloc] initWithStyle:UITableViewStyleGrouped];
}
[firstTable setDataSource:firstController];
[firstTable setDelegate:firstController];
firstController.view = firstController.tableView;
// Set up the delegate for the controller
[firstController setDelegate1:self];
// Do any additional setup after loading the view.
}
Also, the delegate property should usually be (weak) rather than (retain).
I want to load data (an array of strings) from the parent view into a set of UITextFields in the child view upon presenting the modalView.
I know how to pass from child to parent, and I'm sure it's even easier to go the other way, but I don't know how.
UPDATE: Update removed because I found the problem (double releasing of modal view)
Override the init method for the child view controller.
- (id) initWithStrings:(NSArray *)string {
if (self = [super init]) {
// Do stuff....
}
return self;
}
Then in the parent:
MyChildViewController *vc = [[[MyChildViewController alloc] initWithStrings: strings] autorelease];
Two ways you could do it:
1.Override the init method as Matt suggests
2.Create fields in your child class and pass those values to your text field.
#interface ChildViewController : UIViewController{
NSArray *strings;
UITextfield *textField1;
UITextfield *textField2;
}
...
- (void)viewDidLoad {
[super viewDidLoad];
textField1.text = [strings objectAtIndex:0];
textField2.text = [strings objectAtIndex:1];
}
Then in the parent class:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ChildViewController *childController = [[ChildViewController alloc] init];
childController.strings = your_array_of_strings;
[self.navigationController pushViewController:childController animated:YES];
[childController release];
}
- (id)initWithDataObject:(YourDataObjectClass *)dataObject {
if (self = [super init]) {
self.dataObject = dataObject;
// now you can do stuff like: self.myString = self.dataObject.someString;
// you could do stuff like that here or if it is related to view-stuff in viewDidLoad
}
return self;
}
If you want to get really fancy, you can make a delegate for your child view.
#protocol MyChildViewDelegate
- (NSArray*)getStringsForMyChildView:(MyChildView*)childView;
#end
#interface MyChildView : UIView
{
id <MyChildViewDelegate> delegate;
...
}
#property (nonatomic, assign) id <MyChildViewDelegate> delegate;
...
#end
Then somewhere in your view you would ask for the strings:
- (void)viewDidLoad
{
...
NSArray* strings = [delegate getStringsForMyChildView:self];
...
}
Then in your controller (or where ever) you can do:
myChildView = [[MyChildView alloc] initWith....];
myChildView.delegate = self;
...
- (NSArray*)getStringsForMyChildView:(MyChildView*)childView
{
return [NSArray arrayWithObjects:#"one", #"two", #"three", nil];
}
It's probably a little overkill in this case, but this is how UITableViews do it too: they have a data source delegate to provide them with their contents.