Rounded UIImageView with border - raw edges - ios

I'm trying to get smooth corners, but without success.
This is how it looks:
I have Subclass of UIImageView where in layoutSubviews I have the logic for putting the rounded corner for uiimageview, and it's clipped to bounds.
But I'm not getting why some images are drawn with these raw edges, and they are OUT of the border layer...
Because it's clipped, I can't make a custom layer with different frame for border, because whole superlayer is clipped.
- (void)layoutSubviews
{
[super layoutSubviews];
[self layoutIfNeeded];
[self.layer setCornerRadius:self.frame.size.width/2];
[self.layer setBorderWidth:BORDERSIZE];
[self.layer setBorderColor:[BORDERCOLOR CGColor]];
[self setClipsToBounds:YES];
}

To make the circular imageview use this following code in viewDidLoad. For clipped to bounds set it to yes.
self.MyImageView.layer.cornerRadius = self.MyImageView.frame.size.width / 2;
self.MyImageView.clipsToBounds = YES;
To apply the border use this code in viewDidLoad:
self.MyImageView.layer.borderWidth = 3.0f;
self.MyImageView.layer.borderColor = [UIColor whiteColor].CGColor;
This will definitely works out.

Related

IOS: UIImageView set border white radius and shadow

Here is my code for set the border, shadow and corner
// set border
[self.avatarImageView.layer setBorderColor: [[UIColor whiteColor] CGColor]];
[self.avatarImageView.layer setBorderWidth: 2.0];
// set shadow
[self.avatarImageView.layer setShadowOffset:CGSizeZero];
[self.avatarImageView.layer setShadowOpacity:1.0];
self.avatarImageView.clipsToBounds = NO;
// set corner
self.avatarImageView.layer.cornerRadius = 10.0;
self.avatarImageView.layer.masksToBounds = YES;
If I only use the code for set border and set corner, it work well like this
But if I add the code set corner, I will got the result like this (the border and corner radius work, but the shadow is gone)
However the code for set corner work perfect if it stay alone.
Please guide me what to do. Any help would be appreciate
Update
Follow #ozgur answer. Add 2 lines to my code, it will give a very beautiful view but the shadow is a little bit smaller
self.avatarImageView.layer.shouldRasterize = YES;
self.avatarImageView.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.avatarImageView.bounds cornerRadius:10].CGPath;
Rounded corners requires masksToBounds to be set to YES. Because of that nothing past the boundaries (like a shadow) can be shown, because it will be masked/clipped off. If you disable masksToBounds so that it will show then the rounded corners wont work because they are unable to mask/clip your image into the rounded shape because you disabled masksToBounds.
Therefor, you can't do both at the same time on one view—so you need TWO views.
You need to make a UIView the same dimensions as your UIImageView and make the UIImageView a subview of your UIView.
Then on your UIImageView set masksToBounds to YES, and on its' superview (the UIView with the same dimensions) set masksToBounds to NO and add the properties accordingly.
Change your code to this: (typed it all w/o using xCode so it's possible I have typos)
UIView *avatarImageViewHolder = [[UIView alloc] initWithFrame:self.avatarImageView.frame];
avatarImageViewHolder.backgroundColor = [UIColor clearColor];
[avatarImageView.superview addSubview:avatarImageViewHolder];
avatarImageViewHolder.center = avatarImageView.center;
[avatarImageViewHolder addSubview:avatarImageView];
avatarImageView.center = CGPointMake(avatarImageViewHolder.frame.size.width/2.0f, avatarImageViewHolder.frame.size.height/2.0f);
self.avatarImageView.layer.masksToBounds = YES;
avatarImageViewHolder.layer.masksToBounds = NO;
// set avatar image corner
self.avatarImageView.layer.cornerRadius = 10.0;
// set avatar image border
[self.avatarImageView.layer setBorderColor: [[UIColor whiteColor] CGColor]];
[self.avatarImageView.layer setBorderWidth: 2.0];
// set holder shadow
[avatarImageViewHolder.layer setShadowOffset:CGSizeZero];
[avatarImageViewHolder.layer setShadowOpacity:1.0];
avatarImageViewHolder.clipsToBounds = NO;
You need to add a container view and move your imageview inside that container view, Use this code after doing so:
CALayer *imageViewLayer= self.imageView.layer;
imageViewLayer.cornerRadius= 20.0f;
imageViewLayer.masksToBounds= YES;
CALayer *containerLayer = self.containerView.layer;
containerLayer.borderColor= [UIColor whiteColor].CGColor;
containerLayer.borderWidth= 3.0f;
containerLayer.cornerRadius= 20.0f;
containerLayer.shadowOffset = CGSizeMake(0, 0);
containerLayer.shadowColor = [UIColor blackColor].CGColor;
containerLayer.shadowRadius = 10.0f;
containerLayer.shadowOpacity = 0.80f;
containerLayer.masksToBounds= NO;
containerLayer.shadowPath = [[UIBezierPath bezierPathWithRect:containerLayer.bounds] CGPath];
Feel free to tweak the settings as per your need. Enjoy!
You should update layer.shadowPath if you don't want the shadow to get clipped:
self.avatarImageView.layer.shouldRasterize = YES;
self.avatarImageView.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.avatarImageView.bounds cornerRadius:10].CGPath;
For this trick to work, clipsToBounds and his brother masksToBounds should be set to NO.
Thus, if you have an image whose size is greater than the avatarView's own bounds, you should define avatarImageView as plain UIView, create another imageView to display the image set its masksToBounds to YES and add it as a subview to avatarImageView so that you'll have a good cornered and shadowed view capable of displaying the clipped image.
You should set self.avatarImageView.clipsToBounds = YES; in set corner

