Accessing properties from Object within NSMutableArray not working - ios

I have the class LearnfestItem.h :
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface LearnfestItem : NSObject
#property (strong, nonatomic) NSString * itemId;
#property (strong, nonatomic) NSString * itemTitle;
#property (strong, nonatomic) NSString * itemDescription;
#property (strong, nonatomic) NSString * itemContent;
#property (strong, nonatomic) NSString * itemType;
#property (strong, nonatomic) UIImage * itemImage;
#property (strong, nonatomic) NSDate * itemRegistered;
-(id)initWithData:(NSDictionary *)data andImage:(UIImage *)image;
#end
& Object.m :
#import "LearnfestItem.h"
#import "Defaults.h"
#implementation LearnfestItem
-(id)init
{
self = [self initWithData:nil andImage:nil];
return self;
}
-(id)initWithData:(NSDictionary *)data andImage:(UIImage *)image
{
self = [super init];
self.itemId = data[ITEM_ID];
self.itemTitle = data[ITEM_TITLE];
self.itemDescription = data[ITEM_DESCRIPTION];
self.itemContent = data[ITEM_CONTENT];
self.itemType = data[ITEM_TYPE];
self.itemImage = image;
self.itemRegistered = data[ITEM_REGISTERED];
return self;
}
#end
In my UIViewController I have a UITableView that creates a NSMutableArray of LearnfestItems within the cellForRowAtIndexPath method:
LearnfestItem * createLearnfestItem = [[LearnfestItem alloc] initWithData:learnfestItemDictionary andImage:learnfestItemImage];
NSLog(#"Insert learnfest item with id: %# at index %li", createLearnfestItem.itemId, (long)row);
[self.learnfestItemObjects insertObject:createLearnfestItem atIndex:row];
On didSelectRowAtIndexPath I want to receive the LearnfestItem from the NSMutableArray I do this by calling:
self.selectedLearnfestItem = [self.learnfestItemObjects objectAtIndex:indexPath.row];
Then I want to send it to another view controller to present the data I do this in the prepareForSegue segement:
LearnfestItemViewController * learnfestVC = [segue destinationViewController];
NSLog(#"Sending learnfest item with id: %#", self.selectedLearnfestItem.itemId);
learnfestVC.item = self.selectedLearnfestItem;
When I try to access my LearnfestItem's properties within cellForRowAtIndexPath. All I get is null... and so forth in my other table view delegate methods.. Can anyone spot what i'm doing wrong? Thanks

Code you show is missing all error and validity checking. When using NSAssert() and item.length/item.count checks, you will know what's going on.
However, based on your code I'd suggest two things:
All objects which also have mutable version should use "copy" properties. Now you have "strong" pointer to data received via init method, but if you reset the originally given data variable to point elsewhere (e.g. reusing one data variable inside for loop to init multiple items), then... I don't know what your LearnfestItem item properties will point to.
Make sure your LearnfestItemViewController *item is a copy, too.
My guess is that everything is initialised correctly, but the data disappears afterwards.
For example:
#property (nonatomic, copy) NSString * itemId; // Safe
#property (nonatomic, strong) NSString * itemId; // Not safe
When your property class has mutable variations (e.g. NSString vs. NSMutableString vs. MyMutableString), copy is safer. Using strong will create a pointer to original data, which could have been a mutable instance and could be modified afterwards. Using strong will always point to original data, even after it has been changed.
Second part:
learnfestVC.item = self.selectedLearnfestItem;
Your LearnfestItemViewController contains some property related to LearnfestItem class. Make sure it's a copy, too. When using segues, the calling object quite often just disappears. Make sure your new controller has local copy of all needed data (or use a protocol delegate, but that's a longer discussion)

Adding error and data validity checking will make your task a lot easier. Instead of trying to figure out afterwards why something doesn't work, you'll get notifications when something isn't as you're expecting.
Here's quick and dirty "maintenance" for your code. What you should get out of this is some ideas what to check, where and how. In normal situations this is overkill, but now you have a mysterious problem and need to find it. It can be hard and monotonous work.
#import Foundation;
#import UIKit;
#interface LearnfestItem : NSObject
#property (copy, nonatomic) NSString *itemId;
#property (copy, nonatomic) NSString *itemTitle;
#property (copy, nonatomic) NSString *itemDescription;
#property (copy, nonatomic) NSString *itemContent;
#property (copy, nonatomic) NSString *itemType;
#property (strong, nonatomic) UIImage *itemImage;
#property (strong, nonatomic) NSDate *itemRegistered;
- (instancetype)initWithData:(NSDictionary *)data andImage:(UIImage *)image;
#end
Object.m :
#import "Defaults.h"
#import "LearnfestItem.h"
#implementation LearnfestItem
- (instancetype)init
{
self = [self initWithData:nil andImage:nil];
return self;
}
- (instancetype)initWithData:(NSDictionary *)data andImage:(UIImage *)image
{
NSAssert(data.length, #"My Assert: missing data");
NSAssert(image, #"My Assert: missing image");
if ((self = [super init]))
{
// TODO: nil ok, if doesn't exist?
_itemId = data[ITEM_ID];
_itemTitle = data[ITEM_TITLE];
_itemDescription = data[ITEM_DESCRIPTION];
_itemContent = data[ITEM_CONTENT];
_itemType = data[ITEM_TYPE];
_itemImage = image;
_itemRegistered = data[ITEM_REGISTERED];
}
return self;
}
#end
"(original text) In my UIViewController I have a UITableView that creates a NSMutableArray of LearnfestItems within the cellForRowAtIndexPath method:"
NSAssert(learnfestItemDictionary.count, #"My Assert: missing dict");
NSAssert(learnfestItemImage, #"My Assert: missing image");
LearnfestItem *createLearnfestItem = [[LearnfestItem alloc] initWithData:learnfestItemDictionary andImage:learnfestItemImage];
NSLog(#"Insert learnfest item with id: %# at index %#", createLearnfestItem.itemId, #(row));
NSAssert(createLearnfestItem, #"My Assert: missing item");
NSAssert(self.learnfestItemObjects.count > row, #"My Assert: learnfestItemObjects");
self.learnfestItemObjects[row] = createLearnfestItem;
"(original text) On didSelectRowAtIndexPath I want to receive the LearnfestItem from the NSMutableArray I do this by calling:"
NSAssert(self.learnfestItemObjects.count > indexPath.row, #"My Assert: learnfestItemObjects");
self.selectedLearnfestItem = self.learnfestItemObjects[indexPath.row];
"(original text) Then I want to send it to another view controller to present the data I do this in the prepareForSegue segment:"
LearnfestItemViewController *learnfestVC = (LearnfestItemViewController *)[segue destinationViewController];
NSLog(#"Sending learnfest item with id: %#", self.selectedLearnfestItem.itemId);
learnfestVC.item = self.selectedLearnfestItem;

Related

Copying one NSString into other one Xcode

I have a problem in Xcode.I am trying to copy one NSString into other one but is not working.
The original NSString pageTitle is on the RestViewController:
This is my RestViewContoller.h:
#import <UIKit/UIKit.h>
#import "Restaurant.h"
#interface RestViewController : UIViewController
#property (strong, nonatomic) IBOutlet UILabel *TitleLabel;
#property (strong, nonatomic) IBOutlet UILabel *DescriptionLabel;
#property (strong, nonatomic) IBOutlet UIImageView *ImageView;
#property (nonatomic, strong) Restaurant *DetailModal;
#property (nonatomic, strong) NSString *pageTitle;
#property (nonatomic, strong) NSString *pageDescription;
#end
The NSString I want to save the data is on the RestViewController:
#property (nonatomic, strong) Restaurant *DetailModal;
but is a Restaurant class type.
My Restaurant class is:
Restaurant.h:
#import <Foundation/Foundation.h>
#interface Restaurant : NSObject
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *desc;
#property (nonatomic, copy) NSString *image;
- (instancetype)init:(NSString *)title descripiton:(NSString *)description image:(NSString *)image;
#end
Restaurant.m:
#import "Restaurant.h"
#implementation Restaurant
- (instancetype)init:(NSString *)title descripiton:(NSString *)description image:(NSString *)image {
self = [super init];
if (self != nil) {
self.title = title;
self.desc = description;
self.image = image;
}
return self;
}
#end
And finally the place where I want to copy this NSStrings is my RestViewController.m:
#import "RestViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface RestViewController ()
#end
#implementation RestViewController
- (void)viewDidLoad {
[super viewDidLoad];
_pageTitle = #"Test";
_DetailModal.title = _pageTitle;
NSLog(#"_DetailModal.title: %#", _DetailModal.title);
}
#end
The problem is when I see the result of the
NSLog(#"_DetailModal.title: %#", _DetailModal.title);on the console,
it puts me:2016-11-21 11:42:05.407 MadEat[3667:104028]
_DetailModal.title: (null)
What can I do? I know I have a low level of Xcode, but I need your help please. Thank you very much.
Initailize the instance of DataModel First.
Then proceed with assignment
_DetailModal = [[DetailModal alloc] init:_pageTitle descripiton:#"Desc" image:#"image"];
There is a instance method defined already to do the task.
After this also you can alter the value by
_DetailModal.title = #"title_of_yours"
If you have to use detailmodal, you must alloc the restaurant class object (detailmodal). Then only the objects can be used.
detailmodal=[[restaurant alloc]init];
detailmodal.title=_pagetitle;
Before you can use a object, you have to create it first.
DetailModal *detailModal = [DetailModal new];
That way the object will be created and values stored to the class properties will be reserved.
Try:
_DetailModal.title = [[NSString alloc] initWithString: _pageTitle];
(Did not test this)
plz chek you _DetailModal is not nil
if it's nil then
first you need to alloc the memory for you DetailModal Class object
like this
DetailModal *detailModal = [DetailModal new];
then after set the value you want
detailModal.title = _pageTitle;

How to save UITextField Text to NSString?

In my main view controller, I have a UITextField, and I am trying to save the text input into it to a NSString in my Homework model(class).
Homework.h
#import <Foundation/Foundation.h>
#interface Homework : NSObject
#property (nonatomic, strong) NSString *className;
#property (nonatomic, strong) NSString *assignmentTitle;
#end
Homework.m
#import "Homework.h"
#implementation Homework
#synthesize className = _className;
#synthesize assignmentTitle = _assignmentTitle;
#end
In my assignmentViewController, I create an object of type Homework and try to set it equal to whatever is entered into the the UITextField when the Save Button is pressed.
Assignment View Controller
#import <UIKit/UIKit.h>
#import "Homework.h"
#interface Assignment : UIViewController {
}
#property(nonatomic) IBOutlet UITextField *ClassNameField;
#property(nonatomic) IBOutlet UILabel *ClassNameLabel;
#property (weak, nonatomic) IBOutlet UIButton *SaveButton;
#property (nonatomic, strong) Homework *homeworkAssignment;
- (IBAction)Save:(UIButton *)sender;
#end
AssignmentViewController.m
- (IBAction)Save:(UIButton *)sender {
self.homeworkAssignment.className = self.ClassNameField.text;
NSLog(#"SaveButtonPressed %#", self.homeworkAssignment.className);
}
The NSLog prints out that className is (null). Can anyone help me figure out what I am doing wrong? This is my first ever iOS app (besides Hello World).
Edit: This is using ARC
Edit: I tried changing
self.homeworkAssignment.className = self.ClassNameField.text; to
self.homeworkAssignment.className = #"TEST";
and the log still shows (Null).
Double check you properly linked ClassNameField outlet and that you're initializing homeworkAssignment. Something like.-
self.homeworkAssignment = [[Homework alloc] init];
By the way, you should consider using camelCase notation for your variable names :)
Well to be honest the first steps are always hard but you should learn it the right way, héhé
First of all synthesize this way:
#synthesize labelAssignmentTitle,labelClassName;
or
#synthesize labelAssignmentTitle;
#synthesize labelClassName;
there is no need to do the following:
#synthesize className = _className;
#synthesize assignmentTitle = _assignmentTitle;
Now if you initialize the right way from the the start you'll find it a lot easier later!
HomeWork.h
#interface HomeWork : NSObject
#property (nonatomic, strong) NSString *className;
#property (nonatomic, strong) NSString *assignmentTitle;
-(id)initWithClassName:(NSString *)newClassName andAssignmentTitle:(NSString*)newAssigmentTitle;
HomeWork.m
#implementation HomeWork
#synthesize assignmentTitle,className;
-(id)initWithClassName:(NSString *)newClassName andAssignmentTitle:(NSString*)newAssigmentTitle {
self = [super init];
if(self){
assignmentTitle = newAssigmentTitle;
className = newClass;
}
return self;
}
#end
ViewController.m
- (IBAction)saveIt:(id)sender {
HomeWork *newHomeWork = [[HomeWork alloc]initWithClassName:[labelClassName text]andAssignmentTitle:[labelAssignmentTitle text]];
}
Because of this, you directly make a newHomeWork object with the parameters given by your two UITextFields.
Now print it out in your logmessage and see what happends ;)

Unrecognized selector sent to instance (UIStepper)

Okay, I know there is a ton of these questions out there, because I've looked and tried some of the solutions. However, many of the ones I tried didn't work or the answer was too over my head for me to really grasp well - I'm a new developer and this is my first app. I learn by learning what not to do at this point.
I have the 'unrecognized selector sent to instance error' on a UIStepper stepperValueChanged setup. Here is the contents of the error message as it is given to me:
[DetailViewController stepperValueChanged]: unrecognized selector sent to instance 0x8637630
I will probably be ripped apart for this, but I can't really understand what's going on here - my only guess so far is to assume it has something to do with the only point in my code where stepperValueChanged exists - under the DetailViewController.h, as placed below:
#interface DetailViewController : UIViewController <UISplitViewControllerDelegate>
{
// Create GUI parameters for text fields, text labels, and the stepper:
IBOutlet UITextField *value1;
IBOutlet UITextField *value2;
IBOutlet UITextField *value3;
IBOutlet UISwitch *double_precision;
IBOutlet UILabel *value1_type;
IBOutlet UILabel *value2_type;
IBOutlet UILabel *value3_type;
IBOutlet UILabel *deriv_units;
IBOutlet UILabel *units;
IBOutlet UILabel *result;
IBOutlet UIStepper *stepper;
}
// Define properties of the above GUI parameters:
#property (nonatomic, retain) UITextField *value1;
#property (nonatomic, retain) UITextField *value2;
#property (nonatomic, retain) UITextField *value3;
#property (nonatomic, retain) UILabel *value1_type;
#property (nonatomic, retain) UILabel *value2_type;
#property (nonatomic, retain) UILabel *value3_type;
#property (nonatomic, retain) UILabel *deriv_units;
#property (nonatomic, retain) UILabel *units;
#property (nonatomic, retain) UILabel *result;
// Setup property as instance of UIStepper:
#property (nonatomic, strong) IBOutlet UIStepper *stepper;
// Setup NSString instance for segue linking:
#property (nonatomic, strong) NSString *equationName;
#property (strong, nonatomic) id detailItem;
#property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel;
// IBActions for the Calculate button and UIStepper instance:
- (IBAction)Calculate:(id)sender;
- (IBAction)stepperValueChanged:(id)sender;
- (IBAction)double_precision:(id)sender;
#end
Any ideas what is going on here? I don't have much of a clue, and if anyone can help explain to me what exactly is in play here while addressing it, I would be more than grateful.
If you need the contents of the implementation file, let me know; I'll edit it in.
Relevant areas of the .m file:
#interface DetailViewController ()
#property (strong, nonatomic) UIPopoverController *masterPopoverController;
- (void)configureView;
#end
#implementation DetailViewController
// Synthesize an instance of NSString for segue linking:
#synthesize equationName = _equationName;;
// Synthesize all other variables:
#synthesize value1 = _value1;
#synthesize value2 = _value2;
#synthesize value3 = _value3;
#synthesize value1_type = _value1_type;
#synthesize value2_type = _value2_type;
#synthesize value3_type = _value3_type;
#synthesize deriv_units = _deriv_units;
#synthesize result = _result;
#synthesize units = _units;
#synthesize stepper = _stepper;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
self.title = _equationName;
self.stepper.stepValue = 1;
self.stepper.autorepeat = NO;
self.stepper.continuous = YES;
self.stepper.wraps = YES;
int eqNum;
if ((_equationName = #"Energy-Frequency Relation"))
{
eqNum = 1;
self.stepper.minimumValue = 1;
self.stepper.maximumValue = 3;
}
else if ((_equationName = #"Energy-Frequency-Wavelength Relation"))
{
eqNum = 2;
self.stepper.minimumValue = 1;
self.stepper.maximumValue = 4;
}
// Take _equationName quantization and use it in a switch case to determine the formula that IBAction will use:
if (dflt)
{
switch (eqNum)
{
case 1:
if ((stepper.value = 1))
{
// Change deriv_units appropriately:
self.deriv_units.text = #"Energy (Joules)";
// This is a Planck's constant calculation, we hide the second variable as the constant
// is stored:
self.value2.hidden = YES;
self.value2_type.hidden = YES;
self.value3.hidden = YES;
self.value3_type.hidden = YES;
// Now we set up the parameters of the first entry variable:
self.value1_type.text = #"Frequency (in Hz)";
double frequency = [value1.text doubleValue];
double Planck = 6.626069e-34;
double energy = Planck * frequency;
// Now we set up the return field to return results:
NSString* resultIntermediate = [NSString stringWithFormat:#"%f", energy];
self.units.text = #"J";
}
// Identical function statements under ViewDidLoad truncated
}
bool dflt;
-(IBAction)KeyboardGoAway:(id)sender
{
[self.value1 resignFirstResponder];
[self.value1 resignFirstResponder];
[self.value1 resignFirstResponder];
}
-(IBAction)double_precision:(id)sender
{
// Sets double-float 'truth' value depending on state of UISwitch:
if (double_precision.on)
{
dflt = TRUE;
}
else
{
dflt = FALSE;
}
}
#pragma mark - Calculation runtime
-(IBAction)Calculate:(id)sender
{
// Assigns numerical information to _equationName data -
// switch case can only handle integer literals
// Also handles stepper incrementation and UILabel/UITextView hiding
NSString* resultIntermediate;
self.result.text = resultIntermediate;
}
The trailing colon makes the difference. Your action method is stepperValueChanged:,
but from the error message it seems that you connected the stepper to stepperValueChanged.
There are two reason for these kind of issues.
Probable case 1:
You first declared the function like - (IBAction)stepperValueChanged;
Connected the IBAction to stepper
Changed the method to - (IBAction)stepperValueChanged:(id)sender;
Solution:
Delete old connection in the interface builder and connect it again.
Probable case 2:
In your code you are calling the method using a selector where you written like: #selector(stepperValueChanged)
Solution:
Change the selector like: #selector(stepperValueChanged:)
Usually this means you are missing the method in your .m or you might of misspelled stepperValueChanged.
Edit: Actually, I believe it needs to be stepperValueChanged: with a semicolon.

Issue passing object to NSMutableArray in AppDelegate

I'm having trouble making a shopping cart sort-of concept in my app. I have my AppDelegate (named ST2AppDelegate) that contains an NSMutableArray called myCart. I want RecipeViewController.m to pass an NSString object to myCart, but every time I pass it the NSString and use NSLog to reveal the contents of the array, it is always empty.
Can anyone please tell me what I am doing wrong? I have worked on this code for days, and there is a line of code in which I don't understand at all what's going on (in the RecipeViewController.m, labeled as such).
Any help would be so appreciated... I'm just a beginner. Here are the relevant classes:
ST2AppDelegate.h:
#import <UIKit/UIKit.h>
#interface ST2AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) NSMutableArray* myCart;
- (void)addToCart:(NSString*)item;
- (void)readCartContents;
#end
ST2AppDelegate.m:
#import "ST2AppDelegate.h"
#implementation ST2AppDelegate
#synthesize myCart;
// all the 'applicationDid...' methods...
- (void)addToCart:(NSString *)item
{
[self.myCart addObject:item];
}
- (void)readCartContents
{
NSLog(#"Contents of cart: ");
int count = [myCart count];
for (int i = 0; i < count; i++)
{
NSLog(#"%#", myCart[count]);
}
}
#end
RecipeDetailViewController.h:
#import <UIKit/UIKit.h>
#import "ST2AppDelegate.h"
#interface RecipeDetailViewController : UIViewController
#property (nonatomic, strong) IBOutlet UILabel* recipeLabel;
#property (nonatomic, strong) NSString* recipeName;
#property (nonatomic, strong) IBOutlet UIButton* orderNowButton;
- (IBAction)orderNowButtonPress:(id)sender;
#end
RecipeDetailViewController.m:
#import "RecipeDetailViewController.h"
#implementation RecipeDetailViewController
#synthesize recipeName;
#synthesize orderNowButton;
// irrelevant methods...
- (IBAction)orderNowButtonPress:(id)sender
{
// alter selected state
[orderNowButton setSelected:YES];
NSString* addedToCartString = [NSString stringWithFormat:#"%# added to cart!",recipeName];
[orderNowButton setTitle:addedToCartString forState:UIControlStateSelected];
// show an alert
NSString* addedToCartAlertMessage = [NSString stringWithFormat:#"%# has been added to your cart.", recipeName];
UIAlertView* addedToCartAlert = [[UIAlertView alloc] initWithTitle:#"Cart Updated" message:addedToCartAlertMessage delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[addedToCartAlert show];
// add to cart (I don't understand this, but it works)
[((ST2AppDelegate*)[UIApplication sharedApplication].delegate) addToCart:recipeName];
// read cart contents
[((ST2AppDelegate*)[UIApplication sharedApplication].delegate) readCartContents];
}
#end
You need to initialize myCart when your application launches:
self.myCart = [[NSMutableArray alloc] init];
otherwise you are just attempting to add objects to a nil object which while it won't throw an exception because of the way objective-c handles nil objects it will not function as expected until you initialize it.
Do you ever initalize the shopping cart variable?
Try doing lazy instantiation.
-(NSMutableArray *) myCart{
if (!_myCart){
_myCart = [[NSMutableArray alloc] init];
}
return _myCart;
}
This way you will know it will always get allocated. Basically, this method makes it so that whenever someone calls your classes version of the object it checks to see if that object has been allocated and then allocates it if it has not. It's a common paradigm that you should employ with most of your objects.
This method should go in the app delegate (where the object was declared).

Won't call on method in other class, Objective-C

I am trying to learn Objective-C for iOS. i am currently following the "coding together" on iTunesU. Although i have got stuck since i can't get my controller to call on a method from another class. Can't find what i am doing wrong and thought that StackOverflow might have the solution to it!
The method "flipCardAtIndex" is the one that isn't working. I have debugged using nslog and from the method "flipCard" i get an output. But when i put in the implementation for flipCardAtIndex i don't get anything.. So my guess is that it never calls it...
I have made the code a bit shorter so it is only the parts i think is important, this is controller:
#import "ViewController.h"
#import "PlayingCardDeck.h"
#import "CardMatchingGame.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
#property (nonatomic) int flipCount;
#property (weak, nonatomic) IBOutlet UILabel *scoreLabel;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;
#property (strong, nonatomic) CardMatchingGame *game;
#end
#implementation ViewController
- (CardMatchingGame *) game{
if (_game) _game = [[CardMatchingGame alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[[PlayingCardDeck alloc] init]];
return _game;
}
- (IBAction)flipCard:(UIButton *)sender {
[self.game flipCardAtIndex:[self.cardButtons indexOfObject:sender]];
self.flipCount++;
[self updateUI];
}
And implementation:
- (void)flipCardAtIndex:(NSUInteger)index
{
NSLog(#"ALL YOUR BASE ARE BELONG TO US");
Card *card = [self cardAtIndex:index];
}
Fix?
- (CardMatchingGame *) game{
if (!_game) _game = [[CardMatchingGame alloc] initWithCardCount:[self.cardButtons count] usingDeck:[[PlayingCardDeck alloc] init]];
return _game;
}

Resources