I am using this in a UINavigation environment.
I have customClassA. It inherits customClassB and one of its object is a NSMutableDictionary.
I alloc and init customClassA in a viewController, then for adding data, I am pushing a new viewController into the stack. The addNewDataViewController sends the newly added data, a customClassB object back by its delegate. Everything works fine so far.
customClassA has to store the returned object (customClassB) into its NSMutableDictionary object with a key (an NSString created from NSDate).
I get "mutating method sent to immutable object" error and can't think of any solution.
Any help is appreciated.
Thanks.
===========================
interface customClassA : NSObject
{
NSDate date;
NSArray *array; // will contain only NSString objects
}
// and the rest as customary
...
#import "customClassA.h"
interface customClassB : NSObject
{
NSString *title;
NSMutableDictionary *data; // will contain values of customClassA with keys of NSString
}
// and the rest as customary
...
#import "customClassB"
#interface firstViewController : UITableViewController <SecondViewControllerDelegate>
- (void)viewDidLoad
{
self.customClassB_Object = [customClassB alloc] init];
// and the rest...
}
- (void)secondViewControllerDidSaveData:(customClassA *)aData
{
[self.customClassB_Object.data setObject:aData forKey:[NSString stringWithFormat:#"%#", aData.date]];
// update tableView
}
Make sure you are initializing the NSMutableDictionary with something like
NSMutableDictionary *myDictionary = [[NSMutableDictionary alloc] init];
It would appear that your NSMutableDictionary is getting created with an NSDictionary instance instead of a NSMutableDictionary
Althoguh I added the following code to customClassB implementation, it still didn't work.
#implementation customClassB
- (id)init
{
self = [super init];
if (self)
self.data = [NSMutableDictionary alloc] init];
return self;
}
so I added two custom methods to my customClassB implementation, as well as in the header file:
- (void)appendData:(customClassA *)aData;
- (void)removeDataWithKey:(NSString *)aKey;
and instead of manipulating the data dicionary of customClassB in my viewController, I simply call that method and pass the data object to the class and it did the trick.
Related
In Cococa,
Can I link input fields (say a textField) to my array controller so that a new object is added with attributes already populated?
Thanks
Try the below code as it is done for one textfield:-
Header File
#import <Cocoa/Cocoa.h>
#interface testWindowController : NSWindowController
{
IBOutlet NSArrayController *arrayController;
NSMutableArray *array;
}
#property(readwrite,retain)NSMutableArray *array;
#end
Implememntation file:-
#import "testWindowController.h"
#interface testWindowController ()
#end
#implementation testWindowController
#synthesize array;
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
- (void)windowDidLoad
{
NSString *str=#"testValue";
self.array=[[NSMutableArray alloc]init];
NSMutableDictionary *dict=[NSMutableDictionary dictionary];
[dict setObject:str forKey:#"valueText"];
[self.array addObject:dict];
[self setArray:self.array];
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
-(NSString *)windowNibName
{
return #"testWindowController";
}
#end
XIB file:-
Follow below steps:-
1) In this file take one arraycontroller and textfield and then connect your arraycontroller to FileOwner.
2) Then in Binding Inspector bind arraycontroller to FileOwners and mention model key path as array.
3) Now bind your textfield to arraycontroller and model key path as "key:" whose name is valueText.
I'm having a problem with storing and accessing objects with NSmutable array in app delegate. I have tried methods form other websites and stack overlay pages but yet no solution. I want to able to access the array data in another view. Currently nothing is working for me.
Heres my code.
AppDelegate.h :
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
NSMutableArray* sharedArray;
}
#property (nonatomic, retain) NSMutableArray* sharedArray;
ViewController.h :
#import "AppDelegate.h"
-(void)viewDidLoad{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableArray *model = appDelegate.sharedArray;
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:#"hello" forKey:#"title"];
[dict setObject:#"urlhere" forKey:#"thumbnail"];
[model addObject:dict];
NSLog(#"submitted to array: %#",model);
}
Are you, at any point, initializing the sharedArray? The array must be instantiated before you can add objects to it. For example:
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
self.sharedArray = [NSMutableArray array];
return YES;
}
Having done that, now attempts to add objects to this array from your view controllers should succeed.
Unrelated, but you should not define instance variables for your properties. Let the compiler synthesize that for you, e.g.:
AppDelegate.h:
#interface AppDelegate : UIResponder <UIApplicationDelegate>
// {
// NSMutableArray* sharedArray;
// }
#property (nonatomic, retain) NSMutableArray* sharedArray;
#end
What you have is technically acceptable, but it's inadvisable because of possible confusion between this sharedArray instance variable and the what the compiler will synthesize for you (e.g. if you don't have a #synthesize line, the compiler will automatically create an instance variable called _sharedArray, with a leading underscore, for you). Even if you had a #synthesize line that ensured that the instance variable was correct, having the explicitly declared instance variable is simply redundant.
I have a ViewController with UITableView *myTable and MKMapView *myMap designed in xib, but the table delegate/datasource and the map delegate are in another class, named SubClass. When I press a button in ViewController the SubClass parse in the tablecells latitude and longitude from a xml remote file, and now I want to zoom myMap into this coordinates every time I select the rows of myTable: Well, I can't find a way to call this zoom FROM SubClass. This is, simplified, my code:
ViewController.h
// ViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import "SubClass.h"
#interface ViewController : UIViewController {
IBOutlet UITableView *myTable;
IBOutlet MKMapView *myMap;
SubClass *subClassIstance;
}
- (void)buttonPressed:(id)sender
#property (nonatomic, retain) IBOutlet MKMapView *myMap;
ViewController.m
// in ViewController.m
- (void)buttonPressed:(id)sender {
subClassIstance = [[SubClass alloc] init];
myTable.delegate = SubClass;
myTable.dataSource = SubClass;
[myTable reloadData];
subClassIstance = [[SubClass alloc] loadMap:myMap];
}
SubClass.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface SubClass : NSObject <UITableViewDataSource, UITableViewDelegate, MKMapViewDelegate> {
}
- (void)loadValues;
- (id)loadMap:(MKMapView *)mapView;
- (id)zoomTheMap:(NSMutableString *)string1 :(NSMutableString *)string2 :(MKMapView *)mapView; // IS IT RIGHT???
SubClass.m
- (id)init{
self = [super init];
if ( self != nil ) {
[self loadValues];
}
return self;
}
- (void)loadValues {
// CODE TO PARSE VALUES OF LONGITUDE AND LATITUDE TO PASS IN THE TABLE CELLS
latitudeFromLoadValues = // NSMutableString parsed value from a xml remote file
longitudeFromLoadValues = // NSMutableStringparsed value from a xml remote file
}
- (id)loadMap:(MKMapView *)mapView
{
if (self) {
mapView.delegate = self; // CODE TO LOAD ANNOTATIONS AND OTHER STUFF. IT WORKS!
}
return self;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
latitudeFromLoadValues = [dataParsed objectAtIndex:indexPath.row];
longitudeFromLoadValues = [data2Parsed objectAtIndex:indexPath.row];
[self zoomTheMap:latitudeFromLoadValues :longitudefromLoadValues :???]; // IS IT CORRECT? WHAT IS THE RIGHT *MKMAPVIEW?
}
- (id)zoomTheMap:(NSMutableString *)string1 :(NSMutableString *)string2 :(MKMapView *)mapView {
NSLog(#"%#",string1);
NSLog(#"%#",string2);
MKCoordinateRegion region;
region.center.latitude = [string1 floatValue];
region.center.longitude = [string2 floatValue];
region.span.latitudeDelta = 2.0;
region.span.longitudeDelta = 2.0;
// I KNOW, I HAVE TO CALL myMap from ViewController! But with an istance?
mapView.delegate = self;
mapView.region = region;
return self;
}
Well, the rest of code works! I can see *myMap in ViewController loaded with some annotations declared in SubClass and *myTable loaded with cells populated with latitude and longitude parsed in SubClass; I can also see correct longitude and latitude passed in string1 and string2 but when I select the single table cell I don't see myMap zooming, I think I am using the wrong method. Can U help me, please?
loadMap shouldn't return self, only init methods should do that.
In buttonPressed you allocate a new SubClass, do some stuff to it, then allocate another SubClass and call its loadMap function. The last line should be [subClassIstance loadMap:myMap], but you'll also want to reconsider allocating a new SubClass every time that button is pressed.
I think you're really going about this the wrong way. Why do you need a SubClass (terrible name BTW, it says nothing about what it is for)? What class does it extend? If the ViewController has the MKMapView, it is usually the one to issue commands to the map. I can understand you having a separate datasoucre for the tableview, but not the rest. If you make the VC its own table and map delegate you'll simplify things a lot.
If you really want to have a subclass in your code then you should be calling loadMap on the instance you created on the first line of buttonPressed
- (void)buttonPressed:(id)sender {
subClassIstance = [[SubClass alloc] init];
myTable.delegate = SubClass;
myTable.dataSource = SubClass;
[myTable reloadData];
[subClassIstance loadMap:myMap];
}
and your loadMap would look like
- (void)loadMap:(MKMapView *)mapView
{
mapView.delegate = self;
}
However if that's all loadMap does you don't need a function for that, you could just make buttonPressed do it all.
- (void)buttonPressed:(id)sender {
subClassIstance = [[SubClass alloc] init];
myTable.delegate = SubClass;
myTable.dataSource = SubClass;
[myTable reloadData];
myMap.delegate = subClassIstance;
}
Example init function:
- (id)initiWithMapView: (MKMapView)* mapView
{
self = [super init];
if(self)
{
theMap = mapView;
theMap.delegate = self;
[self loadValues];
....
}
return self;
}
If you use this you won't have to set the map delegate or return self all the time and you can use theMap (as declared in your answer) in every function.
Well, I have found a simply solution, for those interested: first, I have defined a generic MKMapView *theMap in my SubClass.h, that now looks like:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface SubClass : NSObject <UITableViewDataSource, UITableViewDelegate,
MKMapViewDelegate> {
MKMapView *theMap // NEW CODE!!!
}
- (void)loadValues;
- (id)loadMap:(MKMapView *)mapView;
- (id)zoomTheMap:(NSMutableString *)string1 :(NSMutableString *)string2 :(MKMapView *)mapView;
In loadMap method I have compared *theMap to mapView called by the SubClassIstance in VC (my *myMap that I want to zoom), so now we have:
- (id)loadMap:(MKMapView *)mapView
{
if (self) {
mapView.delegate = self;
theMap = mapView; // NEW CODE !!!
// CODE TO LOAD ANNOTATIONS AND OTHER STUFF. IT WORKS!
}
return self;
}
In didSelectRowAtIndexPath I have passed *theMap as mapView argument of zoomTheMap method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
latitudeFromLoadValues = [dataParsed objectAtIndex:indexPath.row];
longitudeFromLoadValues = [data2Parsed objectAtIndex:indexPath.row];
[self zoomTheMap:latitudeFromLoadValues :longitudefromLoadValues :theMap]; // NEW CODE !!!
}
The zoomTheMap method doesn't change, and now, "magically", every time I press a row of my table, the *myMap designed in the VC xib (but with delegate in SubClass) zooms into the coordinates stored in the cells:
- (id)zoomTheMap:(NSMutableString *)string1 :(NSMutableString *)string2 :(MKMapView *)mapView {
MKCoordinateRegion region;
region.center.latitude = [string1 floatValue];
region.center.longitude = [string2 floatValue];
region.span.latitudeDelta = 2.0;
region.span.longitudeDelta = 2.0;
mapView.delegate = self; // here mapView is *theMap passed in didSelectRowAtIndexPath, AKA original mapView istance used to delegate *myMap in VC
[mapView setRegion:region animated:YES];
return self;
}
Maybe its not an "elegant" way, but it now works! ;=)
I have the problem that I can't get the Data from one of my classes to the other...
To do this I created this method in the class I am initializing (angebotPage):
- (id) initWithDictionary:(NSDictionary *)dictionary
{
self = [super init];
if (self) {
dict = [dictionary retain];
}
return self;
}
The call from the other class looks like this:
angebotPage *page;
angebotPDF = [[PDFDocument alloc] init];
page = [[angebotPage alloc] initWithDictionary:dictionary];
The Error I get is EXC_BAD_ACCESS in the line where I do:
dict = [dictionary retain];
But why? I need to retain it cause I will use it for the next program steps.. But without retaining I can't use it (EXC_BAD_ACCESS comes elsewhere...)
I recommend that you use a property for dict instead.
#property (retain) NSDictionary *dict;
And then assign the dictionary with self.dict = dictionary. This is assuming all the objects in dictionary are correctly retained in the first place. Try with an empty dictionary if in doubt.
I need some help trying to understand KVO on a complex hierarchy of objects. Let me set the scenario. The MyClass object has a mutable array property that contains MyPerson objects. I want to observe changes in the myPeople property of MyClass. Furthermore I would like to observe all the properties contained in the MyPerson object as well. Here are the class definitions.
#interface MyClass:NSObject
{
NSMutableArray *myPeople;
}
#property(nonatomic, retain)NSMutableArray *myArray;
#end
Here is the MyPerson object,
#interface MyPerson:NSObject
{
NSString *myName;
NSString *myLastName;
}
#property(nonatomic, retain)NSString *myName;
#property(nonatomic, retain)NSString *myLastName;
#end
Is it correct to observe the properties that I'm interested in the following manner?
MyClass *myClass = [[MyClass alloc] init]; //myPeople is filled with myPerson objects
MySchool *mySchool = [[MySchool alloc] init];
[myClass addObserver:mySchool
forKeyPath:#"myPeople"
options:NSKeyValueObservingOptionNew
context:NULL];
[myClass addObserver:mySchool
forKeyPath:#"myPeople.myName"
options:NSKeyValueObservingOptionNew
context:NULL]; //I am unsure about this one
[myClass addObserver:mySchool
forKeyPath:#"myPeople.myLastName"
options:NSKeyValueObservingOptionNew
context:NULL]; //I am unsure about this one
No, it's not correct. You would have to observe the properties for any object you add to the array separately. So whenever an object is added to or removed from the array, you would have to add/remove your observer to/from the added/removed object(s).