Need help transferring data between ViewControllers - ios

I want to keep my project better organized and instead of overloading my Appdelegate with data that I want to transfer from viewcontroller to viewcontroller I want to create a model class to help keep things more organized. However, when using models I am having a hard time transferring data between controllers. Can you show me the error of my ways when trying to transfer this nsstring data from ViewController1 to ViewController2? P.S. I made this example up , because my real project is a little bit more messy so I apologize in advance for any inconsistencies. The following results in NSLog reporting null in
ViewController2.m
ViewController1.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
ViewController2 *viewController2 =[[ViewController2 alloc]init];
ViewControllerModel *vcm = [self.array objectAtIndex:indexPath.row];
NSLog(#"%#",vcm.string) // this will output a number
[self.navigationController pushViewController:viewController2 animated:YES];
}
// this delegate fetches an array of json data
-(void)fetchedResults:(NSMutableArray*)arrayList{
self.array = arrayList;
}
ViewController2.m
- (void)viewDidLoad
{
ViewControllerModel *vcm = = [[ViewController alloc] init];
[super viewDidLoad];
NSLog(#"%#",vcm.string); // this will output null.
}
ViewControllerModel .h
#import
#interface ViewControllerModel : NSObject
#property (nonatomic, strong) NSString *string;
#end
ViewControllerModel.m
#import "ViewControllerModel.h"
#implementation ViewControllerModel
#synthesize string;
#end
MyHandler.m
//this is where vcm.string in ViewController1.m will get all the numbers not sure if this is needed but just in case .
for (NSDictionary *dict in responseArray)
{
ViewControllerModel *vcm = [[ViewControllerModel alloc] init];
vcm.string = ([dict valueForKey:#"string"] == [NSNull null]) ? #"" : [NSString stringWithFormat:#"%#",[dict valueForKey:#"string"]];

Make the following changes:
ViewController1.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
ViewController2 *viewController2 =[[ViewController2 alloc]init];
ViewControllerModel *vcm = [ViewControllerModel alloc] init];
vcm.string = #"My String";
viewController2.vcm = vcm;
NSLog(#"%#",vcm.string) // this will output a number
[self.navigationController pushViewController:viewController2 animated:YES];
}
// this delegate fetches an array of json data
- (void)fetchedResults:(NSMutableArray*)arrayList{
self.array = arrayList;
}
ViewController2.h
#interface ViewController2 : NSObject
#property (nonatomic, strong) ViewControllerModel *vcm;
#end
ViewController2.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", self.vcm.string);
}
I would also recommend the following improvements.
You don't really need a model object at this point. You could just add the string as an NSString property to ViewController2 instead of the ViewControllerModel object:
#property (nonatomic, copy) NSString *string;
I would recommend naming your properties, model object and view controllers to something more descriptive. Even if it's a sample it will be hard to understand it if you don't.
When you create an NSString property (or any other class that has a mutable equivalent) I'd recommend using 'copy' instead of 'strong'. This will make an immutable copy of the string if an NSMutableString is assigned to the property which is considered a safer approach.

Related

Execute a selector within another class

I would like to create a UITableViewController within another controller, as well as pass it a method from that controller. I already read that this can be achieved by using #selector. Now I tried the following:
TimeController.m
- (void)choseTime{
SelectOptionController *selectController = [[SelectOptionController alloc] initWithArray:[Time SQPFetchAll] andSelector:#selector(timeSelected)];
[self.navigationController pushViewController:selectController animated:true];
}
- (void) timeSelected{
NSLog(#"Time selected!");
}
SelectOptionController.h
#interface SelectOptionController : UITableViewController
#property (nonatomic, strong) NSMutableArray *dataset;
#property (nonatomic) SEL selectedMethod;
-(id)initWithArray: (NSMutableArray *) myArray andSelector: (SEL) selectedMethod;
SelectOptionController.m
- (id)initWithArray: (NSMutableArray *) myArray andSelector: (SEL) selectedMethod{
self = [super initWithStyle:UITableViewStyleGrouped];
if(self) {
self.dataset = myArray;
self.selectedMethod = selectedMethod;
}
return self;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self performSelector:self.selectedMethod];
[self.navigationController popViewControllerAnimated:true];
}
However, when a cell gets selected, the following exception is being thrown:
-[SelectOptionController timeSelected]: unrecognized selector sent to instance 0x1450f140
What am I doing wrong over here? Any help would be highly appreciated.
You are calling timeSelected on self which is actually SelectOptionController, but the timeSelected method exists in the TimeController class.
Assuming you don't want to move timeSelected to SelectOptionController, you need to pass a reference to the TimeController to the new SelectOptionController and call the selector on that. A selector is simply a reference to a method, not the method itself. You will probably want to store it as a weak reference too too.
E.g.
#interface SelectOptionController : UITableViewController
#property (nonatomic, strong) NSMutableArray *dataset;
#property (nonatomic) SEL selectedMethod;
#property (nonatomic, weak) TimeController *timeController;
And
- (id)initWithArray: (NSMutableArray *) myArray andSelector: (SEL) selectedMethod timeController:(TimeController*)timeController {
self = [super initWithStyle:UITableViewStyleGrouped];
if(self) {
self.dataset = myArray;
self.selectedMethod = selectedMethod;
self.timeController = timeController;
}
return self;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[self.timeController performSelector:self.selectedMethod];
[self.navigationController popViewControllerAnimated:true];
}
With all of that said, the above will get your code working but this isn't a particularly good pattern. I would suggest you look into Prototypes and Delegates for implementing this behaviour, or if you want to pass the method itself, do some research on Blocks. But hopefully this helps you better understand how selectors work.

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];

UITableView's custom delegate doesn't get called when displayed from UIPopoverController

Hoping someone had to solve related issues .. this is driving me nuts :/
My UITableViewController implements a custom delegate method:
.h
#protocol folderDelegate
#required
- (void)folderViewDidSelectPlan:(NSString*)planId;
#end
#interface FolderViewController : UITableViewController
#property (nonatomic, assign) id delegate;
#end
.m
#implementation FolderViewController
#synthesize delegate;
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
NSDictionary *row = [self->resultsPlan objectAtIndex:indexPath.row];
if ([delegate respondsToSelector:#selector(folderViewDidSelectPlan:)]) {
[delegate folderViewDidSelectPlan:[row objectForKey:#"id"]];
}
}
In my iPad's MainView I'm displaying this UITableView via UIPopoverController:
#interface ProjectViewController ()<folderDelegate>
...
- (void) selectPlan:(UIBarButtonItem*)sender
{
if([self->popoverSelectPlanController isPopoverVisible]){
[self->popoverSelectPlanController dismissPopoverAnimated:YES];
}
FolderViewController *folder = [[FolderViewController alloc] initWithStyle:UITableViewStyleGrouped withInstallation:self->_installationId withProjectId:self->_projectId withParentFolderId:#""];
folder.delegate = self;
UINavigationController *folderNavView = [[UINavigationController alloc] initWithRootViewController:folder];
self->popoverSelectPlanController = [[UIPopoverController alloc] initWithContentViewController:folderNavView];
[self->popoverSelectPlanController presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
And handling the delegate via:
- (void) folderViewDidSelectPlan:(NSString *)planId
{
NSLog(#"called");
}
However, folderViewDidSelectPlan never get's called - I'm really stuck here, hope anyone has an idea how to solve this.
Thanks a lot!
Try to change declaration of the property to:
#property (assign) id<folderDelegate> delegate;
And also use self.delegate instead of in your UITableViewController.m file every time instead of just delegate.
If you don't have to support iOS4 or less remove synthesise from UITableViewController.m.

iOS delegation does not work, cannot fix it (code inside): sending data from UITableView to UITextField in another view

I searched extensively for delegation tutorials, but could not get it to work. Here is the story and the code.
DetailViewController has a UITextField and a UIButton. When you press the button you get to another PricelistViewController with a simple sectioned table. Tapping a row in that table should return the text from the row's title to the first view and insert it into the text field. But it doesn't. Here is the code:
PricelistViewController.h (the second view):
#class PricelistViewController;
#protocol PricelistViewControllerDelegate <NSObject>
#required
//- (void)theSaveButtonOnThePriceListWasTapped:(PricelistViewController *)controller didUpdateValue:(NSString *)value;
- (void)theSaveButtonOnThePriceListWasTapped:(NSString *)value;
#end
#interface PricelistViewController : UITableViewController
#property (nonatomic, weak) id <PricelistViewControllerDelegate> delegate;
#property (retain, nonatomic) NSMutableArray * listOfSections;
#end
PricelistViewController.m
#synthesize delegate;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dictionary = [listOfSections objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"ProductSections"];
NSString *selectedProduct = [array objectAtIndex:indexPath.row];
[self.delegate theSaveButtonOnThePriceListWasTapped:[NSString stringWithFormat:#"%#", selectedProduct]];
[self.navigationController popViewControllerAnimated:YES];
}
This is the code in DetailViewController.h (the first view with a text field and the button):
#import "PricelistViewController.h"
#interface DetailViewController : UIViewController <UITextFieldDelegate, PricelistViewControllerDelegate>
DetailViewController.m (this I how I am trying to change the text in the field):
- (void)theSaveButtonOnThePriceListWasTapped:(NSString *)value
{
NSLog(#"Text, sent here: %#", value);
NSLog(#"Text was sent here.");
self.detailDescriptionLabel.text = value;
}
detailDescriptionLabel is the UITextField for the text to display.
Can somebody check the code and help? I work on this matter two days with no luck!
Firstly why are you forward referencing (#class) your class in the header file of the PriceListViewController? This isn't needed.
Secondly are you using ARC, if you are your array property should be of type nonatomic, strong. If you're not your delegate property should be nonatomic, assign. You seem to be mixing your terminology
Also where and how are you initialising your array?
I figured it out.
In the PricelistViewController.m I changed the way I call the method (added If statement):
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dictionary = [listOfSections objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"ProductSections"];
NSString *selectedProduct = [array objectAtIndex:indexPath.row];
if (delegatePrice && [delegatePrice respondsToSelector:#selector(theSaveButtonOnThePriceListWasTapped:didUpdateValue:)])
{
[delegatePrice theSaveButtonOnThePriceListWasTapped:self didUpdateValue:selectedProduct];
}
AND THE MOST IMPORTANT: I refused to use the Storyboard and instead made a old good .xib file for my PricelistViewController view and it worked right away!

Accessing instance variables from methods

Goal of program: Enter numbers on a viewController. When the user hits Submit button, the data entered by user is passed on to a different class for displaying on a different viewController.
Problem: I am trying to access an instance variable (numberList) in an instance method (-(void)insertNewNumber:(Numbers *)tempNumber), but it never gives me the correct output. But when I access the same variable through a protocol method of UITableViewDataSource, I get the correct answer. I figured this by using NSLog in instance method and protocol method.
Since I have declared numberList as a property variable, I was thought that I can access it from anywhere in the program and get the correct value stored in it. But compiler returned 0 for the NSLog statements when they were called from instance method. When the NSLog statements from protocol method, showed the correct result.
Please help me understand why is this occurring and how can I add elements into an array from any method in a program.
Thank you!
Here's the relevant code I am working on:
Numbers.h:
#interface Numbers:NSObject
#property (strong, retain) NSString *ID;
#property (strong, retain) NSInteger *number;
#end
Numbers.m
#implementation Numbers
#synthesize ID, number;
#end
DisplayNumbers.h
#import <UIKit/UIKit.h>
#import "Numbers.h"
#interface DisplayNumbers : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) NSMutableArray *numberList;
- (void)insertNewNumber:(Numbers *)tempNumber;
#end
DisplayNumbers.m:
#implementation DisplayNumbers
#synthesize numberList;
- (void)viewDidLoad
{
[super viewDidLoad];
numberList = [[NSMutableArray alloc] init];
Numbers *num0 = [[Numbers alloc] init];
Numbers *num1 = [[Numbers alloc] init];
num0.ID = [NSString stringWithFormat:#"ID 0"];
num0.number = 1111111111;
num1.ID = [NSString stringWithFormat:#"ID 1"];
num0.number = 2222222222;
[numberList addObject:num0];
[numberList addObject:num1];
}
- (void)insertNewNumber:(Numbers *)tempNumber
{
NSLog(#"numberList.count (in -(void)insetNewNumber) = %d", numberList.count);
[numberList addObject:tempNumber];
NSLog(#"numberList.count (in -(void)insetNewNumber) = %d", numberList.count);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"numberList.count (in -(NSInteger)tableView:...) = %d", numberList.count);
Numbers *temp = [[Numbers alloc] init];
temp.ID = #"hi";
temp.Number = 1234;
[numberList addObject:temp];
NSLog(#"numberList.count (in -(NSInteger)tableView:...) = %d", numberList.count);
return numberList.count;
}
#end
Edit 1: Calling of insertNewNumber:.
This method is being called from a different class.
InputNumber.h:
#import <UIKit/UIKit.h>
#import "DisplayNumbers.h"
#interface InputNumber:UIViewController
#property (retain, strong) NSInteger *enteredNumber;
-(void)enteredNumber;
#end
InputNumber.m
#implementation InputNumber
#synthesize enteredNumber;
-(void)enterNumber
{
DisplayNumber *temp = [[DisplayNumber alloc] init];
[temp insertNewNumber:enteredNumber];
}
#end
Since you allocate your numberList in the ViewDidLoad method, be sure to call your insertNewNumber method after the call to ViewDidLoad.
I believe that
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
Numbers *temp = [[Numbers alloc] init];
temp.ID = #"hi";
temp.Number = 1234;
[self insertNewNumber:temp];
return contactList.count;
}
works, right?
If you need to call your insertNewNumber method before the call to ViewDidLoad, allocate your numberList
numberList = [[NSMutableArray alloc] init];
in an overloaded initWithNibName:bundle: method.
Your code doesn't have an ivar called numberList. You need to call the property like this:
self.numberList = [NSMutableArray array];
No need to alloc/init since you're already using a strong reference.
Every time you refer to that numberList object, you need to use self.numberList.
[self.numberList addObject:num0];
[self.numberList addObject:num1];

Resources