How to access content of NSMutable array from another class? - objective-c

I have a FirstViewControlloller in which I have a start and stop button. When start is pressed pictures are taken by using AVCaptureSession until stop button is pressed. These pictures are processed and information (The R-value component of RGB) from each picture is stored in an NSMutableArray called Yaxis.
Added to this View I have a ContainerView. In this view I want to continously display a graph of the information in my Yaxis-array. The problem is to reach this information.
Some parts of my code follow here:
FirstViewController.h
#property (nonatomic, strong) NSMutableArray *Yaxis;
FirstViewController.m
-(void)viewDidLoad{
Yaxis=[[NSMutableArray alloc] init];
[super viewDidLoad];
}
At another place in the code objects are continuously added to "Yaxis" as pictures are taken.
[Yaxis addObject: RGB];
This works fine since I have no problem accessing the array filled with information as long as Im in FirstViewController.
To be able to use this information to update my graph I have tried to create an object of FirstViewController in my ContainerViewController and in that way reach my Yaxis array.
ContainerViewController.m
FirstViewController * myView=[[FirstViewController alloc] init];
myView.Yaxis
When I do this Yaxis is null. As I understand it the problem is that I create a new object of FirstViewController and therefore I will not be able to reach the filled array that is actually a part of another object. So how do I get connected to the object that holds my filled Yaxis array from within my ContainerViweController class?
Access NSMutableArray from another class - Objective C
I found this question that seams to adress the same problem but I did not solve my problem using the answers from there.

You initialize the array in -(void)viewDidLoad. But that method is not called anywhere in your second code snippet. Try putting the Yaxis=[[NSMutableArray alloc] init]; line in your init method

When you manually create a new FirstViewController, this is a new object unrelated to the original, and the mutable array property is initially nil.
The new VC will not magically point to the one that's displayed. Also, the VC might get destroyed, and the array released.
You should separate the storage of this data in another object if it's shared between VCs.

Related

Adding 'data' to an NSMutable Array - Xcode

