How to subclass a class in ObjectiveC correctly? - ios

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.

Related

Corner Radius Using UIBezierPath

I'm Trying to round my view's corners using "UIBezierPath". I Only need to round topRight and Top left.
I have used Following code
-(void)setMaskOnView:(UIView *)givenView
{
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:givenView.bounds byRoundingCorners: (UIRectCornerTopLeft|UIRectCornerTopRight) cornerRadii:CGSizeMake(10.0, 10.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = givenView.bounds;
maskLayer.path = maskPath.CGPath;
givenView.layer.mask = maskLayer;
}
But my TopRight Corner does not round.
I've used
UIRectCornerAllCorners
But it does not round my right corners
Any thing i'm missing??
I suggest different approach. Load image with rounded top corners and set is as contents of CALayer. Set this layer as mask of your view layer. Update size of your mask layer in layoutSubivews of a given view or viewDidLayoutSubviews of given view controller.
Loading image as layer contenst
CALayer *maskLayer = [[CALayer alloc] init];
UIImage *maskImage = [UIImage imageNamed:#"mask_image.png" inBundle:[NSBundle mainBundle] compatibleWithTraitCollection:nil];
maskLayer.contents = (__bridge id _Nullable)(maskImage.CGImage);
mainLayer.mask = maskLayer
[EDIT] Answering your questions in comments
Either using CAShapeLayer or image as mask you have to resize your mask layer so it will have the same size as masked layer. If we are talking about UITableViewCell create your own derived cell and update mask shape in layoutSubviews . Below is example code (MyTableCell is loaded from storyboard):
#interface MyTableCell ()
#property (nonatomic, strong) CAShapeLayer *maskLayer;
#end
#implementation MyTableCell
- (void)awakeFromNib
{
self.maskLayer = [[CAShapeLayer alloc] init];
self.layer.mask = self.maskLayer;
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.maskLayer.path = [self maskPath].CGPath;
}
- (UIBezierPath *)maskPath
{
return [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners: (UIRectCornerTopLeft|UIRectCornerTopRight) cornerRadii:CGSizeMake(10.0, 10.0)];
}
#end
I'm using this subclass
.h
#interface UIView (custom)
- (UIView *)setRoundedCorners:(UIRectCorner)corners withRadius:(CGFloat)radius;
#end
.m
#implementation UIView (custom)
- (UIView *)setRoundedCorners:(UIRectCorner)corners withRadius:(CGFloat)radius
{
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds
byRoundingCorners:corners
cornerRadii:CGSizeMake(radius, radius)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
return self;
}
#end
Using it like:
[YOURVIEW setRoundedCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight | UIRectCornerTopLeft | UIRectCornerTopRight withRadius:15];

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

UITableView: offsetting selectedBackgroundView

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

Apply CAShapeLayer to multiple UIImageViews inside Cells

I'm trying to round the top corners of a UIImageView inside a UICollectionViewCell. It worked with my first code but the UICollectionViews performance went way down and started lagging. In a attempt to prevent this I tried to create a variable and just generate a CAShapeLayer for the first cell and then apply the same mask to the rest of the cells. Here is my code:
In cellForItemAtIndexPath
In if(maskLayer == nil){
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:cell.image.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(3.0, 3.0)];
maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = cell.image.bounds;
maskLayer.path = maskPath.CGPath;
}
cell.image.layer.mask = maskLayer;
...
Far above:
#implementation MainViewViewController{
CAShapeLayer *maskLayer;
}
But this does not work. The mast is only getting applied to the last cell that comes in to view when scrolling and gets removed from the rest. Could this solution even work and why is this not working?
Thanks in advance!
EDIT 1
Tried to creata a subclass of UIImageView called RoundedImageView as suggested by #debugger and now the code looks like this:
RoundedImageView.m:
#import "RoundedImageView.h"
#implementation RoundedImageView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)viewDidLoad{
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(3.0, 3.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
UIBezierPath *maskPath;
maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(3.0, 3.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
In my storyboard I have set the UIImageView class to RoundedImagView and in my custom UICollectionViewCell:
#property (strong, nonatomic) IBOutlet RoundedImageView *image;
But I the viewDidLoad or drawRect in RoundedImageView.m is not getting called at all.
for that you will have to create UIImageview extension class name RoundImageview. and add the following code in the ViewDidLoad method into class:
self.contentMode = UIViewContentModeScaleAspectFill;
self.clipsToBounds = YES;
self.layer.cornerRadius = self.frame.size.width/2; // Setting the corner radius
self.layer.masksToBounds = YES;
Now in the viewcontroller class add the UIImageview extension in the header and intitialize like below:
RoundImageview *roundimageview;
and in the cellForRowAtIndexPath method use the following method to add the image:
rounfimageview.image = yourimagename
I think above method will work..just try and test it.
Create CAShapeLayer for cell. When you create a single instance and try to add for all cell, it will add to the last one.
cell.image.layer.mask = maskLayer;
Here it will not create new copy when you assign masklayer.
Edit:
How single instance of layer can be sublayer for more than one layer?.
In cellForRowAtIndexPath: do the following
if (!cell) {
// create cell
//And create mask layer
cell.image.layer.mask = maskLayer; // - And do this
}
You don't need to override drawRect:, and you don't want to put code in viewDidLoad, because that's a UIViewController method, and won't be called in a UIImageView subclass. Since the image view is made in the storyboard, you should put your code in the initWithCoder: method of the UIImageView subclass,
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight) cornerRadii:CGSizeMake(3.0, 3.0)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
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