Displaying an UIImageView property from another class programmatically - objective-c

Well, here's the situation:
I've got...
a custom class with an UIImageView-property, let's call it the Enemy-class
a ViewController
a NSMutableArray to help create multiple instances of Enemy (called enemies)
and what I want:
be able to create an unlimited amount of Enemy-Instances through a method in my ViewController (like [self spawnEnemy];, where self is the ViewController)
and, subsequently, display the UIImageView property (let's call it "enemyImage") on the view that is controlled by my ViewController
I've tried something like this:
-(Enemy *) spawnEnemy
{
Enemy *tempEnemy = [Enemy new];
[enemies addObject:(Enemy*)tempEnemy];
[self.view addSubview:(UIImageView*)[[enemies objectAtIndex:[enemies count]] enemyImage]];
//randomLocation is declared in the Enemy-Class and just assigns a random
//CGPoint to self.enemyImage.center
[[enemies objectAtIndex:[enemies count]] randomLocation];
return [[enemies objectAtIndex:[enemies count]]createEnemy];
}
This runs without errors, randomLocation gets called (tried with NSLog), AND if I do something like this in another Method of ViewController:
[[self spawnEnemy] enemyTestMethod];
enemyTestMethod is being executed as well.
But still, no enemieViews are displayed on the screen...
What am I doing wrong?
Thank you so much for your help and time.
==== Edit ====
Here's the relevant code from Enemy.h/Enemy.m:
#interface Enemy : NSObject
{
UIImageView *enemyImage;
}
#property (nonatomic, retain) IBOutlet UIImageView *enemyImage;
-(Enemy*) createEnemy;
//Enemy.m
#implementation Enemy
#synthesize enemyImage, speed;
-(Enemy *) createEnemy
{
self.enemyImage = [[UIImageView alloc] init];
[self.enemyImage setImage:[UIImage imageNamed:#"enemy.png"]];
return self;
}
I also corrected the last line in the spawnEnemy-Method to properly send createEnemy.

You don't include the code in Enemy where you alloc/init the UIImageView property. Unless this code explicitly specifies a CGRect with the size and origin that you want, the view will be initialized with CGRectZero, which means even if you're correctly adding the subview (and it looks like you are) you still won't see it anywhere.
Post the Enemy code, and the problem will probably be immediately apparent.

Have you called -createEnemy before you added them to your view?
OK, you've got this checked.
Then maybe you should check as #MusiGenesis suggested.
To do this, you need to inspect the properties of your enemyImage.
You can do this in either of the following ways:
print its frame.size by:
NSLog(#"enemyImage frame size: %#", NSStringFromCGRect(enemy.enemyImage));
set a breakpoint where you feel great, and check with your debugger:
p (CGRect)[[enemy enemyImage] frame]

Related

Memory keep increasing even though I set it to nil (screenshot enclosed)

In my ParentViewController I type this:
- (void)updateView:(NSInteger)_index article:(AObject *)aObject {
UIView *aUIView = [someUIViews objectAtIndex:_index];
// clear view
for (UIView *view in aUIView.subviews) {
[view removeFromSuperview];
}
// create view
ChildViewController *cvc = [[ChildViewController alloc] init:aObject context:managedObjectContext];
cvc.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
[self addChildViewController:cvc];
[aUIView addSubview:cvc.view];
cvc = nil;
}
In ChildViewController I type this:
.h file
#interface ChildViewController : UIViewController <UIWebViewDelegate> {
AObject *aObject;
UIWebView *webView;
}
.m file
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:(BOOL)animated];
webView.delegate = nil;
webView = nil;
aObject = nil;
}
Remark:
I have to use addChildViewController because I use UIWebViewDelegate in ChildViewController. If I do not use addChildViewController, I cannot retain the ViewController and functions of UIWebViewDelegate cannot be called.
I use ARC
Program Objective:
There is a scrolling view implementing by UIScrollView and everytime I scroll the screen to another page, function updateView will be called.
After I scroll to another page, the previous page will be disappeared so viewWillDisappear in ChildViewController will be called.
Screenshot:
http://postimage.org/image/mzmksm9d5/
I cannot post image directly because of low reputation here.
Based on the figure, ParentViewController is called at about the 4th second. Then the memory keep increasing rapidly without objects released. At about the 30th second, ParentViewController is gone away by clicking the back button on the navigation bar. Memory is dropped a bit but still somethings are remaining.
Goal:
What I wish to do is that the object of ChildViewController can be destroyed when viewWillDisappear is called and it is no longer used. So I expect the memory would always drop after it increased a bit. Otherwise, the memory will increase proportional to scrolling times and crash until memory is fully used.
Thanks in advance for any help you are able to provide.
Not sure if this will solve the problem, but try removing cvc.view explicitly from parent view.
Also, you are creating ChildViewController, but you are not releasing it. By calling init you increased its retain count, but you are not decreasing it anywhere. Like this (if you do not have some more code), your ChildViewController will not be released since retain count will never be 0 again.
ChildViewController *cvc = [[ChildViewController alloc] init:aObject context:managedObjectContext];
cvc.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
[self addChildViewController:cvc];
[aUIView addSubview:cvc.view];
cvc = nil;

Assignment of an ivar not working

I'm seeing a bizarre case where simple assignment to an instance variable is not working. The right hand object is non-nil, but refuses to be assigned to an instance variable (with the same type). This is not happening consistently within the app.
I have a class like this:
#interface FooViewController : UIViewController {
UIView *contentView;
BOOL testBool;
}
#end
#implementation FooViewController
- (void)viewDidLoad {
UIView *theContentView = [[UIView alloc] initWithFrame:self.view.bounds];
// this assignment fails for some reason:
contentView = [theContentView retain];
/* at this point, contentView == nil */
UIView *bar = theContentView; // this works
testBool = YES; // this works
}
#end
and a subclass like this:
#interface BarViewController : FooViewController {
}
#end
When I instantiate the BarViewController from a nib, when the execution gets to the viewDidLoad defined in the superclass, the strange behavior above happens. Instance variables "refuse to assign".
However, if I add a dummy instance variable to the subclass, the expected behavior returns. Instance variables correctly assign in the viewDidLoad.
I'm seeing this at runtime in iOS 4.3 in the simulator using Xcode 4.0.2. This happens both in LLVM+GCC and GCC.
This is not happening in isolation. When I try this in a test project, it performs normally.
I'm sorry, but I don't believe you.
My first question is how you determine that contentView is equal to nil? Do you have a breakpoint after the assignment? If you break on the line of the assignment, that line will not yet have been executed, so contentView will be nil at that point.
If you're testing contentView in some other method it might just be that viewDidLoad has not been run yet.
Show me the output of:
- (void)viewDidLoad {
UIView *theContentView = [[UIView alloc] initWithFrame:self.view.bounds];
// this assignment fails for some reason:
contentView = [theContentView retain];
/* at this point, contentView == nil */
NSLog(#"theContentView: %p", theContentView);
NSLog(#"contentView: %p", contentView);
UIView *bar = theContentView; // this works
testBool = YES; // this works
}
If they differ I'll start believing you. (Well, maybe not, I'll still consider the possibility that you're lying.)

Reference CustomView from my Controller

This is driving me crazy. Very grateful if someone could help me out!
Problem:
I have subclassed NSView (and implemented initWithRect: and drawRect:) and connected it to a customView in IB. Then in my Controller.h I am trying to create a reference to this instance by using Viewer *view; (Viewer is my subclass of NSView). However, when I try to reach a dummy function that only performs printf("something") nothing happens. Since I haven't allocated any memory for this instance [view retainCount] gives 0. My understanding was that IB would instantiate this class for me. The reason that I want to be able to reference the instance is so that I can call [view setNeedsDisplay: YES] so that the view will be redrawn. I have connected my CustomView with the view outlet in IB and saved.
#import "Viewer.h"
#import "Controller.h"
#implementation Viewer
- (id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
return self;
}
-(void)awakeFromNib
{
printf("awake!\n"); //works!
}
- (void)drawRect:(NSRect)rect
{
CGContextRef myContext = [[NSGraphicsContext currentContext]graphicsPort];
for (int i=0; i<8; i++) {
for (int j=0; j<8; j++) {
printf("%f\n",gPopulation[i][j]/2);
CGContextSetRGBFillColor (myContext, gPopulation[i][j]/2, 0.3, 0.1, 1); // Set color
CGContextFillRect (myContext, CGRectMake (i*50, j*50, 50, 50 ));
}
}
}
**- (void) redraw { //dummy function that I can't reach from controller with [view redraw]. Gives no error, but retainCount = 0**
printf("redraw------\n");
//[self display];
}
#end
#import <Cocoa/Cocoa.h>
#import "Viewer.h"
double gPopulation[8][8];
#interface Controller : NSObject {
NSMutableArray *emptySpots;
int nEmpty, nWhite, nBlack;
NSOperationQueue *queue;
IBOutlet Viewer *view;
}
- (void) main;
- (id) initWithMain;
- (void) updatePopulation;
- (void) initPopulation;
#end
The steps you describe aren't entirely clear, but here are several things that stand out:
1 - It's not your place to ask an object for its -retainCount to determine whether it's being used or not. You have no way of knowing (nor are you supposed to know or depend upon knowing) what else might have an interest in this object.
2 - You check for a valid object by seeing if the object pointer ("view" in your case) is valid (points to an object) or is nil.
3 - When creating a custom NSView subclass and instantiating a copy within your nib/xib, you need to drag an NSView instance out from the library, then set its class name to that of your subclass, otherwise Interface Builder is just creating an instance of NSView. I don't think this is your problem (see #4) but you didn't say this so it's another thing to check.
4 - When you send a message to nil, nothing is exactly what is supposed to happen, so it's likely your "view" pointer/outlet is nil.
5 - It's easy to confuse "an instance I created and referenced in a nib/xib" with "an instance I created at runtime". This happens frequently with those new to Cocoa. Are you absolutely positive that the instance of the object that holds the connection (named "view") is the same as the instance you're examining at runtime? For example, you create a controller class named MyController, instantiated it in your nib/xib (as a blue cube), wired it up, etc. Then at runtime, you instantiate a new MyController ([[MyController alloc] init]...) and tried to access its (nil) "view" outlet, which points to nothing because it's not the same instance as that in your nib/xib.

About drawing using quartz 2D on iPhone

I have a view, that have a drawRect method, I know that this method is the only way I control the View to draw something on it. So, I try to my drawing logic in this way:
- (void)drawRect:(CGRect)rect {
//my drawing code...
}
In my view, I use the IB to link this class.
[myView setNeedsDisplay];
It works, so, I designed to have a Command object inside the drawRect method, so that I can drawing dynamically based on my Cmd. Here is the code in the View after I modified:
- (void)drawRect:(CGRect)rect {
self.cmdToBeExecuted = [[DrawingSomethingCmd alloc] init];
[self.cmdToBeExecuted execute];
}
My DrawingsomthingCmd:
#implementation DrawingSomethingCmd
-(void)execute{
//my drawing code;
}
It works too. But the question is, how can I assign the self.cmdToBeExecuted dynamically.
Also, I changed my drawRect like this:
- (void)drawRect:(CGRect)rect {
[self.cmdToBeExecuted execute];
}
Because I have this to link with the IB,
IBOutlet myDrawingView *myView;
but after I type [myView ... ...], it don't allow me to get the variable cmdToBeExecuted. I ready make my variable accessible in .h:
#property (nonatomic, retain) Command *cmdToBeExecuted;
and .m also:
#synthesize cmdToBeExecuted;
Don't initialize the command inside the drawing rect. Initialize a default (maybe in viewDidLoad? it depends on what you are doing) somewhere as the view is created and then update them dynamically however you are doing this, whenever the need arises. So:
- (void)drawRect:(CGRect)rect
{
[[self commandToBeExecuted] execute];
}
and elsewhere:
// dynamically update the drawing
[myView setCommandToBeExected:[[[DrawingSomethingCommand alloc] init] autorelease]];

Setting a ViewController's properties after instantiation

I'm creating an instance of a viewController, and then trying to set the text on of it's properties, a UILabel.
BoyController *boyViewController = [[BoyController alloc] initWithNibName:#"BoyView" bundle:nil];
NSString *newText = [astrology getSignWithMonth:month withDay:day];
boyViewController.sign.text = newText;
NSLog(#" the boyviewcontroller.sign.text is now set to: %#", boyViewController.sign.text);
[newText release];
I tried this, but it didn't work...
So I tried the following:
BoyController *boyViewController = [[BoyController alloc] initWithNibName:#"BoyView" bundle:nil];
UILabel *newUILabel = [[UILabel alloc] init];
newUILabel.text = [astrology getSignWithMonth:month withDay:day];
boyViewController.sign = newUILabel;
NSLog(#" the boyviewcontroller.sign.text is now set to: %#", newUILabel.text);
[newUILabel release];
But no avail..
I'm not sure why I can't set the text property of the UILabel "sign" in boyViewController..
The problem here is that the initializer does not actually load the nib file into memory. Instead, loading the nib is delayed until your application requests the view controller's view property. As such, your controller's sign property is null when you access it.
Manually requesting the controller's view property would make your example work...
BoyController *boyViewController = [[BoyController alloc] initWithNibName:#"BoyView" bundle:nil];
[boyViewController view]; // !!!: Calling [... view] here forces the nib to load.
NSString *newText = [astrology getSignWithMonth:month withDay:day];
boyViewController.sign.text = newText;
// and so on...
However, I'd guess that what you're really trying to do is create and configure your view controller before setting it free to do it's own thing. (Perhaps to display it modally, say.) Calling [... view] manually is not going to be a long-term solution.
Better is to set a separate property on your view controller for the label text and then implement viewDidLoad to assign it to the label:
#interface BoyViewController : UIViewController {
IBOutlet UILabel *label;
NSString *labelText;
}
#property(nonatomic, copy)NSString *labelText;
#end
#implementation
#synthesize labelText;
- (void)viewDidLoad
{
[label setText:[self labelText]];
}
// and so on...
#end
This has the added benefit of your label text being reset in case the view is purged during a low memory event.
Did you bind your outlets at Interface Builder?
It seems that you need to bind sign outlet of the first example into Interface Builder in order to actually set that text to whatever you want.
Once you bind your outlet to the actual UI component at Interface Builder, then you should be able to do something like:
NSString *newText = [astrology getSignWithMonth:month withDay:day];
[[boyViewController sign] setText:newText];
This is what you need to know about binding.
Your second example does not make sense at all to me.