I want to add 'email' data to my NSMutableArray so every time the button is pushed it adds another email to the array, so I end up with an array of email addresses but can't seem to get it working.
I think my problem is I am reinitialising the array so clearing it out and maybe need to put the initialisation somewhere else..which I have tried.
The below code lets me add an address but each time the IBAction is called I think I am clearing the array, any pointers or help would be great
#property (strong, nonatomic) NSMutableArray *mutable;
#synthesize mutable;
- (IBAction)array:(id)sender {
mutable = [[NSMutableArray alloc]initWithObjects:self.person.email, nil];
[mutable addObject:self.person.email];
NSLog(#"ARRAY OF EMAILS %#",mutable);
}
You are right - you are initializing the array each time the array: method is called. You can change the code to initialize it only once:
- (IBAction)array:(id)sender {
if(!self.mutable) {
self.mutable = [[NSMutableArray alloc]initWithObjects:self.person.email, nil];
}
[self.mutable addObject:self.person.email];
}
As a side note - you don't have to use #synthesize for this property. Read "Adopting Modern Objective-C" for more details.
Did you use UIViewController? If yes then reset the array in viewDidAppear method.
If you are using the UIView, then create the custom method to reset the array and call from the parent viewController.

Observe changes of instance variable in Objective-c

I have a UITableView with a property(strong, nonatomic)NSMutableArray *currentContent, I also have a property(strong, nonatomic)NSMutableArray *cellHeights to keep track of the cell heights cos the user could expand or collapse each cell. The self.currentContent is set by another controller which load data from a web service, so it will change as the data load, I want to keep both of these variables in sync. As soon as currentContent is updated, I want to update cellHeights. How do I do that?
I tried:
- (void)setCurrentContent:(NSMutableArray *)currentContent{
_currentContent = currentContent;
self.cellHeights = [NSMutableArray arrayWithDefaultHeightsForCellCount:[currentContent count]];
}
But it's not working, cos it will only be set at the first time when I set currentContent, when it's empty. So self.cellHeights currently will stay empty. When there is finally value in self.currentContent, self.cellHeights was not updated.
I've done a similar thing before, with variable cell heights depending on the content from your web-service, and I'd advise that keeping an array of cell heights might not be the best idea.
What I did was to create a 'fake' cell in the viewDidLoad: method, that I use just to calculate cell heights.
Then I use the 'heightForRowAtIndexPath' method to specify how tall cell should be by populating the 'fake' cell with the data for the index path, then finding out how tall that cell is. For example:
#interface MyTableViewController()
#property (nonatomic, strong ) MyCustomTableViewCell *cellForTestingHeight;
#end
#implementation MyTableViewController
- (void)viewDidLoad {
[super viewDidLoad]
self.cellForTestingHeight = [[MyCustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *myData = [self.data objectAtIndex:indexPath.row];
self.cellForTestingHeight.viewData = myData;
return self.cellForTestingHeight.height;
}
#end
This code assumes that you've created a class called MyCustomTableViewCell which has a method to set the viewData property on it, and that after setting that property you'll be able to tell how tall that cell will be by accessing a height property.
What you need to do is observe changes to the mutable array itself, not your property which references the array. This should be straightforward but unfortunately there is a twist...
OK, let's assume there is no twist and rather than an NSMutableArray your property is of type MyObservableClass. Then to setup the observing you would do something like this (all code is pseudo - i.e. typed into the answer):
- (void)setCurrentContent:(MyObservableClass *)currentContent
{
if(_currentContent)
[_currentContent removeObserver:self forKeyPath:#"myObservableProperty"];
_currentContent = currentContent;
[_currentContent addObserver:self forKeyPath:#"myObservableProperty" ...];
}
Now whenever the currentContent is changed your code stops observing the properties of its old value and starts observing properties of its new value.
Easy, but its not quite that simple for arrays...
The twist, though KVO will inform an observer of additions, deletions and changes to a collection the NSMutableArray class doesn't issue those notifications. You need to use a proxy for the array, and such a proxy is provided by the frameworks - see for example NSArrayController and mutableArrayValueForKey:. However the second part of the twist is that the changes to the array must be done through this proxy - you can't have your client call setCurrentContent and pass any NSMutableArray if you are going to observe it, your client needs to pass in the proxy... Whether you can use this easily in your design you'll have to figure out. You'll need to read Automatic Change Notification in this document and other Apple docs to sort this out.
This sounds more complicated that it needs to be, maybe somebody else will provide a more succinct way of doing this.

My first Cocoa master-detail application: Binding difficulties

I'm writing my first master detail view in Cocoa. My data model is really simple: just an NSMutableArray that contains instances of a single class having a few NSStrings properties.
Here's what I've created so far:
A class representing the instances.
An NSMutableArray specified as a property of my app delegate to hold the class instances.
A master detail view, with an NSTable and some text fields to hold the properties of a selection instance.
An NSArrayController with a binding specifying the app delegate, and the name of the NSMutableArray property as the model key path.
Bindings between the columns of the NSTable and the NSArrayController (controller key = "arrangedObjects", and model key path of each column = a property of the class).
Bindings between the text fields of the view and the selection (controller key = "selection", and model key path of each text field = a property of the class).
A "+" button and a "-" button in the view to enable adding and removing objects.
However, I'm having two problems with this design:
(1) I can't find a good way to implement the "+" and "-" buttons. First, I bound them to the add: and remove: properties of the array controller. While this seems to work, it has a problem: my class declares an init member that initializes the NSStrings to stub values, but the array controller doesn't seem to [init] the new instances, because the new list entry has empty strings for each column.
Next, I attached them to IBActions in my app delegate that added or removed an object from the NSMutableArray. However, this feels wrong - it feels like I'm violating the model-view-controller architecture by not talking to the array controller. For example, the "-" function has to talk to the array controller to get the selected item. Also, I notice that I have to send a [didChangeValueForKey] message after altering the array - which feels like a signal that I'm doing this wrong.
(2) One of the detail subviews in my view is an NSTextView that's bound to an NSString in the selected object. It really isn't working as expected: when new text is entered, the text remains the same even if other members of the class (which should have different values) are selected. Also, the text data isn't being saved to any instance of the class.
Any thoughts? Thanks in advance.
Here is an example that should be close to what you want:
#import "AppDelegate.h"
#import "Members.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.theData = [NSMutableArray arrayWithObject:[[Members alloc] init]];
}
-(IBAction)addMember:(id)sender {
[self.controller addObject:[[Members alloc] init]];
}
-(IBAction)removeMember:(id)sender {
[self.controller removeObjectAtArrangedObjectIndex:self.controller.selectionIndex];
}
The array controller (whose IBOutlet is controller) has its content array bound to theData. Members is the name of my class that has 3 string properties, name, author and blurb. In the detail view, the 2 text field's are bound to ArrayController.selection.name or author, just like you said you did. The other property, blurb, is bound the same way (ArrayController.selection.blurb), but to the Attributed String value of a text view. This worked fine, I'm not sure what your trouble with the text view was. If I add text to the text view, it does show up there if I click another row in the mater table and then click back (also if I log the array, it shows up there too).

(Beginner)Trying to pass an array from ViewController to DetailController

I have loaded an array (recordArray) in RootViewController class which I need to pass to the DetailViewController class so that I can access any variable within it. I can pass a single value successfully, but I cannot get how to pass the whole array across.
I have created a second NSMutableArray recordArray in DetailsViewController.h, added the #property statement (with retain), and synthesized in DetailsViewController.m. I then added the following line in DetailsViewController.m
recordArray = [[NSMutableArray alloc] initWithArray:self.recordArray];
but that just gives me an empty array in the detail view.
I have read through some of the posts on this, but have not found anything which I understand sufficiently to be able to implement. I expect I'm going about this all wrong....
Thanks in advance for your help guys.
Please refer to this code:
iPhone SDK: How do I pass an array of values from a ViewController onto other ViewController?
There you can refer to Satya's answer.
Let me know if you need more help
EDIT:
Let us assume that there is an NSArray called secondArray declared in SecondViewController
Define a method in SecondViewController which says:
-(void)setValue:(NSArray *)array
{
secondArray = array;
}
Hope this helps.
The problem is that here:
recordArray = [[NSMutableArray alloc] initWithArray:self.recordArray];
You are just assigning the same property recordArray, which is probably nil at this point, what you want to do is dependency injection. So whenever you instantiate your DetailController, you can do:
myDetailController.recordArray = myRecordArray
Where myRecordArray is the value you want to pass or "inject" in the detailview controller.

Passing values from UITableView to UIView in table header

My CoreData model drives series of UITableViewControllers.
One of the detail tables has a header that contains a UIView "GraphView" that uses data from the rows of the table. (It's like iTunes having a custom header to chart song length in a given playlist.) My "playlist" populates from CoreData correctly. But how do I pass data from the TableView to the UIView in its header?
It seems wasteful and redundant to fetch and parse it again, especially when the data is sitting in a convenient mutable array called "steps". Can I just pass the mutable array to the UIView in the header? If so, how?
I've tried creating an NSArray property in the UIView's class called "passedStepsArray", and then setting it from the StepDetailViewController like so:
graphView.passedStepsArray = [[NSArray alloc] initWithArray:steps copyItems:YES];
But back in the GraphView class, it remains NULL. No value gets passed.
Any ideas?
From my visualization of the problem, it seems you are on the right tracks by having an NSArray property in the GraphView. You probably don't need to copy the existing array using the initWithArray:copyItems: method, just pass a non-mutable copy of the populated steps array:
graphView.passedStepsArray = [[steps copy] autorelease];
This is assuming that the graphView passedStepsArray property is set to retain. (Then don't forget to set self.passedStepsArray = nil; in the graphView dealloc methood.)