Issue adding to NSMutableArray - objective-c

I have looked all over the place for anyone who has experienced this issue but have yet to find anything relevant, so I thought I'd ask it myself...
I have a custom object (HitterData) which I will use to populate cells in a UITableView, then two ViewControllers (one is hitterTableViewController, the other is a "detail" view controller labeled "AddPlayerViewController").
The problem is that I can add HitterData objects to the NSMutableArray in my Table VC, but only one, and then when I add another one using the detail view controller, the Mutable array is "reinitialized" and I can again only have one object at a time.
HitterObject:
#implementation HitterData.m
#synthesize hitterName = _hitterName;
#synthesize position = _position;
-(id)initWIthNameAndPosition:(NSString *)hitterName position:(NSString *)position {
if ((self = [super init])) {
self.hitterName = _hitterName;
self.position = _position;
}
return self;
}
HitterTableViewController.h
#import "HitterData.h"
#import "HitterDoc.h"
#import "AddPlayerViewController.h"
#interface HitterTableViewController : UITableViewController
#property (nonatomic, strong) NSMutableArray *hitters;
- (IBAction)backButton:(id)sender;
- (IBAction)addPlayerView:(id)sender;
-(void)addHitterObject:(HitterData *)hitter;
HitterTableViewController.m (only relevant to make this more readable)
#implementation HitterTableViewController
#synthesize hitters = _hitters;
- (void)viewDidLoad {
[super viewDidLoad];
self.hitters = [NSMutableArray array];
}
-(void)addHitterObject:(HitterData *)hitter {
if(_hitters != nil) {
[_hitters addObject:hitter];
} else {
_hitters = [NSMutableArray array];
[_hitters addObject:hitter];
NSLog(#"MutableArray is not valid");
}
}
AddPlayerViewController.h
#interface AddPlayerViewController : UIViewController <UITextFieldDelegate, UINavigationControllerDelegate>
#property (weak, nonatomic) IBOutlet UITextField *nameTextField;
#property (weak, nonatomic) IBOutlet UITextField *positionTextField;
#property (nonatomic) HitterTableViewController *hitterTable;
#property (nonatomic) NSString *hitterName;
#property (nonatomic) NSString *position;
//-(void)addNewHitterToHittersArray:(HitterData *)hitter;
- (IBAction)addPlayerToRoster:(id)sender;
AddPlayerViewController.m
#implementation AddPlayerViewController
#synthesize hitterTable;
- (void)viewDidLoad {
[super viewDidLoad];
hitterTable = [[HitterTableViewController alloc] init];
}
- (IBAction)addPlayerToRoster:(id)sender {
self.hitterName = [self.nameTextField text];
self.position = [self.positionTextField text];
HitterData *hitter = [[HitterData alloc] init];
hitter.hitterName = self.hitterName;
hitter.position = self.position;
[hitterTable addHitterObject:hitter];
ArraySingleton *arrayS = [[ArraySingleton alloc] init];
[arrayS initializeArray];
[arrayS addToHittersArray:hitter];
if (arrayS) {
NSLog(#"arrayS exists in AddPlayerVC");
} else {
NSLog(#"arrayS does not exist");
}
[self performSegueWithIdentifier:#"backToTeamTableViewController" sender:self];
}
Am I missing something here?

Guess based just on the code shown:
Every time you wish to add a player it looks like you create a new AddPlayerView/AddPlayerViewController. In turn that controller creates, in its viewDidLoad, a new HitterTableViewController - which of course has its own empty array. The code should instead be referencing the existing HitterTableViewController.
BTW: The common design pattern is MVC - model, view, controller - consider whether you are in your current situation because you've stored part of your model, the array, in your controller, and maybe both controllers should be referencing a common shared model object containing that array.
BTW: All those #synthesise statements are unneeded. In modern Obj-C synthesis is automatic and you rarely need these statements. Also it is generally recommended to not use the property backing variable directly, and certainly not when storing into the property as this breaks KVO. (There also appears to be a related typo in HitterData.m but as you don't report that as not working it is probably just a typo in your question and not code.)
HTH

AddPlayerViewController should know nothing about HitterTableViewController, return the new HitterData object with a delegate method.
- (IBAction)addPlayerToRoster:(id)sender
{
Hitter *hitter = [[Hitter alloc] init];
hitter.name = [self.nameTextField text];
hitter.position = [self.positionTextField text];
[self.delegate didAddHitter:hitter];
}
Then back in HitterTableViewController
- (void)didAddHitter:(Hitter *)hitter
{
[self.hitters addHitter:hitter];
[self dismissViewControllerAnimated:YES completion:nil];
}

Related

How to share some layout information between several views?

I have this snippet of code, BGBaseSnippetCode.h:
static NSMutableDictionary * defaultHeightDictionary= nil;
static NSMutableDictionary * defaultBoundsDictionary =nil;
+(void)initialize
{
BGBaseTableViewCell * typical = [[self alloc]init];
if (defaultHeightDictionary==nil) {
defaultHeightDictionary = [NSMutableDictionary dictionary];
defaultBoundsDictionary = [NSMutableDictionary dictionary];
}
[defaultHeightDictionary setValue:#(typical.bounds.size.height) forKey:NSStringFromClass([self class])];
CGRect bounds = typical.bounds;
NSValue * boundsValue = [NSValue valueWithCGRect:bounds];
[defaultHeightDictionary setValue:boundsValue forKey:NSStringFromClass([self class])];
}
+(CGFloat) defaultHeight
{
NSNumber * result = [defaultHeightDictionary valueForKey:NSStringFromClass([self class])];
return result.floatValue;
}
+(CGRect) defaultBounds
{
NSValue * result = [defaultBoundsDictionary valueForKey:NSStringFromClass([self class])];
return [result CGRectValue];
}
I want to insert this in both BGBaseOfAllUIControl.m and BGBaseTableViewCell.m. So I just did that awkwardly:
#interface BGBaseOfAllUIControl ()
#property (strong, nonatomic) IBOutlet UIView *view;
#end
#implementation BGBaseOfAllUIControl
#import "BGBaseSnippetCode.h"
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self BaseInitialize];
}
#interface BGBaseTableViewCell ()
#property (strong, nonatomic) IBOutlet UITableViewCell *view;
#end
#implementation BGBaseTableViewCell
//static BOOL isDefaultHeightSet = NO;
#import "BGBaseSnippetCode.h"
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self BaseInitialize];
}
return self;
Basically both BGBaseOfAllUIControl and BGBaseTableViewCell share the same protocol and I want the exact same code implement the protocol for both side. The BGBaseTableViewCell is a subclass of UITableViewCell and BGBaseOfAllUIControl is a subclass of UIControl.
So I am using .h files to include some implementation. Codes work fine. Just awkward. What would be a better way to do this, or am I doing this right?
I would forget all the header imports and static dictionary trickery and introduce a separate class that would take care of the layout information you need. Something like:
#interface BGTableLayoutInfo
- (float) defaultHeightForClass: (Class) tableViewType;
- (CGRect) defaultBoundsForClass: (Class) tableViewType;
#end
In the implementation you’d have a regular (not static) dictionary for caching the layout information for different classes. The only remaining issue is how the table view objects are going to get an instance of the layout class. One possibility is to make the layout info methods static (using a static caching dictionary), second is to keep a shared instance accessible via a +defaultLayoutInfo method.

Cannot access public variable of another class

I know this question is asked often, I've read so much on it but I still cant get it to work. Lets say I have two classes, FirstClass and SecondClass. FirstClass has a label and SecondClass wants to get the text of that label. Here is what I've done:
//FirstClass
#interface FirstClass : UIViewController
{
#public
UILabel *theLabel;
}
#property (strong, nonatomic) UILabel *theLabel;
#implementation FirstClass
#synthesize theLabel;
//SecondClass
#import "MainGameDisplay.h"
#interface SecondClass : UIViewController
{
MainGameDisplay *mainGame;
}
#property (strong, nonatomic) UILabel *theSecondLabel;
#implementation SecondClass
-(void) thisMethodIsCalled {
mainGame = [[FirstClass alloc] init];
self.theSecondLabel.text = mainGame.theLabel.text;
NSLog(#"%#",mainGame.theLabel.text); //Output is '(Null)'
}
theLabel.Text is not nil as it's being changed every second and is also displaying the label on the other controller which is running in the background whilst the SecondClass view is loaded. Could someone please point me to the write direction if I'm completely wrong, or show me some kind of example as to how this would be done. Thank you.
EDIT:
#Implementation FirstClass
#synthesize theLabel;
- (void)viewDidLoad {
[self superview];
[self startTickCount];
}
-(void) startTickCount {
timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(timeChanger) userInfo:nil repeats:YES];
}
-(void) timeChanger {
theDay++;
NSLog(#"%#",self.theLabel.text);
if (theDay <= 9)
self.theLabel.text = [NSString stringWithFormat: #"0%i", theDay];
else
self.theLabel.text = [NSString stringWithFormat: #"%i", theDay];
if (theDay > 27)
[self monthChanger];
}
That's pretty much it. The NSLog outputs the day as expected.
I assume MainGameDisplay is your FirstClass. Then in order to update theSecondLabel.text in you SecondClass object you need to pass an object of FirstClass and not to instantiate it in method call.
I guess you need to do something like this (this is a very simple example)
Add a property to your SecondClass
#property (nonatomic, strong) FirstClass *firstClass;
After that:
1) create instance of FirstClass, let it have name firstClass.
2) create instance of SecondClass. SecondClass *secondClass = [[SecondClass alloc] init];
3) set property of Second class to instance of FirstClass
secondClass.firstClass = firstClass;
4) now you have a reference to actual object of FirstClass and can access its properties.
-(void) thisMethodIsCalled {
self.theSecondLabel.text = self.firstClasss.theLabel.text;
NSLog(#"%#",mainGame.theLabel.text);
}
I hope this will help.
if you didnt leave out LOTS of code,
-(void) thisMethodIsCalled {
mainGame = [[MainGameDisplay alloc] init];
self.theSecondLabel.text = mainGame.theLabel.text;
NSLog(#"%#",mainGame.theLabel.text); //Output is '(Null)'
}
will not work.. nobody can modify mainGame in between the alloc init and the getting of .text variable....
[#all I know this is not an answer, but the formatting of comments sucks. Ill edit or delete it as needed]
If this is your code exactly, you have two problems. First, Text is unnecessarily capitalized. And, secondly, TheLabel is unnecessarily capitalized.
Edited Code:
-(void) thisMethodIsCalled {
mainGame = [[MainGameDisplay alloc] init];
// 'text' shouldn't be capitalized
// 'theLabel' shouldn't be capitalized
self.theSecondLabel.text = mainGame.theLabel.text;
NSLog(#"%#",mainGame.theLabel.text);
}
The method name is text, not Text. The case matters, and using text with a lower T will cause errors.

Array loses all values after go trough its own method - Objective C

I have this piece of code below and I'm trying to add Objects(String elements) to an array, problem is that every time I'm out its adding's method, it goes to nil, it doesn't retain the objects.
I know I'm doing wrong, even that I already tried lot of combinations and variations, even with my own constructor _MyArray etc etc, same result... it works, but not further...
Could you help me please?
#interface ArraysModel()
#property (nonatomic, retain) NSMutableArray *MyArray;
#end
#implementation ArraysModel
#synthesize MyArray;
-(void)AddObjectToTheList:(NSString *)object {
if(!MyArray) MyArray = [[NSMutableArray alloc] init];
[MyArray addObject:object];
NSLog(#"%#",self.MyArray);
NSLog(#"Object added %u",[self.MyArray count]);
}
-(NSMutableArray *)ObjectList {
return self.MyArray;
NSLog(#"%#",self.MyArray);
NSLog(#"Object added %u",[self.MyArray count]);
}
#end
The header is like this:
#interface ArraysModel : NSObject
-(void)AddObjectToTheList:(NSString *)object;
And here is my call from my ViewController:
- (IBAction)AddToTheList {
ArraysModel *MyObjectToAdd = [[ArraysModel alloc] init];
[MyObjectToAdd AddObjectToTheList:TextArea.text];
[self.view endEditing:YES];
Well, there's your problem -- you're alloc init'ing a new instance of ArraysModel, and therefore a new array with every call. You need to create a strong reference to your instance, and check for whether it exits, and only init if it doesn't.
In the .h:
#property (strong, nonatomic) ArraysModel *myObjectToAdd;
in the .m:
-(IBAction)AddToTheList {
if (! self.myObjectToAdd) {
self.myObjectToAdd = [[ArraysModel alloc] init];
}
[self.myObjectToAdd AddObjectToTheList:TextArea.text];
[self.view endEditing:YES]
}

IOS Obj C: Initializing multiple IBOutlet instances with same values

I am having some trouble getting my head around how I can init a class and pass identical init params to multiple instances. I can get it to work with 1 outlet (instance1). But how can I also get it to work with instance 2 without re-writing the variables for each instance?
ViewController.h:
#import "CustomClass.h"
#interface MYViewController : UIViewController
#property (unsafe_unretained, nonatomic) IBOutlet CustomClass *instance1;
#property (unsafe_unretained, nonatomic) IBOutlet CustomClass *instance2;
#end
ViewController.m:
#import "CustomClass.h"
#implementation MYViewController;
#synthesize instance1, instance2;
- (void)viewDidLoad
{
[super viewDidLoad];
instance1.variable1 = option1;
instance1.variable2 = 4.5;
instance1.variable3 = instance1.value;
[instance1 addTarget:self action:#selector(instance1DidChange) forControlEvents:UIControlEventValueChanged];
A loop with some KVC should do it for you...
#implementation MYViewController;
#synthesize instance1, instance2;
- (void)viewDidLoad
{
[super viewDidLoad];
for (int i = 0; i =< 1; i++) {
MyClass *newInstanceVariable = [[MyClass alloc] init];
newInstanceVariable.variable1 = option1;
newInstanceVariable.variable2 = 4.5;
newInstanceVariable.variable3 = instance1.value;
[newInstanceVariable addTarget:self action:#selector(instance1DidChange) forControlEvents:UIControlEventValueChanged];
instanceVariableName = [NSString stringWithFormat:#"instance%i",i];
[self valueForKey:instanceVariableName] = newInstanceVariable;
}
}
Should be pretty close to what you're looking for.
Essentially, you want to add the instances to an NSSet or NSArray and either iterate over that or use Key-Value Coding to set the values. If these outlets are conceptually a group of things rather than just a bunch of independent objects that happen to have the same initial values, you can use IBOutletCollection instead of IBOutlet to have them all kept in an array to begin with.

After setting one NSManaged object to another it returns Null

I am trying to pass the selected object in my coredata from the rootviewcontroller to the edit view. The selected object is being passed but is then becoming null after the theObject=selectedObject is being called. Anyone know what im doing wrong?
This is in the edit.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "LearningAppDelegate.h"
#interface edit : UIViewController <UITextViewDelegate, UITableViewDelegate, UITableViewDataSource, UIActionSheetDelegate>{
UITableView *tableView;
NSManagedObject *theObject;
UITextView *messageView;
}
#property(nonatomic, retain) IBOutlet UITableView *tableView;
#property(nonatomic, retain) IBOutlet UITextView *messageView;
#property(nonatomic, retain) NSManagedObject *theObject;
-(id)initWithObject:(NSManagedObject *)selectedObject;
#end
This is in the edit.m:
-(id)initWithObject:(NSManagedObject *)selectedObject {
self = [super init];
if (nil == self) {
return nil;
}
NSLog(#"selectedObject: %#", selectedObject);
NSLog(#"selecetedObject.message: %#", [[selectedObject valueForKey:#"message"] description]);
theObject=selectedObject;
NSLog(#"theObject 1: %#", theObject);
NSLog(#"theObject.message 1: %#", [[theObject valueForKey:#"message"] description]);
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
NSLog(#"theObject 2: %#", theObject);
NSLog(#"theObject.message 2: %#", [[theObject valueForKey:#"message"] description]);
messageView.text=[[theObject valueForKey:#"message"] description];
[super viewDidLoad];
}
I am actually amazed that doesn't crash for you. You're assigning the variable selectedObject into the instance variable theObject without retaining it for your own use. By accessing the instance variable directly in the assignment 'theObject=selectedObject', you're bypassing the behavior granted by the #property declaration. This means that once selectedObject is finally dealloc'd, theObject will point to garbage memory.
The correct way to do this is to put theObject = [selectedObject retain]; in the -initWithObject: method and in -viewDidLoad to access it via self.theObject rather than just theObject.
In my own usage I prefer to give instance variables names different from the actual property name to avoid confusion. For example:
#interface SomeClass : NSObject
{
#private
NSManagedObject *_theObject;
}
#property (nonatomic, retain) NSManagedObject *theObject;
...
#end
#implementation SomeClass
#synthesize theObject = _theObject
...
- (void)dealloc
{
[_theObject release], _theObject = nil;
[super dealloc];
}
#end
I only have experience with Coredata on the desktop, but the problem looks like it would be with your initWithObject method. At no point do you actually insert the new object into the managed object context. You should be using this method to make new objects:
- (id)initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context
As an example in pseudocode:
NSManagedObject *newObject = [[NSManagedObject alloc] initWithEntity:NSENTITYDESCRIPTION insertIntoManagedObjectContext:MANAGEDOBJECTCONTEXT];
[newObject setValue:#"VALUE_OF_SELECTED_OBJECT" forKey:#"APPROPRIATE_KEY"];
//REPEAT AS NECESSARY
[MANAGEDOBJECTCONTEXT save];
*Code not tested, naming conventions are ignored, etc.
The save is important. If you don't do this the object won't persist.