Objective-C NSMutableArray only returning one item - ios

I am trying to print out a list of objects in my NSMutableArray via NSLog, but for some reason, it is appearing to be null. Basically, I have a to-do list that when the user enters a new string to add to the tableview, it will also add that item to the NSArray so I can save it to the device.
AddToDoItemViewController.m
#import "AddToDoItemViewController.h"
#import "ToDoItem.h"
#interface AddToDoItemViewController ()
#property (weak, nonatomic) IBOutlet UITextField *textField;
#property (weak, nonatomic) IBOutlet UIBarButtonItem *saveButton;
#end
#implementation AddToDoItemViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.toDoItem.itemList = [[NSMutableArray alloc] init];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#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.
if (sender != self.saveButton) return;
if (self.textField.text.length > 0) {
self.toDoItem = [[ToDoItem alloc] init];
self.toDoItem.itemName = self.textField.text;
self.toDoItem.completed = NO;
NSLog(#"Trying to add to array: %#", self.toDoItem.itemName);
[self.toDoItem.itemList addObject:self.toDoItem.itemName];
NSLog(#"Array contents: %#", self.toDoItem.itemList);
}
}
#end
AddToDoItemViewController.h
#import <UIKit/UIKit.h>
#import "ToDoItem.h"
#interface AddToDoItemViewController : UIViewController
#property ToDoItem *toDoItem;
#end
ToDoItem.h
#import <Foundation/Foundation.h>
#interface ToDoItem : NSObject
#property NSString *itemName;
#property BOOL completed;
#property (readonly) NSDate *creationDate;
#property NSMutableArray *itemList;
#end
Now from my AddToDoItem.m file, when I use NSLog to try to output the Array I get this:
2016-02-24 01:04:49.668 ToDoList[4025:249117] Trying to add to array: ok
2016-02-24 01:04:49.669 ToDoList[4025:249117] Array contents: (null)
**** The 'ok' was the text I entered *****

You did not initialise the array before adding to it, add self.toDoItem.itemList = [NSMutableArray new];
Edit:
oh i see you added self.toDoItem.itemList = [[NSMutableArray alloc] init]; in the viewDidLoad, but this is not the right place to put it, it should be after self.toDoItem = [[ToDoItem alloc] init]; or inside the init method of ToDoItem

First, you need create an init method at ToDoItem.m
- (id)init {
self = [super init];
if (self) {
// Any custom setup work goes here
self.itemList = [[NSMutableArray alloc] init];
}
return self;
}
then run your project again.

You are initialising the Array( itemList ) before ToDoItem is initialised, so initialised array remains nil. So, it cannot store any object.
Modify code as below,
self.toDoItem = [[ToDoItem alloc] init];
self.toDoItem.itemList = [[NSMutableArray alloc] init];
you can add above lines of code either at viewDidLoad or at Segue Method
hope it helps you.

Related

How to keep objects in NSMutableArray?

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.

Unwind segue "Property of 'BusStopItem' not found on type 'AddBusStopViewController'"

this question is based on the apple x-code tutorial here.
I am having an error when I call my unwindBusList function which looks at the source of the segue. I have tested it with these lines commented out and everything else seems to run fine other than the BusStopItem not being added.
Property of BusStopItem' not found on type
'AddBusStopViewController'
on this line:
BusStopItem *item = source.busStopItem;
YourBusStopsTableViewController.m
#import "YourBusStopsTableViewController.h"
#import "BusStopItem.h"
#import "AddBusStopViewController.h"
#interface YourBusStopsTableViewController ()
#property NSMutableArray *busStopItems;
- (IBAction)unwindBusList:(UIStoryboardSegue *)segue;
#end
#implementation YourBusStopsTableViewController
- (IBAction)unwindBusList:(UIStoryboardSegue *)segue {
AddBusStopViewController *source = segue.sourceViewController;
BusStopItem *item = source.busStopItem;
if (item != nil) {
[self.busStopItems addObject:item];
[self.tableView reloadData];
}
}
AddBusStopViewController.h
#import <UIKit/UIKit.h>
#import "BusStopItem.h"
#interface AddBusStopViewController : UIViewController
#property BusStopItem *busStopItem;
#end
AddBusStopViewController.m
#import "AddBusStopViewController.h"
#interface AddBusStopViewController ()
#property (weak, nonatomic) IBOutlet UIBarButtonItem *saveButton;
#property (weak, nonatomic) IBOutlet UITextField *stopNumField;
#property (weak, nonatomic) IBOutlet UITextField *nameField;
#end
#implementation AddBusStopViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#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.
if (sender != self.saveButton) return;
if (self.nameField.text.length > 0 && self.stopNumField.text.length > 0) {
self.busStopItem = [[BusStopItem alloc] init];
self.busStopItem.itemName = self.nameField.text;
self.busStopItem.stopNum = [self.stopNumField.text intValue];
self.busStopItem.fetching = NO;
}
}
#end
BusStopItem.h
#import <Foundation/Foundation.h>
#interface BusStopItem : NSObject
#property NSString *itemName;
#property NSInteger stopNum;
#property BOOL fetching;
#end
Any and all feedback is appreciated, this has been bugging me for hours, and nothing has solved my problem.
Thanks in Advance.
The problem was that the objective-c files I created for the views were not in the appropriate directory. Although they appeared in xcode to be in the correct location (and actually were), these versions weren't being updated as I was saving. I replaced the files that were not being written to with the appropriate ones and everything works.

