How to resize all subviews/sublayers when superview is resized? - ios

This question relates to the project: https://github.com/FindForm/FFAngularPointilism
Currently, all the behavior is dictated by the dimensions of the original UIImage. Despite resizing, shrinking, and rotating, the drawing of the "double triangle squares" remains the same. The triangular blur is unchanged regardless of the bounds of the UIImageView to which it is applied.
In my UIImageView subclass, I initialize with a UIImage and call the following in the designated initializer:
- (void)loadMatrix
{
num = 12;
CGFloat width = self.bounds.size.width;
CGFloat height = self.bounds.size.height;
CGFloat theBiggestSideLength = MAX(width, height);
_array = [NSMutableArray array];
_array2 = [NSMutableArray array];
for(int i = 0; i < theBiggestSideLength; i += num)
{
[self.array addObject:[NSMutableArray array]];
[self.array2 addObject:[NSMutableArray array]];
}
for(int i = 0; i < self.array.count; i++)
{
for(int j = 0; j < self.array.count; j++)
{
[self.array[i] addObject:[self getRGBAsFromImage:self.image
atX:j * 2 * num
andY:i * 2 * num]];
int xIndex = ((j * 2 * num) - (num / 2.0));
int yIndex = ((i * 2 * num) + (num / 2.0));
xIndex %= (int)width * 2;
yIndex %= (int)width * 2;
NSLog(#"%d", xIndex);
[self.array2[i] addObject:[self getRGBAsFromImage:self.image
atX:xIndex
andY:yIndex]];
}
}
_imageGrayscaleView = [[UIImageView alloc] initWithImage:[self convertToGreyscale:self.image]];
_finalbwimage = self.imageGrayscaleView.image;
_imageGrayscaleView.frame = self.bounds;
[self insertSubview:_imageGrayscaleView atIndex:0];
_viewMask = [[UIView alloc] initWithFrame:self.frame];
_viewMask.center = CGPointMake(_viewMask.center.x,
_viewMask.center.y + _viewMask.frame.size.height / 2.0f);
_shapeLayer = [[CAShapeLayer alloc] init];
CGRect maskRect = CGRectZero;
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);
_shapeLayer.path = path;
CGPathRelease(path);
self.imageGrayscaleView.layer.mask = _shapeLayer;
}
To draw these tiles, I add a timer and call the following method each interval. Num and row are updating as expected.:
- (void)drawTile
{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(pixel * num, row * num, num, num)];
view.backgroundColor = [self.array[row] objectAtIndex:pixel];
[self addSubview:view];
[self.ksubviews addObject:view];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(view.frame.origin.x , view.frame.origin.y + (view.frame.size.height))];
[path addLineToPoint:CGPointMake(view.frame.origin.x , view.frame.origin.y)];
[path addLineToPoint:CGPointMake(view.frame.origin.x + view.frame.size.width, view.frame.origin.y + view.frame.size.height)];
[path closePath];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = path.CGPath;
shapeLayer.strokeColor = [[UIColor blackColor] CGColor];
shapeLayer.fillColor = [((UIColor *)[self.array2[row] objectAtIndex:pixel]) CGColor];
shapeLayer.lineWidth = 0;
[self.layer addSublayer:shapeLayer];
[self.ksublayers addObject:shapeLayer];
}
The following is the correct behavior:
When this image is resized via the nib, or its bounds set programmatically, the animatable triangular squares are unchanged.

You make all the subviews changes on the method Draw Rect inside your class, and every time you make a change, to the superview, you call the method setNeedDisplay.
I didn't understand you question very well, but i hope this helps!

Related

Unable to scale shapes to match the dimensions of UIView