CAShapeLayer is sometimes visible but sometime is not

Inside UICollectionViewCell I add CAShapeLayer circle inside initWithCoder;
So here I didn't do anything unusual, its very simple. But when I launch the app, the circle is sometimes visible, but sometimes is not.
I logged the circle where I saw that the color, opacity, path and frame is set properly. But on the screen there's no circle. If I begin scrolling collection view, the circle appears right away. Also if I touch any other button or scroll on the screen. I even try to make a screenshot for you to see it, but at the moment I take a screenshot, the circle appears right away. So I can't show the problem to you.
I thing it's possible that this is a mistake made inside the iOS. Is there a way to force to redraw the circle.
here is my initializor
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
CAShapeLayer *circle = [CAShapeLayer layer];
circle.contentsScale = SCREEN_SCALE;
circle.rasterizationScale = 2.0 * SCREEN_SCALE;
circle.shouldRasterize = YES;
circle.opacity = 1.0;
circle.backgroundColor = [UIColor clearColor].CGColor;
circle.fillColor = [UIColor grayColor].CGColor;
CGRect frame = CGRectMake(0, 0, 50, 50);
circle.frame = frame;
circle.path = [UIBezierPath bezierPathWithOvalInRect:frame].CGPath;
[self.layer addSublayer:circle];
}
return self;
}
I think I didnt do anything wrong, there didnt do anything else with the circle elsewhere in the code.

Setting corner radius on a cell kills UICollectionView's performance

