I'm trying to write a shortcut for initializing my custom UIView's properties, rather than write out each one line by line, but unfortunately it's not working the way I expected.
// .h file
#property (nonatomic, strong) UIView *view1;
#property (nonatomic, strong) UIView *view2;
#property (nonatomic, strong) UIView *view3;
#property (nonatomic, strong) UIView *view4;
// .m file
UIView *views[] = { self.view1, self.view2, self.view3, self.view4 };
for ( int i = 0; i < sizeof(views) / sizeof(views[0]); i++ ) {
views[i] = [[UIView alloc] initWithFrame:self.frame];
}
NSLog( #"%#", self.view1 ); // prints null
Is this just not allowed with C-style arrays?
Your initialization of views[] looks fine, but as soon as you do
views[i] = ...
you are merely overwriting the contents of the array. You are, however, not initializing self.view1, self.view2, etc. Try
NSLog(#"%#", views[0]);
to see what I mean.
I would not do it like follows, but you could probably do (untested):
UIView **views[] = { &self.view1, &self.view2, &self.view3, &self.view4 };
for (int i = 0; i < (sizeof(views) / sizeof(views[0]); ++i)
{
*views[i] = [[UIView alloc] initWithFrame: self.frame];
}
NSLog(#"%#", self.view1);
I would rather initialize them one by one, without the array. I don't see any advantage in using an array and a loop.
You are just changing the pointer inside the array, the self.view1 pointer will still point to nil.
You could have an array of views as property:
#property(nonatomic,strong) UIView **views;
Or maybe you can use Key Value Coding like
for(int i=1;i<5;i++){
NSString* key = [NSString stringWithFormat:#"view%i",i];
UIView *view = //initialize view
[self setValue:view forKey:key];
}
Calling e.g. self.view1 at the beginning will give you nil before you have initialized it. So the line
UIView *views[] = { self.view1, self.view2, self.view3, self.view4 };
is effectively creating an array of 4 nils.
Inserting the new instances of UIView to the array afterwards has no effect on the properties. In fact, a property is only a pair of selectors (e.g. view1 and setView1:) and by itself cannot be used reliably with C-style pointers.
You could use the backing ivars with an array of UIView ** pointers, but you're most likely better off rethinking the whole approach.
Related
I've got a UIView in a viewController:
myView = [[rhythmUIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
[self.view addSubview:myView];
The UIView exposes an NSArray in its .h:
#interface rhythmUIView : UIView
#property () NSMutableArray* myHits;
#end
And instantiates and initializes it in the UIView's .m:
NSMutableArray* myHits;
myHits = [[NSMutableArray alloc] init];
To which I add objects of a custom class:
#interface hits : NSObject
#property () double hitTime;
#property () float xPosition;
#end
in the viewController.m using
hits *thisHit;
thisHit = [[hits alloc] init];
<set thisHit's properties>
[myView.myHits addObject:thisHit];
All of that works - no compile or runtime errors, but when I change the values of the thisHit object in preparation to add a new object to the myHit array, it updates the value of every object that was previously inserted using thisHit.
This seems like an attribute problem, so I added a property to the custom class:
#property (copy) NSNumber* test;
And set it with:
thisHit.test = [NSNumber numberWithFloat:arc4random()%100];
Before the addobject.
But it also modifies every row when I touch thisHit.
I didn't expect adding "copy" to the array to work:
#property (copy) NSMutableArray* myHits;
And it didn't. Same results.
I even tried adding this to the ViewControoler.m:
#property (copy) hits* thisHit;
Same results.
Tried insertObject:atIndex: instead of addObject:
Same results.
I messed around with strong and weak in desperation, but then it actually started crashing.
Finally, learning from my last post, I tried moving the instantiation of the array from the UIView.m to the UIView creation in the viewController.m:
myView.myHits = [NSMutableArray new];
I had high hopes for that one, but again, no compile or runtime errors, but it was worse. The addobject doesn't actually do anything - the nsarray.count remains at zero.
Help please?
Thanks!
but when I change the values of the thisHit object in preparation to add a new object to the myHit array, it updates the value of every object that was previously inserted
This is the problem. You should be creating a new hits object for each member of the array. When you add an object to an array, the array simply adds a pointer to that object. Hence, you are repeatedly adding the same object to the array. So every time you change that object, every object in the array appears to change.
If you only call thisHit = [[hits alloc] init]; once then there is only one hits instance, and you are adding that single instance to the array multiple times.
I have a view controller with a few buttons, labels and 2 views. One view is a background. The other is a puzzle on top of it. Everything is in perfect working order. I know how to randomly create the puzzles keeping just one background. What I want to do is hit a button and have a new puzzle with its matching background. I want all other functions on the page to stay the same. Just a new puzzle with it's matching background. Any help would be greatly appreciated.
I'll leave the details of the implementation for you to fill in.
You need to be able to correlate your views. You can do this with strictly-managed arrays or with simple composition objects or with direct relationship.
strictly-managed arrays
NSArray *puzzles = #[/*...*/];
NSArray *backgrounds = #[/*...*/];
- (IBAction)changePuzzle {
NSUInteger index = arc4random_uniform([puzzles count]);
UIView *puzzleView = puzzles[index];
UIView *backgroundView = backgrounds[index];
// Update UI with puzzleView and backgroundView
}
simple composition object
#interface MatchedView : NSObject
#property (strong) UIView *puzzle;
#property (strong) UIView *background;
#end
NSArray *matchedViews = #[/*...*/];
- (IBAction)changePuzzle {
NSUInteger index = arc4random_uniform([matchedViews count]);
UIView *puzzleView = [matchedView[index] puzzleView];
UIView *backgroundView = [matchedView[index] backgroundView];
// Update UI with puzzleView and backgroundView
}
direct relationship
#interface PuzzleView : UIView
#property (strong) UIView *backgroundView;
/* ... */
#end
NSArray *puzzleViews = #[/*...*/];
- (IBAction)changePuzzle {
NSUInteger index = arc4random_uniform([puzzleViews count]);
PuzzleView *puzzleView = puzzleViews[index];
UIView *backgroundView = [puzzleView backgroundView];
// Update UI with puzzleView and backgroundView
}
Make those IBOutlets as you see fit. You can replace the array in any of those cases with any other way that you have to look up the views you want to randomly choose among (i.e. using tag).
I have class, that have several properties, it look like this:
#interface PlaceHolder : NSObject
#property (strong, nonatomic) NSString *name;
#property (strong, nonatomic) NSString *description;
#property (strong, nonatomic) NSString *webPage;
#property (strong, nonatomic) NSNumber *latitude;
What i need is, to create an array, that hold objects of that class. Obvious, properties will not be nil and will be different each time. So, that array must have several hundreds of PlaceHolder object, and it should be possible to get information for any of that object and it properties. But, when i try to create that array, in NSLog i see that it contain only (NULL) objects. This is how i try too add object to array:
In header i wrote:
#property (strong, nonatomic) PlaceHolder *place;
Then:
self.place = [[PlaceHolder alloc]init];
self.place.name = nameString;
NSLog(#"%# name???", self.place.name);
[self.placeObjectsArray addObject:self.place];
self.place.name is not nil, and still, array is empty. Well, its not true, it not empty but, it only contains (null) objects. How to fill array with objects of my class?
Any advice would be appreciated, thanks!
UPDATED:
I init array like this -
-(id)initWithDelegate:(id)delegateObj{
...
self.placeObjectsArray = [NSMutableArray array];
...
return self;
}
UPDATED: Now, when i try to init-alloc array in same method (instead of setting #property and strong relation) i can see it in NSLog. I wonder why it won't happen when i use my array, that set as property..
You need to alloc-init your Mutable Array ;
NSMutableArray *myArray = [[NSMutableArray alloc]initWithObjects:self.place,nil];
or simply
NSMutableArray *myArray = [[NSMutableArray alloc]init];
Then you could add objects with a for loop or whatever you need, using the following :
for ( YOURINSTRUCTION )
{
[myArray addObject:YOUROBJECT]
}
I recommend to lazy instantiate the array, that way it will only get instantiated when really needed. Since you are setting the array as a property, you can override the getter method for it like this:
- (NSMutableArray *)placeObjectsArray
{
if (!_placeObjectsArray) _placeObjectsArray = [[NSMutableArray alloc] init];
return _placeObjectsArray;
}
With this, you can call [self.placeObjectsArray addObject:self.place] anywhere in your code and the array will always be initialized when needed.
I have two custom classes: FSGame and FSEvent. FSGame has an ivar that should be an NSMutableArray of FSEvent objects.
Here's FSGame.h:
#interface FSGame : NSObject
#property (strong, nonatomic) NSMutableArray *players;
#property (strong, nonatomic) NSString *startTime;
#property (strong, nonatomic) NSString *endTime;
#property (strong, nonatomic) NSMutableArray *gameEvents;
#end
And here's my FSEvent.h:
#interface FSEvent : NSObject
#property NSInteger ID;
#property NSInteger pointTo;
#end
I use
#property (strong, nonatomic) FSGame *game;
to keep an instance of FSGame in my AppDelegate. Then, in my application:didFinishLaunchingWithOptions: I create an instance of FSGame so it can be filled throughout the "game".
_game = [[FSGame alloc] init];
Then, in one of my view controllers, I alloc and init an instance of FSEvent and then attempt to add it to the .gameEvents array:
[appDelegate.game.gameEvents addObject: event];
But something seems to go wrong here because if, from another view controller, I try to get access to that object, it will be nil:
FSEvent *previousEvent = [appDelegate.game.gameEvents lastObject];
if (previousEvent == nil) {
NSLog(#"previousEvent is NIL!");
}
What am I missing here?
It's hard to say with the code that's been shown, but it's possible that you never create the mutable array gameEvents and it's nil when you try to access it or add events to it. Make sure you're creating it somewhere, probably in FSGame's -init:
_gameEvents = [NSMutableArray array];
You need to instantiate the arrays, this does NOT happen when you use
_game = [[FSGame alloc] init];
Try using a lazy instantiation in your FSGame class, add this code
-(NSMutableArray *)gameEvents{
if(!_gameEvents){
_gameEvents = [NSMutableArray array];
}
return _gameEvents;
}
And that should be enough, also make sure of accesing by self.gameEvents and not by _gameEvents!
_game.gameEvents is a pointer to a mutable array. It is not initialized, so its value is nil. There are no any mutable array to add objects to.
You can create it when needed writing this method in FSGame.m:
-(NSMutableArray*) gameEvents {
if ( ! _gameEvents ) {
_gameEvents = [NSMutableArray array];
}
return _gameEvents ;
}
Hope you can help.
I have created my own Custom UIView and I'm trying to pass in a NSMutableArray which I have setup from data from a sqlite DB. I have NSLog'd the Array from the ViewController and everything is showing correctly.
However I want to pass this NSMutableArray into my Custom UIView (it's actually a UIScrollView) so that I can do some magic. However when I do this, my NSLog show's the output as (null).
Here is my code (I've also passed a test string to help to see if it's Array specific, but it isn't):
viewcontroller.m (just shown the Custom class call - NSLog outputs the Array contents (see end of examples)
- (void)viewDidLoad
{
...
NSString *teststring = #"Testing";
NSLog(#"Subitems: %#", subitems);
SubItemView* subitemview = [[SubItemView alloc] initWithFrame:CGRectMake(150,150,0,0)];
subitemview.cvSubitems = subitems;
subitemview.teststring = teststring;
[self.view addSubview:subitemview];
}
customview.h
#import <UIKit/UIKit.h>
#class SubItemView;
#interface SubItemView : UIScrollView {
}
#property (nonatomic, retain) NSMutableArray *cvSubitems;
#property (nonatomic, retain) NSString *teststring;
#end
customview.m
#import "SubItemView.h"
#implementation SubItemView
#synthesize cvSubitems;
#synthesize teststring;
- (id)initWithFrame:(CGRect)frame
{
CGRect rect = CGRectMake(0, 0, 400, 400);
NSLog(#"Subclass Properties: %#", self.cvSubitems);
self = [super initWithFrame:rect];
if (self) {
// Initialization code
}
return self;
}
The first NSLog in the viewcontroller.m outputs:
Subitems: (
"<SubItems: 0x6894400>",
"<SubItems: 0x6894560>"
)
The second NSLog from the Custom UIScrollView outputs:
Subclass Properties: (null)
I am a bit of a newbie, so I'm obviously missing something (possibly obvious) here. I am just really struggling to pass an Array and even a simple string into a Custom class and just output it's contents via NSLog.
Any help is gratefully appreciated.
Well when your initWithFrame method is called, your cvSubitems property isn't set yet, as it is set only after your call to initWithFrame.
Try again to log your arrays value in a method that is called after your view is initialized, or provide a custom init method (e.g. initWithMyData: andFrame:) to solve this issue.
So to clarify what has already been said, you are calling out of order.
1| SubItemView* subitemview = [[SubItemView alloc] initWithFrame:CGRectMake(150,150,0,0)];
2| subitemview.cvSubitems = subitems;
3| subitemview.teststring = teststring;
On line 1 you are calling the initWithFrame: method on SubItemView
On lines 2 and 3 you are setting the ivars
The point being that you are setting the ivars (lines 2+3) after the initWithFrame: method has returned.
But you are trying to print the ivars in the initWithFrame: method
You are also trying to log the ivars before you have even assigned self which is not a good idea either
NSLog(#"Subclass Properties: %#", self.cvSubitems);
self = [super initWithFrame:rect];
To prove that they are being set you can just print from where you instantiate:
SubItemView *subitemview = [[SubItemView alloc] initWithFrame:CGRectMake(150,150,0,0)];
subitemview.cvSubitems = subitems;
subitemview.teststring = teststring;
NSLog(#"Subclass Properties: %#", subitemview.cvSubitems);