I have created a custom view that draws shapes of varying sides. The view is added to main view as a subview as given below. The shapes are of different dimensions.
My source code is given below
-(instancetype) initWithSides:(NSUInteger) sides andFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setBackgroundColor:[UIColor clearColor]];
self.sides = sides;
self.radius = CGRectGetWidth(self.frame) * 1.5 / sides);
}
return self;
}
-(void) drawRect:(CGRect) frame {
// Sides is passed as constructor argument. 4 sides means a quadrilateral etc.
// Get CGPAthRef instance
CGFloat angle = DEGREES_TO_RADIANS(360 / ((CGFloat) self.sides));
int count = 0;
CGFloat xCoord = CGRectGetMidX(self.frame);
CGFloat yCoord = CGRectGetMidY(self.frame);
while (count < self.sides) {
CGFloat xPosition =
xCoord + self.radius * cos(angle * ((CGFloat) count));
CGFloat yPosition =
yCoord + self.radius * sin(angle * ((CGFloat) count));
[self.points addObject:[NSValue valueWithCGPoint:CGPointMake(xPosition, yPosition)]];
count ++;
}
NSValue* v = [self.points firstObject];
CGPoint first = v.CGPointValue;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, first.x, first.y);
for (int ix = 1; ix < [self.points count]; ix++) {
NSValue* pValue = [self.points objectAtIndex:ix];
CGPoint p = pValue.CGPointValue;
CGPathAddLineToPoint(path, nil, p.x, p.y);
}
CGPathCloseSubpath(path);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddPath(context, path);
[self colourView:context withPath:path];
}
-(void) colourView:(CGContextRef) context withPath:(CGPathRef) ref {
NSUInteger num = arc4random_uniform(8) + 1;
UIColor* color = nil;
switch (num) {
case 1:
color = [UIColor redColor];
break;
case 2:
color = [UIColor greenColor];
break;
case 3:
color = [UIColor yellowColor];
break;
case 4:
color = [UIColor blueColor];
break;
case 5:
color = [UIColor orangeColor];
break;
case 6:
color = [UIColor brownColor];
break;
case 7:
color = [UIColor purpleColor];
break;
case 8:
color = [UIColor blackColor];
break;
default:
break;
}
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillPath(context);
}
This constitutes one single shape. This is how I am drawing the rest of the views.
-(void) initDrawView {
NSUInteger width = CGRectGetWidth(self.view.bounds) / 8;
NSUInteger height = (CGRectGetHeight(self.view.frame))/ 8;
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSingleTap:)];
for (int i = 0 ; i < 8; i++) {
CGFloat yCoord = i * height + i * 15;
for (int j = 0; j < 8; j ++) {
int side = 3 + arc4random() % 8;
CGFloat xCoord = j * width;
SimpleRegularPolygonView* view =
[[SimpleRegularPolygonView alloc] initWithSides:side andFrame:CGRectMake(xCoord, yCoord, width, height)];
[view sizeToFit];
view.viewEffectsDelegate = self;
[view setTag: (8 * i + j)];
[self.view addGestureRecognizer:singleFingerTap];
[self.view addSubview:view];
}
}
}
1) I don't know how to make them have the same dimensions. How can I do that? (First image)
2) the images don't scale up to the size of the UIView (Second image).
Your current code makes the radius inversely proportional to the number of sides:
self.radius = CGRectGetWidth(self.frame) * 1.5 / sides;
so the more sides, the smaller the image. A quick fix would be to just make the radius half the frame width:
self.radius = CGRectGetWidth(self.frame) /2;
This will mean shapes with an even number of sides fill the frame width. But those with an odd number of sides will appear to have space to the left. If you want to adjust for that you will need more detailed calculations for the width, and you will also need to move the "centre" of the shape. For an odd number sides, the radius would need to be:
self.radius = CGRectGetWidth(self.frame) /(1 + cos(angle / 2));
and xCoord would need to be:
CGFloat xCoord = CGRectGetMinX(self.frame) + self.radius * cos(angle/2);

How to create buttons around circle with some radius