I have a UICollectionView that has only a few cells (about 20). Performance for this collection works great. However, as soon as I try to round the corners of the UICollectionViewCells that are being rendered by this view, my performance takes a significant hit. In my cell's init method, this is the only line I add to cause this:
[self.layer setCornerRadius:15];
Since this is in the init method and I am reusing the cells properly, I don't see why this should be causing me issue.
I have tried adjusting the rasterization and opacity of the sell using multiple combinations of the following, with still no effect:
[self.layer setMasksToBounds:YES];
[self.layer setCornerRadius:15];
[self.layer setRasterizationScale:[[UIScreen mainScreen] scale]];
self.layer.shouldRasterize = YES;
self.layer.opaque = YES;
Is their some setting or trick to improve the performance of a UICollectionView that has cells with rounded corners?
As #Till noted in comments, a prerendered image should solve your performance problem. You can put all the corner rounding, shadowing, and whatever other special effects into that instead of needing CA to render them on the fly.
Prerendered images don't lock you into a static content size, either: look into the UIImage resizable image stuff. (That's still way faster than CA rendering every frame.)
I have found that this is caused entirely because of the call to dequeuereusablecellwithidentifier. Each time this is called, the cell with rounded corners needs to be re-rendered. If the collection view did not remove them from the view when the item scrolled off the screen, then the performance would not be affected (as long as their wasn't too many items in the collection that is). Seems like a double edged sword - both ways have their limits.
There is a code for UIView subclass, which is provide you a view with opaque rounded borders and transparent hole in the middle.
You should create needed view as usual and after that you can add view with hole over your view. Visualisation is here.
It works if you use one-colored background for UICollectionView or UITableView, you can add following subview for each cell:
#interface TPRoundedFrameView : UIView
#property (assign, nonatomic) CGFloat cornerRadius;
#property (strong, nonatomic) UIColor * borderColor;
#end
#implementation TPRoundedFrameView
- (instancetype)init {
if ((self = [super init])) {
self.opaque = NO;
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
UIBezierPath * path = [UIBezierPath bezierPathWithRect:rect];
UIBezierPath * innerPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:self.cornerRadius];
[path appendPath:[innerPath bezierPathByReversingPath]];
[self.borderColor set];
[path fill];
}
#end
Example for target cell class:
- (void)awakeFromNib {
[super awakeFromNib];
// creating holed view with rounded corners
self.myRoundedView.backgroundColor = [UIColor whiteColor];
TPRoundedFrameView * roundedFrame = [TPRoundedFrameView new];
roundedFrame.cornerRadius = 5.f;
roundedFrame.borderColor = [UIColor groupTableViewBackgroundColor];
// add borders to your view with appropriate constraints
[self.myRoundedView addSubview:roundedFrame];
roundedFrame.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary * views = NSDictionaryOfVariableBindings(roundedFrame);
NSArray * horizontal = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[roundedFrame]-0-|" options:0 metrics:nil views:views];
NSArray * vertical = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[roundedFrame]-0-|" options:0 metrics:nil views:views];
[self.myRoundedView addConstraints:horizontal];
[self.myRoundedView addConstraints:vertical];
}
Result:
Table with rounded views as cells
I fixed all of my performance borderRadius issues by applying this radius on the contentView instead of the cell itself.
self.contentView.layer.borderWidth = 1.0f;
self.contentView.layer.cornerRadius = 5.0f;
self.contentView.layer.borderColor = [UIColor colorWithRed:202/255. green:202/255. blue:202/255. alpha:1.0].CGColor;

Flipping a circular UIView

I'm working on a project that has circular views that flip over.
So far, I've tried a couple of the solutions provided here on SOflow:
simply rounding the corners of the view's layer.
creating a custom CircleView that sets backgroundColor to [UIColor clearColor] and manually drawing the circle on in drawRect.
In each case, when the view flips to the new view, it's a rectangular portion of the screen that flips, not just the circle. I'd like the flip to just be the circle, but I'm not sure where to go next.
Here's the method I'm using to flip the view currently:
-(void)changeSpotToView:(SpotView *)toView {
[UIView transitionFromView:self.activeView
toView:toView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromTop
completion:^(BOOL finished) {
self.activeView = toView;
}];
}
The container the SpotViews are in is also a SpotView, so it should be just a circle as well.
Any guidance would be appreciated!
The problem is transitionFromView:toView:… animates the superview of the fromView.
I reproduced the problem:
I was able to fix it in my test case simply by embedding my fromView and toView in a container view with a clear background. Here's my working version:
My nib looks like this:
I have bounds rectangles enabled (Editor > Canvas > Show Bounds Rectangles), so you can see the outline of the container view, which is bigger than its subviews.
I have a red view and a blue view as subviews of the container view. The blue view is behind the red view and has the same frame. I apply circular masks to them in code.
Here's my code:
- (void)viewDidLoad {
[super viewDidLoad];
self.blueView.hidden = YES;
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self addCircleMaskToView:self.blueView];
[self addCircleMaskToView:self.redView];
}
- (void)addCircleMaskToView:(UIView *)view {
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = view.bounds;
maskLayer.path = [UIBezierPath bezierPathWithOvalInRect:view.bounds].CGPath;
maskLayer.fillColor = [UIColor whiteColor].CGColor;
view.layer.mask = maskLayer;
}
- (IBAction)buttonWasTapped:(id)sender {
[UIView transitionFromView:self.redView toView:self.blueView
duration:1.0 options:UIViewAnimationOptionTransitionFlipFromTop
| UIViewAnimationOptionShowHideTransitionViews
completion:nil];
}

