UITableView: offsetting selectedBackgroundView - ios

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)];

Related

How to subclass a class in ObjectiveC correctly?

I write a UITextfield with code in file called login.m
_textfield1 = [[UITextField alloc] initWithFrame:CGRectMake(50, 60, self.view.frame.size.width-50*2, 44)];
_textfield1.placeholder = #"email";
_textfield1.font = [UIFont systemFontOfSize:14.0f];
_textfield1.delegate = self;
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:_textfield1.bounds byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight cornerRadii:CGSizeMake(4, 4)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = _textfield1.bounds;
maskLayer.path = maskPath.CGPath;
_textfield1.layer.mask = maskLayer;
[self.view addSubview:_textfield1];
and i subclass UITextfield with name 'NewTextField', NewTextField.h
#import <UIKit/UIKit.h>
#interface NewTextField : UITextField
#end
NewTextField.m
#import "NewTextField.h"
#implementation NewTextField
- (void)setMaskView:(UIView *)maskView {
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:maskView.bounds byRoundingCorners:UIRectCornerBottomLeft|UIRectCornerBottomRight cornerRadii:CGSizeMake(4, 4)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = maskView.bounds;
maskLayer.path = maskPath.CGPath;
maskView.layer.mask = maskLayer;
}
#end
in file login.m, i update code
_textfield1 = [[NewTextField alloc] initWithFrame:CGRectMake(50, 60, self.view.frame.size.width-50*2, 44)];
_textfield1.placeholder = #"email";
_textfield1.font = [UIFont systemFontOfSize:14.0f];
_textfield1.delegate = self;
[_textfield1 setMaskView:_textfield1];
[self.view addSubview:_textfield1];
it works, but i don't think it is a right way. so how can I subclass a class in right way?
In NewTextField.m instead of - (void)setMaskView:(UIView *)maskView write a initializer method as bellow,
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerBottomLeft|UIRectCornerBottomRight cornerRadii:CGSizeMake(4, 4)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
return self;
}
Technically there is no problem with subclassing UITextField like you've done, maybe I'm stating the obvious because you told us it works :D.
However if you plan just to add a mask to a UITextField, it feels unnecessary to subclass a UITextField, maybe just create helper method or a category to add a the mask to the textfield, that seems like a cleaner approach.

How to add a border to this delimited layer?

I have an image above which I delimitate a transparent rectangle using layer and mask.
I would like this transparent rectangle to be red bordered. But I could find a way to achieve this :
Here is what I have done :
My ViewController has a darkenedView property.
- (void)loadView {
UIView *const rootView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
rootView.backgroundColor = [UIColor whiteColor];
self.view = rootView;
[self addContentSubviews];
}
- (void)addContentSubviews {
UIImageView *const imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"DSC_0823.jpg"]];
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.frame = self.view.bounds;
imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[self.view addSubview:imageView];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self addDarkenedSubview];
}
- (void)viewDidLayoutSubviews {
CGRect const bounds = self.view.bounds;
darkenedView.center = CGPointMake(CGRectGetMidX(bounds), 0);
}
- (void)addDarkenedSubview {
darkenedView = [[UIView alloc] initWithFrame:CGRectMake(10, 30, 400, 1200)];
darkenedView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.6];
darkenedView.autoresizingMask = 0;
[self.view addSubview:darkenedView];
[self addMaskToDarkenedView];
}
- (void)addMaskToDarkenedView {
CGRect bounds = darkenedView.bounds;
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = bounds;
CGRect const myRect = CGRectMake(CGRectGetMidX(bounds) - 100,
CGRectGetMidY(bounds) + 100,
200, 200);
UIBezierPath *path = [UIBezierPath bezierPathWithRect:myRect];
[path appendPath:[UIBezierPath bezierPathWithRect:bounds]];
maskLayer.path = path.CGPath;
maskLayer.fillRule = kCAFillRuleEvenOdd;
darkenedView.layer.mask = maskLayer;
}
I've tried without success :
maskLayer.strokeColor = [UIColor redColor].CGColor;
maskLayer.lineWidth = 3.0f;
Instead of giving stroke to maskLayer object create CAShapeLayer and addSublayer to darkenedView view below is sample code Hope it help
CAShapeLayer *shape = [CAShapeLayer layer];
shape.frame = darkenedView.bounds;
shape.path = path.CGPath;
shape.lineWidth = 3.0f;
shape.strokeColor = [UIColor redColor].CGColor;
shape.fillColor = [UIColor clearColor].CGColor;
[darkenedView.layer addSublayer:shape];

Auto layout constraint and CALayer issue