i try to create programmatically a number of buttons. I what that buttons will created around the circle like on image 1. Now i have some results, i can rotate buttons, but don't know how to set offset between them. Now i have this result: image 2 Here is my code. And thanks for any help!
viewDidload in ViewController
CGPoint point = self.view.center;
NSArray *arrayWithImages = #[#"red", #"pink", #"green", #"blue", #"purple", #"orange", #"yellow"];
NSArray *arrayWithAngle = #[#(0), #(M_PI / 3.8f), #(M_PI / 1.8), #(M_PI / -6), #(M_PI / 6), #(M_PI / -1.8), #(M_PI / -3.8)];
NSMutableArray *arrayWithPlacePoint = [self generatePointForRound:point andAngle:arrayWithAngle andRadius:25.0f];
NSLog(#"array with place point = %#", arrayWithPlacePoint);
for (NSInteger i = 0; i < [arrayWithImages count]; i++) {
PetalObject *petal = [[PetalObject alloc] initWithImageName:arrayWithImages andRotation:arrayWithAngle andIndex:i andPointsArray:arrayWithPlacePoint andCentrPoint:point];
[self.view addSubview:petal.petalButton];
}
I try to generate some x,y point which should be a creation point of each new button
- (NSMutableArray *)generatePointForRound:(CGPoint )centrPoint andAngle:(NSArray *)arrayAngle andRadius:(float)radiusValue {
CGPoint currentPoint;
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < [arrayAngle count]; i++) {
CGFloat x1 = centrPoint.x + (radiusValue * sin([[arrayAngle objectAtIndex:i] doubleValue]));
CGFloat y1 = centrPoint.y + (radiusValue * cos([[arrayAngle objectAtIndex:i] doubleValue]));
currentPoint = CGPointMake(x1, y1);
[array addObject:[NSValue valueWithCGPoint:currentPoint]];
}
return array;
}
And here is my PetalObject in which i create my buttons
-(id)initWithImageName:(NSArray *)imageNameArray andRotation:(NSArray *)angleArray andIndex:(NSInteger )index andPointsArray:(NSMutableArray *)pointsArray andCentrPoint:(CGPoint )centerPoint {
self.isRecorded = NO;
self.isComplited = NO;
UIImage *image = [UIImage imageNamed:imageNameArray[index]];
CGPoint point = [[pointsArray objectAtIndex:index] CGPointValue];
self.petalButton = [[UIButton alloc] initWithFrame:CGRectMake(point.x - 43 , point.y - 180, 86, 168)];
//self.petalButton.transform = CGAffineTransformMake(cos(angle),sin(angle),-sin(angle),cos(angle),boundsPoint.x-boundsPoint.x*cos(angle)+boundsPoint.y*sin(angle),boundsPoint.y-boundsPoint.x*sin(angle)-boundsPoint.y*cos(angle));
[self.petalButton setBackgroundImage:image forState:UIControlStateNormal];
CGFloat angle = [[angleArray objectAtIndex:index] floatValue];
[self.petalButton setTransform:CGAffineTransformMakeRotation(angle)];
return self;
}
Image 1:
Image 2:
Screenshot
And code:
- (void)viewDidLoad {
[super viewDidLoad];
for (NSInteger index = 0; index < 6; index ++) {
UIButton * button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 50, 20)];
button.layer.anchorPoint = CGPointMake(-0.5, 0.5);
button.backgroundColor = [UIColor greenColor];
button.center = CGPointMake(self.view.center.x + CGRectGetWidth(button.frame) / 2.0, self.view.center.y);
button.transform = CGAffineTransformMakeRotation(M_PI * 2 * index / 6.0);
[self.view addSubview:button];
}
}
The key point is anchorPoint, you set all the button with same anchorPoint and frame, then apply different transform.

Draw a lot of objects with NSBezierPath

Hi i need draw many complex objects, like in image
i draw square and point in it with this code:
CGFloat mmForSqure = self.frame.size.height/6;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(originX, originY, mmForSqure, mmForSqure)];
CGFloat originPoint = mmForSqure/4;
for (int i = 1; i<4; i++) {
for (int j = 1; j<4; j++) {
CGPoint pathPoint = CGPointMake(i*originPoint+originX, j*originPoint+originY);
[path moveToPoint:pathPoint];
[path addLineToPoint:CGPointMake(i*originPoint+originX+1, j*originPoint+originY+1)];
}
}
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.path = path.CGPath;
shapeLayer.fillColor = [UIColor whiteColor].CGColor;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.lineWidth = 1.0;
[self.layer addSublayer:shapeLayer];
and other squares with two loops
CGFloat sizeOfSquared = self.frame.size.height/6;
for (int i = 0; i<=self.frame.size.width/sizeOfSquared; i++) {
for (int j=0; j!=6; j++) {
}
}
But this is slow UI and take much resources, some one have any idea how optimize resources consumption and UI ??
If u don't need any operations on individual squares, u can try making the lines first(all vertical and 5 horizontal) and then add the dots in a loop using self.frame.size.height/6 divided by 4 as the distance between the points and skip after every 3 points. Just a suggestion. Try it if it works. :)

Custom Graphics on UICollectionViewCell

