Having problems with CALayer - objective-c

So I have a class that returns a CALayer that has an array of CALayers added as sublayers, but the problem is that it doesn't seem to be rendering any of them. I think I may just be missing something about how CALayers work as I'm not very familiar yet. Could someone please take a look at my code to see what I am doing wrong?
- (CALayer*)drawMap {
CALayer* theLayer = [CALayer layer];
for (int y = 0; y < [self.height intValue]; y++) {
for (int x = 0; x < [self.width intValue]; x++) {
CALayer* tileLayer = [CALayer layer];
tileLayer.bounds = CGRectMake((x * [self.tileWidth intValue]), (y * [self.tileHeight intValue]), [self.tileWidth intValue], [self.tileHeight intValue]);
[tileLayer setBackgroundColor:[UIColor colorWithHue:(CGFloat)x saturation:(CGFloat)y brightness:255 alpha:1.0].CGColor];
[theLayer addSublayer:tileLayer];
}
}
[theLayer setBounds:CGRectMake([self.width intValue] * [self.tileWidth intValue], [self.height intValue] * [self.tileHeight intValue], 10, 10)];
return theLayer;
}
This is the code that initializes and draws the map.
Map* myMap = [[Map alloc] initFromFile];
[myMap setWidth:[NSNumber numberWithInt:100]];
[myMap setHeight:[NSNumber numberWithInt:100]];
[self.view.layer insertSublayer:[myMap drawMap] above:self.view.layer];
I can provide the headers if needed, but I don't think the problem is there and I don't want a huge post.

I think the problem is the bounds you're setting to your parent layer:
[theLayer setBounds:
CGRectMake(
[self.width intValue] * [self.tileWidth intValue],
[self.height intValue] * [self.tileHeight intValue],
10, 10)];
That would appear to set the bounds so that it starts just exactly past all the sublayers you've just added and extends for 10 points in both directions. I think the following would be more appropriate:
[theLayer setBounds:
CGRectMake(
0,
0,
[self.width intValue] * [self.tileWidth intValue],
[self.height intValue] * [self.tileHeight intValue])];
The following also looks fishy:
[self.view.layer insertSublayer:[myMap drawMap] above:self.view.layer];
[myMap drawMap] will become a sublayer of self.view.layer. self.view.layer is not one of its own subviews so it won't know what to do with that above: instruction. You probably want just addSublayer:.
Finally, this:
tileLayer.bounds = CGRectMake((x * [self.tileWidth intValue]), (y * [self.tileHeight intValue]), [self.tileWidth intValue], [self.tileHeight intValue]);
gives every single one of your sublayers exactly the same frame. Much like UIViews, the bounds are the source area for information and the frame is where that information will be put within the super layer. I think probably you want to set frame.
Finally, UIColor +colorWithHue:... clamps hue and saturation to the range [0, 1] so you probably want (CGFloat)x / [self.width floatValue] and the equivalent for saturation to ensure all your tiles come out in different colours.
EDIT: so, tying all that together, I modified your code slightly so that I didn't have to write the full encompassing class and added it to the Xcode view-based project template as follows:
#define kTileWidth 3
#define kTileHeight 3
#define kWidth 100
#define kHeight 100
- (CALayer*)drawMap {
CALayer* theLayer = [CALayer layer];
for (int y = 0; y < kHeight; y++)
{
for (int x = 0; x < kWidth; x++)
{
CALayer* tileLayer = [CALayer layer];
tileLayer.frame = CGRectMake((x * kTileWidth), (y * kTileHeight), kTileWidth, kTileHeight);
[tileLayer setBackgroundColor:[UIColor colorWithHue:(CGFloat)x/kWidth saturation:(CGFloat)y/kHeight brightness:255 alpha:1.0].CGColor];
[theLayer addSublayer:tileLayer];
}
}
[theLayer setFrame:CGRectMake(0, 0, kWidth * kTileWidth, kHeight * kTileHeight)];
return theLayer;
}
// ...
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.view.layer addSublayer:[self drawMap]];
}
That gives the hue/saturation plane familiar from many an art package.

Related

memory leak while making graph of functions in calculator iPad