I am using this code to make my top corners to be rounded.
- (void)setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners
{
UIBezierPath *rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(5.0, 5.0)];
CAShapeLayer *shape = [[CAShapeLayer alloc] init];
[shape setPath:rounded.CGPath];
view.layer.mask = shape;
}
then in initWithFrame of superview method I make this:
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[self setMaskTo:imageView byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight];
But as result of it I don't see image. I am using auto layout constraint that works correct and I see image in case if I don't use rounding corner method above. But if I use it, I don't see image.
Seems if use draw rect callback of my view where image is placed, I can set rounding corners. But in this case if view contains other views it will add rounding to all subviews, if I will cycle it:
It works but for all subviews of course:
- (void)drawRect:(CGRect)rect {
// Drawing code
for (UIView *view in self.subviews)
{
[self setMaskTo:view byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight];
}
}
- (void)setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners
{
UIBezierPath *rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(5.0, 5.0)];
CAShapeLayer *shape = [[CAShapeLayer alloc] init];
[shape setPath:rounded.CGPath];
view.layer.mask = shape;
}
So how to detect when image view has got frame, I suppose that the frame is nil at start CALayer does not understand how to interact with view.
That's happen because you instantiate the view with frame CGRectZero, then you use that bounds for the mask so nothing in the view is inside the mask and therefore nothing is shown.
As a work around you can set a tag to UIImageView instance different than 0 and search in the subviews for that
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
imageView.tag = 111; //or any other number you want
- (void)drawRect:(CGRect)rect {
// Drawing code
for (UIView *view in self.subviews)
{
if (view.tag==111){[self setMaskTo:view byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight];
}}
}
- (void)setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners
{
UIBezierPath *rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(5.0, 5.0)];
CAShapeLayer *shape = [[CAShapeLayer alloc] init];
[shape setPath:rounded.CGPath];
view.layer.mask = shape;
}
If I understand it correctly, you want your UIView, which contains an image inside, to have rounded corners on the top side, right? Then you could just resize the mask layer in layoutSubviews.
Something like:
- (void)awakeFromNib {
[super awakeFromNib];
[self setMaskTo:self byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight];
}
- (void)layoutSubviews {
[super layoutSubviews];
self.layer.mask.frame = self.layer.bounds;
}
- (void)setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners {
UIBezierPath *rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(5.0, 5.0)];
CAShapeLayer *shape = [[CAShapeLayer alloc] init];
[shape setPath:rounded.CGPath];
view.layer.mask = shape;
view.layer.masksToBounds = YES;
}

Why does a CAShapeLayer's stroke extend out of the frame?

Here's sample code:
//Called by VC:
HICircleView *circleView = [[HICircleView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
// init of circle view
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
CAShapeLayer *borderLayer = [CAShapeLayer layer];
borderLayer.fillColor = [UIColor whiteColor].CGColor;
borderLayer.path = [UIBezierPath bezierPathWithOvalInRect:self.frame].CGPath;
borderLayer.strokeColor = [[UIColor redColor] CGColor];
borderLayer.lineWidth = 5;
[self.layer addSublayer:borderLayer];
}
return self;
}
OK, thanks for the answer. to shift i:
CGRect rect = CGRectMake(3, 3, self.frame.size.width, self.frame.size.height);
borderLayer.path = [UIBezierPath bezierPathWithOvalInRect:rect].CGPath;
And made 6 the line width.
Setting the lineWidth draws a line, where the actual path is exactly in the middle of the drawn line.
If you want the drawn line to line up with something, you will have to shift the path by half the lineWidth.
You can shift the path by using - (void)applyTransform:(CGAffineTransform)transform on UIBezierPath and apply a translate transform.
If you want a drawn path to be contained in a certain area, shifting the path doesn't help. In that case just create a smaller path. If you want to draw a 100ptx100pt rect with a line width of 5, you have to draw a path in a 95pt*95pt rect (2.5pt space on either side).
You'd rather choose your view's bounds property for calculation. Frame property won't work properly if it's origin is greater than (0,0). You can use CGRectInsets to adjust your circle's rectangle instead of performing transform calculations. This will automatically position the rectangle centered within the original rectangle.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
CAShapeLayer *borderLayer = [CAShapeLayer layer];
borderLayer.fillColor = [UIColor whiteColor].CGColor;
CGFloat lineWidth = 5;
CGRect rect = CGRectInset(self.bounds, lineWidth / 2, lineWidth / 2);
borderLayer.path = [UIBezierPath bezierPathWithOvalInRect:rect].CGPath;
borderLayer.strokeColor = [[UIColor redColor] CGColor];
borderLayer.lineWidth = lineWidth;
[self.layer addSublayer:borderLayer];
}
return self;
}

Creating a rounded-corner view

Good day!
Im trying to make my view (view in main view) make rounded corner. Im doing like this, but it doesn't work. Any ideas?
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
currenView = [[UIView alloc] init];
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:currenView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(30.0, 30.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = currenView.bounds;
maskLayer.path = maskPath.CGPath;
currenView.layer.mask = maskLayer;
}
return self;
Try something like this:
view.layer.cornerRadius = 5.0;
view.layer.masksToBounds = YES;
for shadow:
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
view.layer.masksToBounds = NO;
view.layer.shadowRadius = 5.0f;
Make sure to import <QuartzCore/QuartzCore.h>
Here is the code. Alloc init a view and send to this method to get corners rounded. You can optionally round any of the corners u want. Also give shadow stroke color.
-(void) setMaskTo:(UIView*)view byRoundingCorners:(UIRectCorner)corners withColor: (UIColor*) color
{
UIBezierPath* rounded = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(9.0, 9.0)];
CAShapeLayer* shape = [[[CAShapeLayer alloc] init] autorelease];
[shape setPath:rounded.CGPath];
shape.strokeColor = [[UIColor grayColor] CGColor];
view.backgroundColor=color;
view.layer.mask = shape;
}
Call the method like this.
[self setMaskTo:ABCView byRoundingCorners:UIRectCornerAllCorners withColor:[UIColor greenColor]];
Stavash's solution seems to be correct, I have used it several times. If you are looking for an alternative or insist on using masklayers, see this answer: https://stackoverflow.com/a/13163693/936957
Your view has no size. its w and h is 0. Try something like,
currentView = [[UIView alloc] initWithFrame:CGRectMake(0,0 200,200)];
and then apply
currentView.layer.cornerRadius = 8.0;
currentView.layer.masksToBounds = YES;
currentView.layer.borderWidth = 1.0;

Resources