iOS slide up an image over an image, revealing the underneath one. (A-la jQuery.slide())

I'm trying to do the following thing -
I have two version of the same image, one colored and one black and white. I want the B&W to "Slide up" revealing the one under it.
I tried setting the height and the frame, but couldn't get it to work like I want it to.
For example , this would be a combination of both UIImages where one is revealing the other, while not moving:
Any ideas? :)
OK, here is a different method using a mask on the grey view. I actually tested this with a blueView and a greyView, where the grey covers up the blue. Put a mask layer on the grey layer so that the grey layer is visible. Now animate the mask away from the grey layer, making the grey layer disappear without moving it, the blue shows thru where the grey disappears.
If you want to experiment with this, create an empty project in XCode with a single view, and put this code inside viewDidLoad. That's how I tested this.
self.view.backgroundColor = [UIColor whiteColor];
UIView* blueView = [[[UIView alloc] init] autorelease];
blueView.backgroundColor = [UIColor blueColor];
blueView.frame = CGRectMake(50,50,100,100);
UIView* grayView = [[[UIView alloc] init] autorelease];
grayView.backgroundColor = [UIColor grayColor];
grayView.frame = CGRectMake(50,50,100,100);
[self.view addSubview:blueView];
[self.view addSubview:grayView]; // gray covers the blue
// Create a mask to allow the grey view to be visible and cover up the blue view
CALayer* mask = [CALayer layer];
mask.contentsScale = grayView.layer.contentsScale; // handle retina scaling
mask.frame = grayView.layer.bounds;
mask.backgroundColor = [UIColor blackColor].CGColor;
grayView.layer.mask = mask;
// Animate the position of the mask off the grey view, as the grey becomes unmasked,
// that part of the grey "dissappears" and is no longer covering the blue underneath
CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:#"position"];
a.duration = 4;
a.fromValue = [NSValue valueWithCGPoint:mask.position];
CGPoint newPosition = mask.position;
newPosition.y += mask.bounds.size.height;
a.toValue = [NSValue valueWithCGPoint:newPosition];
a.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[mask addAnimation:a forKey:#"colorize"]; // animate presentation
mask.position = newPosition; // update actual position
Don't forget this line at the top with the #imports:
#import <QuartzCore/QuartzCore.h>
One way is to stack the UIView's so that the B&W image on top of the color image, where the color image is completely covered (hidden) by the B&W image.
[self.view insertSubview:bwImage aboveSubview:colorImage];
Only the B&W view is visible. Now set clipsToBounds on the B&W view:
bwImage.clipsToBounds = YES;
Finally animate the height in the bounds of the bwImage so that its height shrinks down to 0, clipping the B&W image and revealing the color image behind it. Something like this:
[UIView animateWithDuration:1.0 animations:^{
CGRect b = bwImage.bounds;
b.size.height = 0;
bwImage.bounds = b;
}];
I haven't tried this but hopefully you get the idea.

Resources