i am developing a scientific calculator for iPad . I am trying to draw functions creating a graphical representation . but i a have a memory leak when i draw a function of second degree for example x^2 . I was wondering if you could help solve the following memory leak,please? I would appreciate any help. The button that opens the screen with the function to be drawn has this code :
userIsInTheMiddleOfTypingANumber = YES;
graphVC.expression =[NSMutableArray arrayWithObject:[NSString
stringWithFormat:#"%#",#"$x**2"] ];
[self prepareToSolve];
graphVC=[[GraphViewController alloc] initWithNibName:#"GraphViewController"
bundle:Nil];
[self presentModalViewController:graphVC animated:YES];
Then in the GraphViewController i set the expression with the code:
- (void)viewDidLoad
{
[graphView reset];
[graphView setNeedsDisplay];
currentColumn = [NSMutableString string];
substitutions = [NSMutableDictionary dictionary];
eval = [DDMathEvaluator sharedMathEvaluator];
[currentColumn setString:[expression objectAtIndex:0]];
}
and then the method expressionResultForXValue leaks
- (double)expressionResultForXValue:(NSNumber*)x requestor:(GraphView *)graphView {
[substitutions setValue:x forKey:#"x"];
return [[eval evaluateString:currentColumn withSubstitutions:substitutions]
doubleValue]; //[CalculatorBrain evaluateExpression:self.expression
usingVariableValues:variableValues];
}
even though in the viewDidUnload method i set - [expression release];
The expressionResultForXValue method is called many times by the UIView GraphView by the drawrect method:
- (void)drawRect:(CGRect)rect {
CGPoint midpoint;
midpoint.x = self.bounds.origin.x + self.bounds.size.width/2 + originOffset.x;
midpoint.y = self.bounds.origin.y + self.bounds.size.height/2 + originOffset.y;
[[UIColor whiteColor] setStroke];
[AxesDrawer drawAxesInRect:self.bounds originAtPoint:midpoint
scale:self.scale*self.contentScaleFactor];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 5);
CGContextBeginPath(context);
for (CGFloat i = 0; i <= self.bounds.size.width; i++) {
CGPoint nextPointInViewCoordinates;
nextPointInViewCoordinates.x = i;
CGPoint nextPointInGraphCoordinates;
nextPointInGraphCoordinates.x = (nextPointInViewCoordinates.x - midpoint.x)/(self.scale
* self.contentScaleFactor);
nextPointInGraphCoordinates.y = ([self.delegate expressionResultForXValue:[NSNumber
numberWithDouble:nextPointInGraphCoordinates.x]
requestor:self]);
nextPointInViewCoordinates.y = midpoint.y - (nextPointInGraphCoordinates.y * self.scale
* self.contentScaleFactor);
if (i == 0) {
CGContextMoveToPoint(context, nextPointInViewCoordinates.x,
nextPointInViewCoordinates.y);
} else {
CGContextAddLineToPoint(context,
nextPointInViewCoordinates.x,nextPointInViewCoordinates.y);
}
}
[[UIColor yellowColor] setStroke];
CGContextDrawPath(context, kCGPathStroke);
}
The memory leak i get is :
Leaked Object # Address Size Responsible Library Responsible Frame
_DDFunctionExpression 1020 < multiple > 15,94 KB Rechemaschinn
+[DDExpression functionExpressionWithFunction:arguments:error:]
NSDecimalNumber 1018 < multiple > 15,91 KB Foundation -[NSDecimalNumberPlaceholder initWithDecimal:]
_DDNumberExpression 1020 < multiple > 15,94 KB Rechemaschinn +[DDExpression numberExpressionWithNumber:]
NSArray 1020 < multiple > 15,94 KB Rechemaschinn -[_DDFunctionExpression initWithFunction:arguments:error:]
_DDVariableExpression 1020 < multiple > 15,94 KB Rechemaschinn +[DDExpression variableExpressionWithVariable:]
__NSCFString 1019 < multiple > 15,92 KB Foundation -[NSPlaceholderString initWithCharacters:length:]
Thank you in advance.

UIScrollView and Autoration issues

I'm trying to understand an issue I'm having with an iOS Universal app. I have a UIScrollView which I want the page of to take up the full dimension of the device and adjust itself when rotating.
In my viewDidLoad method, I'm testing with:
NSArray *colors = [NSArray arrayWithObjects:[UIColor orangeColor], [UIColor cyanColor], [UIColor redColor], [UIColor greenColor], [UIColor blueColor], nil];
for (int i = 0; i < colors.count; i++) {
CGRect frame;
frame.origin.x = _scrollView.bounds.size.width * i;
frame.origin.y = 0;
frame.size = _scrollView.bounds.size;
UIView *subview = [[UIView alloc] initWithFrame:frame];
subview.backgroundColor = [colors objectAtIndex:i];
[_scrollView addSubview:subview];
}
_scrollView.contentSize = CGSizeMake(_scrollView.bounds.size.width * colors.count, _scrollView.bounds.size.height);
then I added in the didRotateFromInterfaceOrientation method:
_scrollView.contentSize = CGSizeMake(_scrollView.bounds.size.width * colors.count, _scrollView.bounds.size.height);
for (int i= 0; i< colors.count; i++)
{
CGRect frame;
frame.origin.x = _scrollView.bounds.size.width * i;
frame.origin.y = 0;
frame.size = _scrollView.bounds.size;
UIView *subview= [[_scrollView subviews] objectAtIndex:i];
subview.frame= frame;
}
This seems to work, but I seem to have some clipping on the main view while the device is rotating.
Here's a screenshot of what happens when rotating:
I thought this happened because I changed the size after it was rotated, but I tried to handle it in the willRotateToInterfaceOrientation method, but it gave the wrong result...
Am I handling this incorrectly?
What would be the correct approach?
TIA!
S.
Am I handling this incorrectly?
You should put the code that changes the bounds in willRotateToInterfaceOrientation, but do so in an animation block (using [UIView animateWithDuration:0.4 animations:...])_so that the transition will be smooth rather than immediate.
An extra note is that inside willRotateToInterfaceOrientation, the parent view will not yet have been resized, so you cannot depend on it's bounds to calculate the final position of the views, rather you must do so manually.

Why is each line of code giving me "invalid context"?

This is my code in which every line is giving me an "invalid context" error when running the app in the iPad Simulator. Every statement after "self.Frame = frame; gives the error. How do I fix it?
- (void)makeFrame:(CGRect)frame number:(NSInteger) number color:(UIColor *) color {
float rd1 = 225.00/255.00;
float gr1 = 102.00/255.00;
float bl1 = 0.00/255.00;
float rd2 = 225.00/255.00;
float gr2 = 153.00/255.00;
float bl2 = 0.00/255.00;
self.frame = frame;
self.backgroundColor = [UIColor colorWithRed:rd1 green:gr1 blue:bl1 alpha:1.0];
[[self layer] setBorderColor:[[UIColor blackColor] CGColor]];
[[self layer] setBorderWidth:0.5];
[[self layer] setCornerRadius:10];
self.tag = number; // set each button's tag
[self setClipsToBounds: YES];
// do date label
date = [[UILabel alloc]initWithFrame:CGRectMake(10, 2, 65, 40 )];
date.backgroundColor = [UIColor clearColor]; // x y w h
date.textColor = [UIColor colorWithRed:rd2 green:gr2 blue:bl2 alpha:1.0];
date.text = [NSString stringWithFormat:#"%d", number];
date.font = [UIFont boldSystemFontOfSize:40];
// date.alpha = 0.2;
[self addSubview:date];
}
And this is the code which calls makeFrame:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
allButtons = [NSMutableArray array];
for(int k = 40, m = 0; m < 6; m++, k+= 42) // this controls the vertical distance between rows
for(int i = 0, j=32; i < 7; i++, j+=102) { // this controls the size and horizontal distance
calendarButton *cb= [calendarButton buttonWithType:UIButtonTypeCustom];
[cb makeFrame:CGRectMake(j, k, 100, 40) number: allButtons.count+1 color:[UIColor orangeColor]];
// x y w h
[self.view addSubview:cb];
[allButtons addObject:cb]; // put it in the array
}
calendarButton *cb= [calendarButton buttonWithType:UIButtonTypeCustom];
[cb drawRect:CGRectMake(0,0, 100, 40)]; // 150
You're manually calling drawRect, which I suspect is causing this problem. From the docs:
You should never call this method directly yourself. To invalidate part of your view, and thus cause that portion to be redrawn, call the setNeedsDisplay or setNeedsDisplayInRect: method instead.
This doesn't look necessary in this case, though. The view has been added as a subview of your view; it will automatically draw itself when your view renders.
You are trying to access view properties in view controller. In your case self is UIViewController (or sublcass), change it to self.view in order to access to view
E.g.
self.view.frame = frame;

Make a Views with Array

I want to create views programmatically, Matrix: 10X10 = 100 views . I've created IB but it has to be programmed with Xcode. How can i do this?
For one view create this, for 10X10 views I don't know. How can i do all views;
UIView *tempView1 = [[UIView alloc] initWithFrame:CGRectMake(0, 660, 60, 60)];
[self.view addSubview:tempView1];
rd = 0;
gr = 0.5;
bl =0;
tempView1.backgroundColor = [UIColor colorWithRed:rd green:gr blue:bl alpha:1.0];
This should do the trick for you:
for (int i = 0; i < amountOfViewsHorizontally; i++)
{
for (int j = 0; j < amountOfViewsVertically; j++)
{
UIView *someView = [[UIView alloc] initWithFrame:CGRectMake((i*widthOfView), (j*heightOfView), widthOfView, heightOfView)];
rd = 0;
gr = 0.5;
bl =0;
someView.backgroundColor = [UIColor colorWithRed:rd green:gr blue:bl alpha:1.0];
UILabel *someLabel = [[UILabel alloc] init];
someLabel.frame = CGRectMake(0,0,50,20); //this will add a label in all the upperleft corners (point(0,0)) of every view with width 50 and height 20.
[someView addSubView:someLabel];
//[someViewArray addObject:someView]; //add view to an array
[self.view addSubview:someView];
}
}
You might want to put them into some sort of array so you can access them at some later point.
If you want components like UILabel to be added to those views. You will have to use the intern coordinate system of those views.
For an example of more detailed layout, row, height dynamically, check out my article here. It shows how you can not only dynamically layout boxes according to size and screen size but also how dynamic reordering works.
See the article for more details but the key approach is...
- (void) layoutBoxesWithRowCount:(NSInteger)rowCount {
double xPos = kBoxSpacer;
double yPos = kBoxSpacer;
int boxCount = 0;
for(LOBox *box in boxes) {
CGRect frame = [box frame];
frame.origin.x = xPos;
frame.origin.y = yPos;
[box setFrame:frame];
xPos += kBoxDimension + kBoxSpacer;
boxCount++;
if(boxCount == rowCount) {
boxCount = 0;
xPos = kBoxSpacer;
yPos += kBoxDimension + kBoxSpacer;
}
}
}
Full write-up here.

"Flatten" or "Merge" Quartz 2D drawn lines

I'm trying to figure out how you can flatten or merge alot of dynamicly drawn lines in Quartz 2D,
I'm drawing random lines over time on my stage, I add new line coordinates each time to an array and draw the array (with my drawRect and I push a new line in my array with a timer and a setNeedDisplay to acctually redraw all the previous lines plus the new one)
now the PROBLEM: after a while it starts to get slower and slower because the arrays are getting very long, so I though I should merge the coordinates into a flat image or something and clear the arrays to keep it memory healthy, but how do I do this?
This is my current workflow:
Call timer
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(drawdrawdraw) userInfo:nil repeats:YES];
Refresh the drawRect in my "drawdrawdraw" function
-(void)drawdrawdraw{
[self setNeedsDisplay];
}
my drawRect
-(void)drawRect:(CGRect)rect{
viewContext = UIGraphicsGetCurrentContext();
int count = [mijnArray_r_x count];
float r_x = (float)(random() % 768);
[mijnArray_r_x insertObject:[NSNumber numberWithFloat:r_x] atIndex:count];
float r_y = (float)(random() % 1004);
[mijnArray_r_y insertObject:[NSNumber numberWithFloat:r_y] atIndex:count];
float r_w = (float)(random() % 100);
[mijnArray_r_w insertObject:[NSNumber numberWithFloat:r_w] atIndex:count];
float r_a = (float)(random() % 100);
[mijnArray_r_a insertObject:[NSNumber numberWithFloat:r_a] atIndex:count];
CGContextSetLineWidth(viewContext, 2.0);
CGContextSetStrokeColorWithColor(viewContext, [UIColor blackColor].CGColor);
for (int k = 0; k <= count; k++) {
float temp_x = [[mijnArray_r_x objectAtIndex: k] floatValue];
float temp_y = [[mijnArray_r_y objectAtIndex: k] floatValue];
float temp_w = [[mijnArray_r_w objectAtIndex: k] floatValue];
float temp_a = [[mijnArray_r_a objectAtIndex: k] floatValue];
CGPoint pointpointpoint = CGPointMake(temp_x, temp_y);
CGPoint pointpointpointpoint = CGPointMake(temp_w, temp_a);
CGContextMoveToPoint(viewContext, pointpointpoint.x, pointpointpoint.y);
CGContextAddLineToPoint(viewContext, pointpointpoint.x - pointpointpointpoint.x, pointpointpoint.y + pointpointpointpoint.y);
CGContextStrokePath(viewContext);
}
} }
You can draw your lines into a UIImage by using UIGraphicsBeginImageContext(). This way, the time needed to draw will be constant, because in each iteration you're only drawing the cached image as a bitmap and the new lines you add.
- (void)drawRect:(CGRect)rect {
[self.currentImage drawInRect:[self bounds]];
UIGraphicsBeginImageContext([self bounds].size);
CGContextRef cgContext = UIGraphicsGetCurrentContext();
//Draw your lines into the cgContext here...
self.currentImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
This assumes that you have the property currentImage of type UIImage declared.