I'm trying to create a subclass object, whose size can be determined when I declare it. For example, do something near to "circle (int width, int height)", and in the C4WorkSpace, attribute two numbers that define the size of the circle. If I understood correctly, you can use initializers for that, like this one:
- (id) initWithNumber: (int) n {
self = [super init]; ❶ ❷
if (self) {
self->_number = n; ❸
}
return self; ❹
}
...but I didn't quite understand how to use it and where to put it.
Here is the code I'm working with. I inserted "size" into the parameters of the ellipse, just to illustrate what I'm trying to do.
My circle.h file:
#import "C4Shape.h"
#interface circle : C4Shape
#end
And the circle.m one:
#import "circle.h"
#implementation circle
-(void) setup
{
[self addGesture:PAN name:#"pan" action:#"move:"];
[self addGesture:TAP name:#"tap" action:#"changeColour"];
[self ellipse:CGRectMake(0, 0, size, size)];
[self setFillColor:[UIColor blackColor]];
[self setStrokeColor:[UIColor blackColor]];
}
-(void) changeColour
{
self.fillColor = [UIColor colorWithRed:[C4Math randomInt: 100]/100.0f green:[C4Math randomInt: 100]/100.0f blue:[C4Math randomInt: 100]/100.0f alpha:1.0f];
}
#end
How is the best way to attribute a variable to a subclass in C4, in this case? If possible, could you explain how I create the object in the C4WorkSpace.m too?
Thanks for your attention. And sorry if I was not clear.
You can do this. You have to declare your initializer method into the header file in order for other files to see it. You'll need to create an instance variable called size and set it to your number. Alternatively, you could use a property. You provide the definition in your Cirlce.m file. I've changed self->size to be just size, since it is an instance variable in your class.
In your C4Workspace.m you'll need to import the header file, then you'll be able to create one of your objects anywhere in the file. You'll need to call alloc and then your initWithNumber in order to create the object. You'll have to call setup in order to get it to show up on the screen, since that is where you've provided all of your code.
Check out C4: Add panning to an object other than "self" for a related discussion.
Circle.h
#import "C4Shape.h"
#interface Circle : C4Shape
- (id) initWithNumber: (int) n;
#end
Circle.m
#import "Circle.h"
#implementation Circle
{
int size;
}
- (id) initWithNumber: (int) n {
self = [super init];
if (self) {
size = n;
}
return self;
}
-(void) setup
{
[self addGesture:PAN name:#"pan" action:#"move:"];
[self addGesture:TAP name:#"tap" action:#"changeColour"];
[self ellipse:CGRectMake(0, 0, size, size)];
[self setFillColor:[UIColor blackColor]];
[self setStrokeColor:[UIColor blackColor]];
}
-(void) changeColour
{
self.fillColor = [UIColor colorWithRed:[C4Math randomInt: 100]/100.0f green:[C4Math randomInt: 100]/100.0f blue:[C4Math randomInt: 100]/100.0f alpha:1.0f];
}
#end
C4Workspace.m
#import "C4Shape.h"
#import "C4WorkSpace.h"
#import "Circle.h"
#implementation C4WorkSpace
{
Circle * c;
}
-(void)setup
{
c = [[Circle alloc] initWithNumber:100];
[c setup];
[self.canvas addSubview:c];
}
#end
Related
I have requirement in which I have tableview as a subview of imageview. Now when the user zooms-in , the text inside the tableview is getting blur. Is there any way to manipulate it at time of zoom-in?
Please follow the steps which is below.
Don't set the position of image statically i mean by giving coordinate.Set the position dynamically or at the run time it will take position based on current window or zoom in zoom out position label,i am not pretty much sure because i havn't faced this situation earlier but as i think it might be use case scenario for solving your problem.
Thanks
I have achieved it by subclassing CALayer for labels. Thanks all for help.
Subclass label as follows :
TiledLable.h file ::
#import <UIKit/UIKit.h>
#import "FastCATiledLayer.h"
#interface TiledLable : UILabel
{
}
#end
TiledLale.m file ::
#import "TiledLable.h"
#import <QuartzCore/QuartzCore.h>
#implementation TiledLable
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code.
FastCATiledLayer *tiledLayer = (FastCATiledLayer *)[self layer];
tiledLayer.levelsOfDetail = 2;
tiledLayer.levelsOfDetailBias = 2;
tiledLayer.tileSize = CGSizeMake(1024.0,1024.0);
tiledLayer.contents = nil;
self.layer.contents = nil;
}
return self;
}
// Set the layer's class to be CATiledLayer.
+ (Class)layerClass
{
return [FastCATiledLayer class];
}
- (void)dealloc
{
((CATiledLayer *)[self layer]).delegate = nil;
[self removeFromSuperview];
[self.layer removeFromSuperlayer];
[super dealloc];
}
#end
here's the current situation:
TestViewController.h:
#import <UIKit/UIKit.h>
#interface TestViewController : UIViewController {
}
#end
TestViewController.m:
#import "TestViewController.h"
#import "draw.h"
#implementation TestViewController
- (void)viewDidLoad {
draw.rectColor = [UIColor colorWithHue:0.6 saturation:0.6 brightness:0.6 alpha:1].CGColor;
[draw setNeedsDisplay];
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)dealloc {
[super dealloc];
}
#end
draw.h:
#import <UIKit/UIKit.h>
#interface draw : UIView {
UIColor* rectColor;
}
#property (retain) UIColor* rectColor;
#end
draw.m:
#import "draw.h"
#implementation draw
#synthesize rectColor;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code.
}
return self;
}
- (void)drawRect:(CGRect)rectangle {
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef myColor = [UIColor colorWithHue:0 saturation:1 brightness:0.61 alpha:1].CGColor;
CGContextSetFillColorWithColor(context, myColor);
CGContextAddRect(context, CGRectMake(0, 0, 95.0, 110.0));
CGContextFillPath(context);
}
- (void)dealloc {
[super dealloc];
}
#end
then in the Interface Builder I've created a 90x115 UIView and set it's Class Identity to "draw".
When I run the application (without the two non-compiling lines) it displays a nice red rectangle in the middle of the screen. My goal is it be able to change the color of the rectangle from within my TestViewController. However when I compile the test app I get the following compiler errors:
error: accessing unknown 'setRectColor:' class method
error: object cannot be set - either readonly property or no setter found
warning: 'draw' may not respond to '+setNeedsDisplay'
I know I am missing "a link" between my TestViewController and draw, but can't figure out how to go about implementing it. I have tried various tricks, but nothing worked.
Could someone please explain what needs to be done in order for
draw.rectColor = [UIColor colorWithHue:0.6 saturation:0.6 brightness:0.6 alpha:1].CGColor;
[draw setNeedsDisplay];
to compile and work ?
(I would use this knowledge to create a different method in TestViewController, I am just testing it inside viewDidLoad)
In the code you posted, you set the rectColor property and call setNeedsDisplay: on draw, but draw is your class. You need to do it to the instance. You shouldn't need to create a new property for this because UIViewController defines the view property. Just use self.view instead of draw in your viewDidLoad method. Also, remove the .CGColor at the end of the line.
Second, your drawRect: method ignores that color and uses its own. Change
CGColorRef myColor = [UIColor colorWithHue:0 saturation:1 brightness:0.61 alpha:1].CGColor;
to
CGColorRef myColor = self.rectColor.CGColor;
The big problem here is that you haven't declared a property for draw; you have to do this and link it up to your Interface Builder object if you want to access it. The solution is to create a property that will hold the draw object, and synthesize its setters and getters using #synthesize. (that last piece is why you're getting the error.)
Once you change the code to what I have below, you'll need to make the proper connection in Interface Builder, or else you'll find that your code changes simply have no effect, even though they're error free.
Capitalize your class names (always!), and then try this:
TestViewController.h:
#import <UIKit/UIKit.h>
#class Draw; //forward class declaration
#interface TestViewController : UIViewController {
IBOutlet Draw *drawCopy;
}
#property (nonatomic, retain) Draw *drawCopy;
#end
Then, for TestViewController.m:
#import "TestViewController.h"
#import "Draw.h"
#implementation TestViewController
#synthesize drawCopy;
-(void) viewDidLoad {
self.drawCopy.rectColor = [UIColor colorWithHue:0.6 saturation:0.6 brightness:0.6 alpha:1];
[self.drawCopy setNeedsDisplay];
[super viewDidLoad];
}
- (void)dealloc {
[drawCopy release];
[super dealloc];
}
#end
I think that should do the trick.
you definde UIColor* rectColor; but u write an CGColor too it
draw.rectColor = [UIColor colorWithHue:0.6 saturation:0.6 brightness:0.6 alpha:1].CGColor;
try
self.drawCopy.rectColor = [UIColor colorWithHue:0.6 saturation:0.6 brightness:0.6 alpha:1];
i want to make headerfile Rimage.h and m that i can use anywhere but i have a problem here
this is my
Rimage.h
#class Rahul_imageplaceCordinatesViewController;
#interface Rimage : NSObject {
Rahul_imageplaceCordinatesViewController *vc;
}
-(void)addImageSubViewAtX:(CGFloat)x atY:(CGFloat)y;
#property (nonatomic,copy) Rahul_imageplaceCordinatesViewController *vc;
+(void)check1;
#end
and this is my .m
#implementation Rimage
#synthesize vc;
/// this method let you have image with given X n Y
- (void)addImageSubViewAtX:(CGFloat)x atY:(CGFloat)y {
CGRect myImageRect1 = CGRectMake(x, y, 30.0f, 30.0f);
UIImageView *myImage1 = [[UIImageView alloc] initWithFrame:myImageRect1];
[myImage1 setImage:[UIImage imageNamed:#"status_finish.gif"]];
myImage1.tag = 1000;
myImage1.userInteractionEnabled = YES;
[self.view addSubview:myImage1];
}
now in my mainVC
i am calling like this
- (void)viewDidLoad {
Rimage *hw = [[Rimage alloc] init];
//[hw check1];
[Rimage check1];
[hw addImageSubViewAtX:160.0 atY:190.0];
}
but i cant display that image i dont know why :(
First I noticed is your #property you are making a "copy" of the ViewController in vc, I suspect that's a bad idea, you just want to assign or retain it.
Second, in addImageSubViewAtX, you have:
[self.view addSubview:myImage1];
But what is self there? It is the instance of Rimage. I'm guessing that perhaps you meant to add the image to the view in vc?? I don't see that vc is ever used in the code you provided so what is it for?
[vc.view addSubview:myImage1];
I have been trying to set a UIImageView background color (see below) in awakeFromNib
[imageView setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:1.0]];
When it did not work, I realised that its probably because the view has not loaded yet and I should move the color change to viewDidLoad.
Can I just verify that I have this right?
gary
EDIT_002:
I have just started a fresh project to check this from a clean start. I setup the view the same as I always do. The results are that the controls are indeed set to (null) in the awakeFromNib. Here is what I have:
CODE:
#interface iPhone_TEST_AwakeFromNibViewController : UIViewController {
UILabel *myLabel;
UIImageView *myView;
}
#property(nonatomic, retain)IBOutlet UILabel *myLabel;
#property(nonatomic, retain)IBOutlet UIImageView *myView;
#end
.
#synthesize myLabel;
#synthesize myView;
-(void)awakeFromNib {
NSLog(#"awakeFromNib ...");
NSLog(#"myLabel: %#", [myLabel class]);
NSLog(#"myView : %#", [myView class]);
//[myLabel setText:#"AWAKE"];
[super awakeFromNib];
}
-(void)viewDidLoad {
NSLog(#"viewDidLoad ...");
NSLog(#"myLabel: %#", [myLabel class]);
NSLog(#"myView : %#", [myView class]);
//[myLabel setText:#"VIEW"];
[super viewDidLoad];
}
OUTPUT:
awakeFromNib ...
myLabel: (null)
myView : (null)
viewDidLoad ...
myLabel: UILabel
myLabel: UIImageView
I would be interested to know if this should work, from the docs it looks like it should, but given the way I usually set things up I can't quite understand why it does not in this case.
One more answer :-) It looks like you’re getting this behaviour because the controller loads the views lazily. The view is not loaded immediately, it gets loaded the first time somebody calls the view accessor. Therefore at the time you recieve awakeFromNib the NIB loading process is done, but not for the objects inside your views. See this code:
#property(retain) IBOutlet UILabel *foo;
#synthesize foo;
- (void) awakeFromNib
{
NSLog(#"#1: %i", !!foo);
[super awakeFromNib];
NSLog(#"#2: %i", !!foo);
}
- (void) viewDidLoad
{
NSLog(#"#3: %i", !!foo);
}
This logs:
#1: 0
#2: 0
#3: 1
But if you force-load the view:
- (void) awakeFromNib
{
NSLog(#"#1: %i", !!foo);
[super awakeFromNib];
[self view]; // forces view load
NSLog(#"#2: %i", !!foo);
}
The log changes into this:
#1: 0
#3: 1
#2: 1
I believe your call to super needs to be the first line in the awakeFromNib method, otherwise the elements won't be setup yet.
-(void)awakeFromNib {
[super awakeFromNib];
[imageView setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:1.0]];
[testLabel setText:#"Pants ..."];
}
I know, this post is a bit older, but I recently had a similar problem and would like to share its solution with you.
Having subclassed NSTextView, I wanted to display the row colors in alternating orders. To be able to alter the colors from outside, I added two instance vars to my subclass, XNSStripedTableView:
#interface XNSStripedTableView : NSTableView {
NSColor *pColor; // primary color
NSColor *sColor; // secondary color
}
#property (nonatomic, assign) NSColor *pColor;
#property (nonatomic, assign) NSColor *sColor;
#end
Overwriting highlightSelectionInClipRect: does the trick to set the correct color for the respective clipRect.
- (void)highlightSelectionInClipRect:(NSRect)clipRect
{
float rowHeight = [self rowHeight] + [self intercellSpacing].height;
NSRect visibleRect = [self visibleRect];
NSRect highlightRect;
highlightRect.origin = NSMakePoint(NSMinX(visibleRect), (int)(NSMinY(clipRect)/rowHeight)*rowHeight);
highlightRect.size = NSMakeSize(NSWidth(visibleRect), rowHeight - [self intercellSpacing].height);
while (NSMinY(highlightRect) < NSMaxY(clipRect)) {
NSRect clippedHighlightRect = NSIntersectionRect(highlightRect, clipRect);
int row = (int) ((NSMinY(highlightRect)+rowHeight/2.0)/rowHeight);
NSColor *rowColor = (0 == row % 2) ? sColor : pColor;
[rowColor set];
NSRectFill(clippedHighlightRect);
highlightRect.origin.y += rowHeight;
}
[super highlightSelectionInClipRect: clipRect];
}
The only problem now is, where to set the initial values for pColor and sColor? I tried awakeFromNib:, but this would cause the debugger to come up with an error. So I dug into the problem with NSLog: and found an easy but viable solution: setting the initial values in viewWillDraw:. As the objects are not created calling the method the first time, I had to check for nil.
- (void)viewWillDraw {
if ( pColor == nil )
pColor = [[NSColor colorWithSRGBRed:0.33 green:0.33 blue:0 alpha:1] retain];
if ( sColor == nil )
sColor = [[NSColor colorWithSRGBRed:0.66 green:0.66 blue:0 alpha:1] retain];
}
I do think this solution is quite nice :-) although one could reselect the names of pColor and sColor could be adjusted to be more "human readable".
Are you sure the objects are not nil? NSAssert or NSParameterAssert are your friends:
-(void) awakeFromNib {
NSParameterAssert(imageView);
NSParameterAssert(testLabel);
NSLog(#"awakeFromNib ...");
[imageView setBackgroundColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:1.0]];
[testLabel setText:#"Pants ..."];
[super awakeFromNib];
}
If the objects are really initialized, try to log their address and make sure that the instances that appear in viewDidLoad are the same as those in awakeFromNib:
- (void) awakeFromNib {
NSLog(#"test label #1: %#", testLabel);
}
- (void) viewDidLoad {
NSLog(#"test label #2: %#", testLabel);
}
If the numbers are the same, you can create a category to set a breakpoint on setBackgroundColor and peek in the stack trace to see what’s going on:
#implementation UIImageView (Patch)
- (void) setBackgroundColor: (UIColor*) whatever {
NSLog(#"Set a breakpoint here.");
}
#end
You can do the same trick using a custom subclass:
#interface PeekingView : UIImageView {}
#end
#implementation PeekingView
- (void) setBackgroundColor: (UIColor*) whatever {
NSLog(#"Set a breakpoint here.");
[super setBackgroundColor:whatever];
}
#end
Now you’ll set your UIViewObject to be of class PeekingView in the Interface Builder and you’ll know when anybody tries to set the background. This should catch the case where somebody overwrites the background changes after you initialize the view in awakeFromNib.
But I presume that the problem will be much more simple, ie. imageView is most probably nil.
In case you're using a UIView subclass instead of a UIViewController subclass, you can override loadView method:
- (void)loadView
{
[super loadView];
//IBOutlets are not nil here.
}
I am writing a program which redraws a graph every so often. Unfortunately it seems that for some reason core graphics does not seem to release the previous graphic so I run into memory problems fast. How can I force it to release memory at the end of each draw?
Not that this is not a memory leak as the memory does get released when I leave close that view. I have tried moving the NSTimer to several places in the code to no avail.
Here is the code below with the files going from most important to (potentially) irrelevant.
First is the core file+header which draws the graphic (in this case a sine wave) every so often.
GraphicsView.h
#import <UIKit/UIKit.h>
#import "Model.h"
#interface GraphicsView : UIView
{
Model * model;
}
#end
GraphicsView.m
#import "GraphicsView.h"
#implementation GraphicsView
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
model=[Model sharedModel];
[NSTimer scheduledTimerWithTimeInterval:.010 target:self selector:#selector(setNeedsDisplay) userInfo:nil repeats:YES];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
int now=[model n];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, CGRectMake(0, 0, rect.size.width, rect.size.height));
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextMoveToPoint(context, 0.0, 0.0);
double xc_old=0;
double yc_old=100;
for (int i=0;i<now;i++)
{
double xc=(double)i/(double)now*200;
double yc=100-100*sin((double)i/6);
CGContextMoveToPoint(context, xc_old, yc_old);
CGContextAddLineToPoint(context, xc, yc);
CGContextStrokePath(context);
xc_old=xc;
yc_old=yc;
}
}
- (void)dealloc {
[super dealloc];
}
#end
The view controller header:
#import <UIKit/UIKit.h>
#import "GraphicsView.h"
#interface SbarLeakAppDelegate : NSObject <UIApplicationDelegate>
{
UIWindow *window;
Model *model;
GraphicsView * gv;
NSTimer * timer;
}
#end
The view controller itself.
#import "SbarLeakAppDelegate.h"
#implementation SbarLeakAppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
model=[Model sharedModel];
CGRect appRect;
CGRect frame = CGRectInset(appRect, 0.0f, 0.0f);
appRect.origin=CGPointMake(0.0, 40.0);
appRect.size = CGSizeMake(200.0f, 200.0f);
frame = CGRectInset(appRect, 0.0f, 0.0f);
gv=[[GraphicsView alloc] initWithFrame:appRect];
[gv setFrame:frame];
[window addSubview:gv];
[gv release];
[window makeKeyAndVisible];
}
-(void) viewDidAppear:(id)sender
{
//timer=[NSTimer scheduledTimerWithTimeInterval:.250 target:gv selector:#selector(setNeedsDisplay) userInfo:nil repeats:YES];
}
- (void)dealloc
{
[window release];
[super dealloc];
}
#end
This code is only included for completeness but (should not) affect the question.
The data source header file (just a simple integer)
#import <UIKit/UIKit.h>
#interface Model : NSObject
{
int n;
}
#property int n;
+(Model *) sharedModel;
-(void) inc;
#end
The data source, Model.m:
#import "Model.h"
#implementation Model
static Model * sharedModel = nil;
+ (Model *) sharedModel
{
if (sharedModel == nil)
sharedModel = [[self alloc] init];
return sharedModel;
}
#synthesize n;
-(id) init
{
self=[super init];
[NSTimer scheduledTimerWithTimeInterval:.05 target:self selector:#selector(inc) userInfo:nil repeats:YES];
return self;
}
-(void) inc
{
n++;
}
#end
The selector you use for your timer does not have the correct signature.
From the Apple docs:
The selector must have the following signature:
- (void)timerFireMethod:(NSTimer*)theTimer
You should use your own method for the timer and call setNeedsDisplay from there. As Robert said, you should turn down the frequency a bit, but I don't think either of these issues will solve your problem.
Did you try: self.clearsContextBeforeDrawing = YES; in the drawRect method ?
According to the NSTimer documentation, the max time resolution is 50-100ms... it looks like you're trying for 1ms... according to spec a timer that asks for finer resolution than that shouldn't cause memory issues, it just shouldn't fire as often as needed. I suspect that's part of your problem, though.
NSTimer Class Reference
Because of the various input sources a
typical run loop manages, the
effective resolution of the time
interval for a timer is limited to on
the order of 50-100 milliseconds.
+ (Model *) sharedModel
{
if (sharedModel == nil)
sharedModel = [[self alloc] init];
return sharedModel;
}
in this snippet, autorelease is missing. This also leads to a leak i think.
try with
sharedModel = [[[self alloc] init] autorelease];