How do i add multiple objects to NSMutableArray and how do i retrieve my objects properties?

Super basic questions, which i'm having problems with.
How do i add multiple objects to my NSMutableArray? (Now i only add one with self.itemsArray[0] = iPhoneItem; )
How do i retrieve for the first objects property (itemName)?
I have a calss: Item - which looks like follows.
Item.h
#interface Item : UITableViewController
#property (nonatomic, copy) NSString *itemTitle;
- (id)initWithItemTitle:(NSString *)aTitle;
#end
Item.m
#interface Item ()
#end
#implementation Item
- (id)initWithTitle:(NSString *)aTitle {
self = [super init];
if (self) {
self.itemTitle = aTitle;
}
return self;
}
#end
And now i just want to create a few objects, add them in to an NSMutableArray and retrieve the itemTitle property.
ViewController.m - (.h has no additional changes from standard "create singel view application"
#import "ViewController.h"
#import "Item.h"
#interface ViewController ()
#property (nonatomic, strong) NSMutableArray *itemsArray;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Item *iPhoneItem = [[Item alloc] initWithItemTitle:#"iPhone"];
Item *iPadItem = [[Item alloc] initWithItemTitle:#"iPad"];
Item *macBookPro = [[Item alloc] initWithItemTitle:#"MacBookPro"];
self.itemsArray[0] = iPhoneItem;
NSLog(#"%#", self.itemsArray[0].itemTitle); //How would i do this?
}
#end
Best regards, iOS-rookie.
You can simply check iOS reference, no need to ask questions: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html#//apple_ref/occ/instm/NSMutableArray/addObjectsFromArray:
Anyway, here is an example:
[itemsArray addObjectsFromArray: #[obj1, obj2]]; //adding multiple objects
((Item *)[itemsArray firstObject]).itemTitle //get title of your object
It is bad practice to access properties for an object in an array directly like :
self.itemsArray[0].itemTitle
It is cleaner to:
Item iPhoneItem = (Item)[itemsArray objectAtIndex:0];
NSLog(#"%#", iPhoneItem.itemTitle);
Also keep in mind that you can use [itemsArray firstObject]; and [itemsArray lastObject];

iOS problems sharing data between view controllers

I'm having problems passing data between two view controllers.
I've seen two ways to do this.
One involves implementing prepareForSeque: in the segue's source view controller and another involves setting properties in the viewDidLoad: method of the segue's destination view controller.
e.g.-
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"toEmailReservationViewController"]) {
FLSendEmailViewController *controller = (FLSendEmailViewController *)segue.destinationViewController;
if (!controller.startDateField.text) {
controller.startDateField.text = #"today";
}
}
}
and
- (void)viewDidLoad
{
[super viewDidLoad];
self.startDateField.text = ((FLViewController *)self.presentingViewController).startDate;
}
I've got these to work on simple apps using two UIViewController. However, I can't get them to work on an app that has a UITabViewController connected to some UINavigationViewController connected to custom subclasses of UIViewController. When I click the button to perform the push seque, I get to the view I want, but the startDateField.text doesn't have the text from the segue's source view controller.
Why are these methods of sharing data not working with the tab controller and navigation controller setup?
I noticed that in prepareForSegue: I can't set controller.startDateField.text; as shown when I try to set it and use NSLog to display it. Could this be the problem? Is it possible that the property controller.startDateField.text doesn't exist yet?
I'm trying to grab the date from a datePicker in an instance of FLViewController, store this date in the property NSString *startDate, and in an instance of FLSendEmailViewController set NSString *startDateField.text to the `NSString *startDate'.
Here are the UIViewController subclasses I created:
FLViewController.h
#import <UIKit/UIKit.h>
#import "FLSendEmailViewController.h"
// import frameworks to use ad
#import AddressBook;
#import AddressBookUI;
#interface FLViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIScrollView *theScroller;
#property (weak, nonatomic) NSString *startDate;
#property (weak, nonatomic) NSString *stopDate;
- (IBAction)exitToReservations:(UIStoryboardSegue *)sender;
#end
FLViewController.m
#import "FLViewController.h"
#interface FLViewController ()
#property (weak, nonatomic) IBOutlet UIDatePicker *startReservationDatePicker;
#property (weak, nonatomic) IBOutlet UIDatePicker *stopReservationDatePicker;
#end
#implementation FLViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.theScroller setScrollEnabled:YES];
[self.theScroller setContentSize:CGSizeMake(280, 1000)];
//setup reservationDatePicker
[self.startReservationDatePicker addTarget:self
action:#selector(startDatePickerChanged:)
forControlEvents:UIControlEventValueChanged];
[self.stopReservationDatePicker addTarget:self
action:#selector(stopDatePickerChanged:)
forControlEvents:UIControlEventValueChanged];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
// add method called when user changes start date
- (void)startDatePickerChanged:(UIDatePicker *)datePicker
{
NSDateFormatter *dateFortmatter = [[NSDateFormatter alloc] init];
[dateFortmatter setDateFormat:#"dd--MM-yyyy HH:mm"];
// get date using stringFromData: method and getter datePicker.date
self.startDate = [dateFortmatter stringFromDate:datePicker.date];
NSLog(#"The start date is %#", self.startDate);
}
// add method called when user changes stop date
- (void)stopDatePickerChanged:(UIDatePicker *)datePicker
{
NSDateFormatter *dateFortmatter = [[NSDateFormatter alloc] init];
[dateFortmatter setDateFormat:#"dd--MM-yyyy HH:mm"];
// get date using stringFromData: method and getter datePicker.date
self.stopDate= [dateFortmatter stringFromDate:datePicker.date];
NSLog(#"The stop date is %#", self.stopDate);
}
- (IBAction)exitToReservations:(UIStoryboardSegue *)sender {
// execute this code upon unwinding
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"toEmailReservationViewController"]) {
FLSendEmailViewController *controller = (FLSendEmailViewController *)segue.destinationViewController;
if (!controller.startDateField.text) {
controller.startDateField.text = #"today";
NSLog(#"in vc startDate is null but set to %#",controller.startDateField.text );
}
}
}
#end
FLSendEmailViewController.h
#import <UIKit/UIKit.h>
#class FLViewController;
#interface FLSendEmailViewController : UIViewController
#property (retain, nonatomic) IBOutlet UITextField *startDateField;
#property (retain, nonatomic) IBOutlet UITextField *stopDateField;
#end
FLSendEmailViewController.m
#import "FLSendEmailViewController.h"
#import "FLViewController.h"
#interface FLSendEmailViewController ()
- (IBAction)sendEmail:(id)sender;
#property (weak, nonatomic) IBOutlet UITextField *numberOfDoggies;
#property (weak, nonatomic) IBOutlet UITextField *emailAddressField;
- (IBAction)hideKeyboard:(id)sender;
#end
#implementation FLSendEmailViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.startDateField.text = ((FLViewController *)self.presentingViewController).startDate;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)sendEmail:(id)sender {
NSString *emailString = [NSString stringWithFormat:#"I would like you to watch my %# doggies from %# to %#. Thank you.", self.numberOfDoggies.text, self.startDateField.text, self.stopDateField.text];
NSLog(#"%#",emailString);
}
- (IBAction)hideKeyboard:(id)sender {
[self.startDateField resignFirstResponder];
}
#end
Import the FLSendEmailViewController.h to the "InitialViewController.h"
Add this property to the FLSendEmailViewController.h
#property (nonatomic, strong) NSString *exportedData;
Add this to the FLSendEmailViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.startDateField.text = self.exportedData
}
4.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"toEmailReservationViewController"]) {
FLSendEmailViewController *controller = (FLSendEmailViewController *)segue.destinationViewController;
if (!controller.startDateField.text) {
controller.exportedData = #"today";
}
}
}
This "sharing method" works in any case when from a controller go to another controller.
So, in case of a navigation controller, if you want push another viewController passing the data, you have just to set the trigger in the Storyboard (if you are using storyboard) from the button, and the new viewController.
So, you will not met troubles. Otherwise, you are committing other type of errors, and in this case, update your question.

Add Item from another class to a property from another class. Objective-C

So I have two classes. When press the save button, it will pass down the value from self.screen.text by addItem method to the totalArray in class 2. If I try to NSLog in the #implementation of addItem method, then it will give out the correct output but If I do it in viewDidLoad, the output is null. How can I save the value passing from class1 to property of class2 permanently? Thank you. The class2 in a subclass of UITableViewController
Class 1 #interface
//class1.h
#import class2.h
#interface class1 : superclass {
}
- (IBAction)buttonSave:(id)sender;
Class1 #implementation
//class1.m
#interface class1 ()
#end
#implementation class1 {
}
- (IBAction)buttonSave:(id)sender {
class2 *Obj = [[class2 alloc] init];
[Obj addItem:self.screen.text];
}
And class2 #interface
//class2.h
#import class2.h
#interface {
}
#property (strong, nonatomic) NSMutableArray *totalArray;
class2 #implementation
#interface class2 ()
#end
#implementation {
}
- (void) addItem:(id)item {
self.totalArray = [[NSMutableArray alloc] init]; //alloc & init
[self.totalArray addObject:item]; //add object to the total array
// NSLog(#"%#", self.totalArray); If I NSLog in within this method then everything works as expected.
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"%#", self.totalArray); //But in here the output is null. ???
}
I think that your problem is that you have use a different class2 object. The one that you had init in buttonSave, is not the one that you are displaying
add a property in class1.h
#property (nonatomic, strong) NSMutableArray *savedArray;
and modify buttonSave :
- (IBAction)buttonSave:(id)sender {
self.savedArray = [[NSMutableArray alloc] init];
[self.savedArray addObject:self.screen.text];
}
You are using a storyboard, then please try to add this in class1.h and add an identifier class2Segue to this segue in your storyboard :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"class2Segue"])
{
Class2 *tableController = (Class2 *)[segue destinationViewController];
tableController.totalArray = self.savedArray;
}
}
viewDidLoad is called after init so you array is nil here. Change your class2 init method to accept the item.
// In class2
-(id) initWithStyle:(UITableViewStyle)style andItem:(id)item {
self = [super initWithStyle:style];
if(self) {
self.totalArray = [[NSMutableArray alloc] init];
[self.totalArray addObject:item];
}
return self;
}
Your addItem will then look like,
- (void) addItem:(id)item {
//Just add, do not initialize again
[self.totalArray addObject:item];
}
The button action in class1 will now look like,
- (IBAction)buttonSave:(id)sender {
class2 *Obj = [[class2 alloc] initWithItem:self.screen.text];
//OR
//class2 *Obj = [[class2 alloc] initWithItem:UITableViewStylePlain andItem:self.screen.text];
}
Hope that helps!
Try to use like this...
- (IBAction)buttonSave:(id)sender
{
class2 *Obj = [[class2 alloc] init];
Obj.totalArray = [[NSMutableArray alloc] init]; //alloc & init
[Obj.totalArray addObject:self.screen.text];
NSLog(#"screen.text %#", self.screen.text); // -- check here it may be null----
NSLog(#"Obj.totalArray %#", Obj.totalArray);
}
#interface class2 ()
#end
#implementation {
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", self.totalArray); //But in here the output is null. ???
}
You can not ensure when your viewDidLoad method will call... so better pass the value to the init method and set there initWithText:(NSString*)text{}. Other wise try to call NSLog in viewWillAppear or viewDidAppear just for testing purpose. In iOS 7 now presentation of view-controllers is bit changed now.

Resources