I have a UIView that contains some subviews - drawn using UIBezierPath. Every subview contains a scrollview having image as mask. When I try to render image of whole view (overall), instead of making image from path, it draws image of individual rect of subviews. Let me make it clear by figure:
Original View:
What I am getting after rendering Image:
I don't know what am I missing. Please guide me. Thanks.
Code:
IrregularSquare
#interface IrregularSquare : UIView
{
UIScrollView *imageResizer;
CAShapeLayer *maskLayer;
}
#end
#implementation IrregularSquare
- (id)initWithFrame:(CGRect)frame index:(NSInteger) index
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor clearColor];
self.layer.backgroundColor = [UIColor clearColor].CGColor;
}
return self;
}
-(void) drawRect:(CGRect)rect
{
UIBezierPath *aPath = [UIBezierPath bezierPath];
[aPath moveToPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect))];
[aPath addLineToPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect))];
[aPath addLineToPoint:CGPointMake(CGRectGetMaxX(rect) - 20, CGRectGetMidY(rect) - 10)];
[aPath addLineToPoint:CGPointMake(CGRectGetMaxX(rect) - 5, CGRectGetMaxY(rect) - 10)];
[aPath addLineToPoint:CGPointMake(CGRectGetMidX(rect) + 10, CGRectGetMaxY(rect))];
[aPath addLineToPoint:CGPointMake(CGRectGetMidX(rect) - 20, CGRectGetMaxY(rect) - 20)];
[aPath addLineToPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect) - 20)];
[aPath closePath];
aPath.lineWidth = 2;
[[UIColor whiteColor] setStroke];
[aPath stroke];
[self initializeImageWithPath]; //this method adds scrollview to main view and assigns an image to scrollview with mask
}
-(void) initializeImageWithPath
{
CGRect aFrame = somevalue;
self.backImage = [[UIImageView alloc] initWithFrame:aFrame];
self.backImage.contentMode = UIViewContentModeScaleAspectFill;
self.backImage.clipsToBounds = YES;
imageResizer = [[UIScrollView alloc] initWithFrame:self.bounds];
imageResizer.showsHorizontalScrollIndicator = imageResizer.showsVerticalScrollIndicator = NO;
imageResizer.scrollEnabled = YES;
imageResizer.backgroundColor = [UIColor clearColor];
imageResizer.zoomScale = 1.0;
imageResizer.delegate = self;
imageResizer.clipsToBounds = YES;
[imageResizer addSubview:self.backImage];
[self addSubview: imageResizer];
maskLayer = [CAShapeLayer layer];
maskLayer.path = [aPath CGPath];
self.layer.mask = maskLayer;
}
IrregularShapesContainer.m
#implementation IrregularShapesContainer
-(void) drawSubviews
{
for(UIView *aView in self.subviews)
[aView removeFromSuperview];
IrregularSquare *sq1 = [[IrregularSquare alloc] initWithFrame:CGRectMake(HORIPADDING, VERTICALPADDING, (self.frame.size.width - (HORIPADDING * 2) - (SPACING * 2)) * .5, (self.frame.size.height - (VERTICALPADDING) - SPACING) * .5) index:1];
[self addSubview:sq1];
[sq1.layer display];
[self randomlyPlaceImage:sq1];
sq1.irrViewDelegate = self;
//many similar instances are added in following code
}
#end
I hope my Question is clear.. Any help is appreciated.
Related
This code creating only topLeft border, but i want topRight also. How?
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:self.colorSliderBackgroundView.bounds
byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight)
cornerRadii:CGSizeMake(10.0,10.0)];
CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init];
borderLayer.frame = self.colorSliderBackgroundView.bounds;
borderLayer.path = maskPath.CGPath;
borderLayer.lineWidth = 1.5f;
borderLayer.strokeColor = [UIColor colorWithRed:243.0/255.0 green:243.0/255.0 blue:243.0/255.0 alpha:1.0].CGColor;
borderLayer.fillColor = [UIColor clearColor].CGColor;
[self.colorSliderBackgroundView.layer addSublayer:borderLayer];
Based on your code, it looks like you want an outlined rectangle with the top-left and top-right corners rounded, as a sublayer of another view...
Create a new view class, and set it as the custom class of your colorSliderBackgroundView.
TopCornersRoundedView.h
//
// TopCornersRoundedView.h
//
// Created by Don Mag on 10/30/19.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
IB_DESIGNABLE
#interface TopCornersRoundedView : UIView
#end
NS_ASSUME_NONNULL_END
TopCornersRoundedView.m
//
// TopCornersRoundedView.m
// ObjCXIBTest
//
// Created by Don Mag on 10/30/19.
// Copyright © 2019 Don Mag. All rights reserved.
//
#import "TopCornersRoundedView.h"
#interface TopCornersRoundedView ()
#property (strong, nonatomic) CAShapeLayer *borderLayer;
#end
#implementation TopCornersRoundedView
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (void)prepareForInterfaceBuilder {
[super prepareForInterfaceBuilder];
[self commonInit];
}
- (void) commonInit {
// instantiate the shape layer
_borderLayer = [CAShapeLayer new];
// set line width, stroke and fill colors
_borderLayer.lineWidth = 1.5f;
_borderLayer.strokeColor = [UIColor colorWithRed:243.0/255.0 green:243.0/255.0 blue:243.0/255.0 alpha:1.0].CGColor;
_borderLayer.fillColor = [UIColor clearColor].CGColor;
// add the shape layer as a sublayer of self
[self.layer addSublayer:_borderLayer];
}
- (void)layoutSubviews {
[super layoutSubviews];
// create a bezier path with top left and right corners rounded
// doing this in layoutSubviews will keep the frame size correct when
// the view changes size
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds
byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight)
cornerRadii:CGSizeMake(10.0,10.0)];
_borderLayer.frame = self.bounds;
_borderLayer.path = maskPath.CGPath;
}
#end
By designating this class as IB_DESIGNABLE you will even see the result at design-time:
All you need todo is this:
UIView *view = [[UIView alloc] initWithFrame:frame];
CALayer *layer = [CALayer layer];
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:frame byRoundingCorners:(UIRectCornerTopLeft|UIRectCornerTopRight)
cornerRadii:CGSizeMake(3.0, 3.0)];
layer.shadowPath = shadowPath.CGPath;
view.layer.mask = layer;
Or you can check this link for further detail
how to set cornerRadius for only bottom-left,bottom-right and top-left corner textview?
I am creating one custom button class inherit from the UIControl.
I am creating Button with Kite Shape as shown in image using Bizer Path.
Code
.h
import
#interface CustomButton : UIControl
-(instancetype)initWithFrame:(CGRect)frame;
#end
.m
#import "CustomButton.h"
#implementation CustomButton
-(instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
UIBezierPath *path = [UIBezierPath new];
// [path moveToPoint:(CGPoint){0, 0}];
[path moveToPoint:(CGPoint){100,50}];
[path addLineToPoint:(CGPoint){100, 100}];
[path addLineToPoint:(CGPoint){150, 100}];
[path addLineToPoint:(CGPoint){200, 0}];
[path addLineToPoint:(CGPoint){100, 50}];
// Create a CAShapeLayer with this triangular path
// Same size as the original imageView
CAShapeLayer *mask = [CAShapeLayer new];
mask.frame = self.bounds;
mask.path = path.CGPath;
// Mask the imageView's layer with this shape
self.layer.mask = mask;
}
return self;
}
#end
VC .m
CustomButton *button = [[CustomButton alloc]initWithFrame:CGRectMake(20, 20, 200, 200)];
button.backgroundColor = [UIColor redColor];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside] ;
[self.view addSubview:button];
}
- (void)buttonPressed:(id)sender
{
NSLog(#"pressing");
}
Image
My Question Is,
When I clicked outside the button(Red Portion) It gives me a click event.
I do not want to do this.
Please help me,How can i solve it?
Thank You
I am updating my answer for UIBUtton
CustomButton.h
#import <UIKit/UIKit.h>
#interface CustomButton : UIControl
-(instancetype)initWithFrame:(CGRect)frame;
#end
CustomButton.m
#import "CustomButton.h"
#implementation CustomButton
-(instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self)
{
UIBezierPath *path = [UIBezierPath new];
[path moveToPoint:(CGPoint){100,50}];
[path addLineToPoint:(CGPoint){100, 100}];
[path addLineToPoint:(CGPoint){150, 100}];
[path addLineToPoint:(CGPoint){200, 0}];
[path addLineToPoint:(CGPoint){100, 50}];
CAShapeLayer *mask = [CAShapeLayer new];
mask.frame = self.bounds;
mask.path = path.CGPath;
// Mask the imageView's layer with this shape
self.layer.mask = mask;
}
return self;
}
In your controller viewDidLoad
CustomButton *buttonCustom = [[CustomButton alloc] initWithFrame:CGRectMake(20, 20, 200, 200)];
[buttonCustom setBackgroundColor:[UIColor redColor]];
[buttonCustom addTarget:self action:#selector(buttonCustom:forEvent:) forControlEvents:UIControlEventTouchUpInside];
[buttonCustom setTag:1000];
[self.view addSubview:buttonCustom];
Add these methods in your Controller
-(void)buttonCustom:(UIButton *)sender forEvent:(UIEvent*)event{
UITouch *touch = [[event touchesForView:sender] anyObject];
CGPoint location = [touch locationInView:self.view];
UIColor *color = [self colorOfPoint:location];
if([color isEqual:[UIColor redColor]]){
NSLog(#"red ");
}else{
NSLog(#"White Color");
}
}
- (UIColor *) colorOfPoint:(CGPoint)point
{
unsigned char pixel[4] = {0};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pixel, 1, 1, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast);
CGContextTranslateCTM(context, -point.x, -point.y);
[self.view.layer renderInContext:context];
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
UIColor *color = [UIColor colorWithRed:pixel[0]/255.0 green:pixel[1]/255.0 blue:pixel[2]/255.0 alpha:pixel[3]/255.0];
return color;
}
If you have multiple UIButton then use the tag property to distinguish your button.
im learning CoreGraphic and want to make a simple game, but stuck on drawing thing with bezier path, i wanted to draw a triangle inside a subview, but it always come out wrong, i want it to fit 1/4 of the square view
My code:
UIBezierPath* trianglePath = [UIBezierPath bezierPath];
[trianglePath moveToPoint:CGPointMake(0, 0)];
[trianglePath addLineToPoint:CGPointMake(self.mainView.frame.size.width/2, self.mainView.frame.size.height/2)];
[trianglePath addLineToPoint:CGPointMake(self.mainView.frame.size.width, 0)];
[trianglePath closePath];
CAShapeLayer *triangleMaskLayer = [CAShapeLayer layer];
[triangleMaskLayer setPath:trianglePath.CGPath];
UIView *firstView = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.mainView.frame.size.width, self.mainView.frame.size.height)];
firstView.backgroundColor = [UIColor colorWithWhite:.75 alpha:1];
firstView.layer.mask = triangleMaskLayer;
[self.mainView addSubview:firstView];
It look like this:
If the dimensions are incorrect, you probably are creating the triangle before AutoLayout has done its job.
To ensure your self.mainView has the correct size, create your triangle in your controller viewDidLayoutSubviews method.
Also mind that viewDidLayoutSubviews may be called multiple times.
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void) viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
if (self.mainView.subviews.count == 0) {
UIBezierPath* trianglePath = [UIBezierPath bezierPath];
[trianglePath moveToPoint:CGPointMake(0, 0)];
[trianglePath addLineToPoint:CGPointMake(self.mainView.frame.size.width/2, self.mainView.frame.size.height/2)];
[trianglePath addLineToPoint:CGPointMake(self.mainView.frame.size.width, 0)];
[trianglePath closePath];
CAShapeLayer *triangleMaskLayer = [CAShapeLayer layer];
[triangleMaskLayer setPath:trianglePath.CGPath];
UIView *firstView = [[UIView alloc] initWithFrame:CGRectMake(0,0, self.mainView.frame.size.width, self.mainView.frame.size.height)];
firstView.backgroundColor = [UIColor colorWithWhite:.75 alpha:1];
firstView.layer.mask = triangleMaskLayer;
[self.mainView addSubview:firstView];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I've created a custom cell look that creates the look of a smaller cell and the illusion of space between them:
// SETTING UP A UIView IN ORDER TO CREATE ILLUSION OF SEPARATION BETWEEN CELLS
cell.contentView.backgroundColor = [UIColor clearColor];
UIView *clearRoundedCornerView = [[UIView alloc] initWithFrame:CGRectMake(10,10,310,110)];
clearRoundedCornerView.backgroundColor = [UIColor clearColor];
clearRoundedCornerView.layer.masksToBounds = YES;
clearRoundedCornerView.layer.cornerRadius = 3.0;
clearRoundedCornerView.layer.shadowOffset = CGSizeMake(-1, 1);
clearRoundedCornerView.layer.shadowOpacity = 0.5;
// Set up Mask with 2 rounded corners
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:clearRoundedCornerView.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerBottomLeft) cornerRadii:CGSizeMake(20.0, 20.0)];
CAShapeLayer *cornerMaskLayer = [CAShapeLayer layer];
[cornerMaskLayer setPath:path.CGPath];
clearRoundedCornerView.layer.mask = cornerMaskLayer;
// Make a transparent, stroked laker which will display the stroke
CAShapeLayer *strokeLayer = [CAShapeLayer layer];
strokeLayer.path = path.CGPath;
strokeLayer.fillColor = [UIColor clearColor].CGColor;
strokeLayer.strokeColor = [UIColor colorWithRed:(194/255.0) green:(77/255.0) blue:(1/255.0) alpha:1].CGColor;
strokeLayer.lineWidth = 5.0;
// Transparent view that will contain the stroke layer
UIView *strokeView = [[UIView alloc] initWithFrame:clearRoundedCornerView.bounds];
strokeView.userInteractionEnabled = NO; // in case your container view contains controls
[strokeView.layer addSublayer:strokeLayer];
[clearRoundedCornerView addSubview:strokeView];
[cell.contentView addSubview:clearRoundedCornerView];
In the following code I tried to modify selectedBackgroundView to match the view I created. The problem I am running into is that the X Y coordinates won't change and the selectedBackgroundView stays pinned to the upper left corner of the cell.
- (void)installSelectedBackgroundView{
UIView *bgCustomView = [[UIView alloc] initWithFrame:CGRectMake(10,10,310,110)];
bgCustomView.backgroundColor = [UIColor lightGrayColor];
bgCustomView.layer.masksToBounds = YES;
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:bgCustomView.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerBottomLeft) cornerRadii:CGSizeMake(20.0, 20.0)];
CAShapeLayer *cornerMaskLayer = [CAShapeLayer layer];
[cornerMaskLayer setPath:path.CGPath];
bgCustomView.layer.mask = cornerMaskLayer;
self.selectedBackgroundView = bgCustomView;
[self setSelected:YES animated:YES];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self installSelectedBackgroundView];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self installSelectedBackgroundView];
}
Not sure why this isn't working. Changing the X Y in bgCustomView doesn't seem to accomplish anything.
Thank you.
You need to change the origin of the path in installSelectedBackgroundView. As you've noticed, the origin of the bgCustomView is ignored; that's because as the selectedBackgroundView, it will have the same origin as the cell. So, instead, you need to offset the origin of the mask layer's path,
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectOffset(bgCustomView.bounds, 10, 10) byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerBottomLeft) cornerRadii:CGSizeMake(20.0, 20.0)];
Before the thought of posting this question came into my mind I had already spent a few hours on this small snippet of code... So I wrote two different pieces of code in two different classes, and both were used to cut out a specific shape. But for some reason only one of them worked!?!? I have ABSOLUTELY no idea why this happened, for they have the exact same structure.
Below is the snippet that WORKS.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor blackColor];
UIBezierPath *arrowPath = [UIBezierPath bezierPath];
CGPoint start = CGPointMake(self.frame.size.width - 2, self.frame.size.height / 2);
[arrowPath moveToPoint:start];
[arrowPath addLineToPoint:CGPointMake(7, 7)];
[arrowPath addLineToPoint:CGPointMake(2, 7)];
[arrowPath addLineToPoint:CGPointMake(self.frame.size.width - 7, self.frame.size.height/2)];
[arrowPath addLineToPoint:CGPointMake( 2, self.frame.size.height - 7)];
[arrowPath addLineToPoint:CGPointMake(7, self.frame.size.height - 7)];
[arrowPath addLineToPoint:start];
[arrowPath closePath];
UIView *arrow = [[UIView alloc]initWithFrame:self.frame];
arrow.backgroundColor = [UIColor whiteColor];
CAShapeLayer *mask = [CAShapeLayer layer];
mask.frame = self.frame;
mask.backgroundColor = (__bridge CGColorRef)([UIColor clearColor]);
mask.path = arrowPath.CGPath;
arrow.layer.mask = mask;
[self addSubview:arrow];
}
return self;
}
And here is the piece of code that only works PARTIALLY.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
UIView *roundedRectView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height - 20)];
roundedRectView.backgroundColor = [UIColor clearColor];
[self addSubview:roundedRectView];
UIBezierPath *roundedRectPath = [UIBezierPath bezierPathWithRoundedRect:roundedRectView.frame cornerRadius:10];
UIView *rectangle = [[UIView alloc]initWithFrame:roundedRectView.frame];
rectangle.backgroundColor = [UIColor blackColor];
CAShapeLayer *mask = [CAShapeLayer layer];
mask.frame = rectangle.frame;
mask.path = roundedRectPath.CGPath;
mask.backgroundColor = (__bridge CGColorRef)([UIColor clearColor]);
rectangle.layer.mask = mask;
rectangle.alpha = 0.8;
[roundedRectView addSubview:rectangle];
UIView *rectangleTwo = [[UIView alloc]initWithFrame:CGRectMake(0, self.frame.size.height - 20, self.frame.size.width, 20)];
rectangleTwo.backgroundColor = [UIColor redColor];
UIBezierPath *trianglePath = [UIBezierPath bezierPath];
CGPoint start = CGPointMake(rectangleTwo.frame.size.width / 2 - 15, 0);
[trianglePath moveToPoint:start];
[trianglePath addLineToPoint:CGPointMake(start.x + 20, start.y)];
[trianglePath addLineToPoint:CGPointMake(start.x + 10, start.y + 20)];
[trianglePath addLineToPoint:start];
[trianglePath closePath];
CAShapeLayer *triangleMask = [CAShapeLayer layer];
triangleMask.frame = rectangleTwo.frame;
triangleMask.path = trianglePath.CGPath;
rectangleTwo.layer.mask = triangleMask;
[self addSubview:rectangleTwo];
}
return self;
}
Allocating this speech bubble using CGRectMake(0,0,80,60), only the rounded rectangle showed up on the screen, but the triangle was no where to be seen.
Can anyone point out what's wrong with this code? Any help is appreciated, thanks!!
Problem solved!
Turned out that my triangleMask.frame wasn't defined properly. Instead of triangleMask.path = rectangleTwo.frame I should've used triangleMask.frame = CGRectMake(0, 0, rectangleTwo.frame.size.width, rectangleTwo.frame.size.height); instead! CAShapeLayer's origin is relative to that of the shape on which you want to place it!