I want to add a grid on each UICollectionViewCell in my UICollectionView. Here is what I tried to do:
- (void) drawRect:(CGRect)rect{
int width = rect.size.width;
int height = rect.size.height;
int i = 0;
[[UIColor blackColor] setStroke];
UIBezierPath* drawingPath = [UIBezierPath bezierPath];
for( i = 0 ; i <= width ; i += width/3) {
[drawingPath moveToPoint:CGPointMake(i, 0)];
[drawingPath addLineToPoint:CGPointMake(i, height)];
NSLog(#"width:%d, height:%d", height, i);
}
for( i = 0 ; i <= height ; i += height/3) {
[drawingPath moveToPoint:CGPointMake(0,i)];
[drawingPath addLineToPoint:CGPointMake(width, i)];
}
[drawingPath stroke];
}
However, I end up with something that looks like this:
The other thing is that I want to color in certain squares in the grid. Is there any simple way of doing that without creating additional subviews? Thanks for all the help!

CALayer breaks in landscape mode

I am coding a custom transition based on the Shutter transition tutorial. it's pretty simple, the first view controller is meant to divide into hundreds of pieces and then fade one at a time, problem however is that it doesn't work in landscape mode. apparently the "contentsRect" method of CALayer (which is supposed to work on the normalized image space) only works in the portrait mode. I have attached my file in here if anyone needs to have a look and please if you can help me.
http://tinyurl.com/88669oy
regards
here is also my trans code:
CALayer* viewLayer;
- (void)transitWithImageView:(UIImageView *)imgView inView:(UIView *)view withImage:(UIImage *)img
{
viewLayer = [imgView layer];
[view.layer addSublayer:viewLayer];
CGSize layerSize = viewLayer.bounds.size;
// BEGINING OF MAKE ARRAY OF TRANSITIONS
NSMutableArray* allTransitionsArr = [[NSMutableArray alloc] init];
for ( int i = 0; i < BLOCK_COUNT; i++ )
{
CABasicAnimation *fade = [CABasicAnimation animationWithKeyPath:#"opacity"];
fade.toValue = [NSNumber numberWithFloat:-1-sqrt(i)];
fade.autoreverses = NO;
fade.duration = DURATION;
[allTransitionsArr addObject: fade];
}
// END OF MAKE ARRAY OF TRANSITIONS
NSMutableArray *blocks = [[NSMutableArray alloc] init];
[viewLayer removeFromSuperlayer];
[CATransaction begin];
[CATransaction setCompletionBlock:^(void)
{
[blocks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
{
[obj setDelegate:nil];
[obj removeFromSuperlayer];
}];
[view.layer addSublayer:viewLayer];
}];
CGFloat bandWidth = layerSize.width / sqrt(BLOCK_COUNT);
CGFloat bandHeight = layerSize.height / sqrt(BLOCK_COUNT);
int yVal = -1;
for(int i = 0; i < BLOCK_COUNT; i++)
{
CALayer *block = [[CALayer alloc] init];
block.masksToBounds = YES;
CGFloat xOffset = 1.f / sqrt(BLOCK_COUNT);
CGFloat yOffset = 1.f / sqrt(BLOCK_COUNT);
block.bounds = CGRectMake(0, 0, bandWidth, bandHeight);
block.bounds = CGRectMake(0.f, 0.f, bandWidth, bandHeight);
block.contents = (id)[img CGImage];
block.contentsGravity = kCAGravityCenter;
CGPoint bandOrigin = viewLayer.frame.origin;
bandOrigin.x = bandOrigin.x + (bandWidth * (i % (int)sqrt(BLOCK_COUNT)));
if (i % (int)sqrt(BLOCK_COUNT) == 0 )
yVal++;
bandOrigin.y = bandOrigin.y + (bandHeight * yVal);
// PROBLEM IS GOT TO BE HERE
block.contentsRect = CGRectMake(xOffset * (i % (int)sqrt(BLOCK_COUNT)) ,yOffset * yVal, xOffset, yOffset);
[block setValue:[NSValue valueWithCGPoint:bandOrigin] forKeyPath:#"frame.origin"];
[view.layer addSublayer:block];
[block addAnimation:[allTransitionsArr objectAtIndex: BLOCK_COUNT - i - 1] forKey:nil];
[blocks addObject:block];
[block release];
}
[CATransaction commit];
[blocks release];
}

Resources