Circular UIImageView in UITableView without performance hit? - ios

I have a UIImageView on each of my UITableView cells, that display a remote image (using SDWebImage). I've done some QuartzCore layer styling to the image view, as such:
UIImageView *itemImageView = (UIImageView *)[cell viewWithTag:100];
itemImageView.layer.borderWidth = 1.0f;
itemImageView.layer.borderColor = [UIColor concreteColor].CGColor;
itemImageView.layer.masksToBounds = NO;
itemImageView.clipsToBounds = YES;
So now I have a 50x50 square with a faint grey border, but I'd like to make it circular instead of squared. The app Hemoglobe uses circular images in table views, and that's the effect I'd like to achieve. However, I don't want to use cornerRadius, as that degrades my scrolling FPS.
Here's Hemoglobe showing circular UIImageViews:
Is there any way to get this effect? Thanks.

Simply set the cornerRadius to half of the width or height (assuming your object's view is square).
For example, if your object's view's width and height are both 50:
itemImageView.layer.cornerRadius = 25;
Update - As user atulkhatri points out, it won't work if you don't add:
itemImageView.layer.masksToBounds = YES;

To Add border
self.ImageView.layer.borderWidth = 3.0f;
self.ImageView.layer.borderColor = [UIColor whiteColor].CGColor;
For Circular Shape
self.ImageView.layer.cornerRadius = self.ImageView.frame.size.width / 2;
self.ImageView.clipsToBounds = YES;
Refer this link
http://www.appcoda.com/ios-programming-circular-image-calayer/

Use this code..
This will be helpful..
UIImage* image = ...;
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
// Add a clip before drawing anything, in the shape of an rounded rect
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds
cornerRadius:50.0] addClip];
// Draw your image
[image drawInRect:imageView.bounds];
// Get the image, here setting the UIImageView image
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
// Lets forget about that we were drawing
UIGraphicsEndImageContext();
It works fine for me. :)

Here is a more up to date method in swift using IBDesignable and IBInspectable to subclass UIImageView
#IBDesignable class RoundableUIImageView: UIImageView {
private var _round = false
#IBInspectable var round: Bool {
set {
_round = newValue
makeRound()
}
get {
return self._round
}
}
override internal var frame: CGRect {
set {
super.frame = newValue
makeRound()
}
get {
return super.frame
}
}
private func makeRound() {
if self.round == true {
self.clipsToBounds = true
self.layer.cornerRadius = (self.frame.width + self.frame.height) / 4
} else {
self.layer.cornerRadius = 0
}
}
override func layoutSubviews() {
makeRound()
}
}

Yes, it is possible to give layer.cornerRadius (need to add
#import <QuartzCore/QuartzCore.h>)
for create circular any control but in your case instead of set layer of UIImageView it is The
Best Way to Create Your Image as circular and add it on UIImageView Which have backGroundColor is ClearColor.
Also refer this Two Source of code.
https://www.cocoacontrols.com/controls/circleview
and
https://www.cocoacontrols.com/controls/mhlazytableimages
This might be helpful in your case:

Set the height & width of the of the UIImageView to be the same e.g.:Height=60 & Width = 60, then the cornerRadius should be exactly the half.I have kept it 30 in my case.It worked for me.
self.imageView.layer.cornerRadius = 30;
self.imageView.layer.borderWidth = 3;
self.imageView.layer.borderColor = [UIColor whiteColor].CGColor;
self.imageView.layer.masksToBounds = YES;

Usable solution in Swift using extension:
extension UIView{
func circleMe(){
let radius = CGRectGetWidth(self.bounds) / 2
self.layer.cornerRadius = radius
self.layer.masksToBounds = true
}
}
Usage:
self.venueImageView.circleMe()

If use some autolayout and different cells heights, u better do it this way:
override func layoutSubviews() {
super.layoutSubviews()
logo.layer.cornerRadius = logo.frame.size.height / 2;
logo.clipsToBounds = true;
}

I use a Round Image View class…
So I just use it instead of UIImageView, and have nothing to tweak…
This class also draws an optional border around the circle. There is often a border around rounded pictures.
It is not a UIImageView subclass because UIImageView has its own rendering mechanism, and does not call the drawRect method..
Interface :
#import <UIKit/UIKit.h>
#interface MFRoundImageView : UIView
#property(nonatomic,strong) UIImage* image;
#property(nonatomic,strong) UIColor* strokeColor;
#property(nonatomic,assign) CGFloat strokeWidth;
#end
Implementation :
#import "MFRoundImageView.h"
#implementation MFRoundImageView
-(void)setImage:(UIImage *)image
{
_image = image;
[self setNeedsDisplay];
}
-(void)setStrokeColor:(UIColor *)strokeColor
{
_strokeColor = strokeColor;
[self setNeedsDisplay];
}
-(void)setStrokeWidth:(CGFloat)strokeWidth
{
_strokeWidth = strokeWidth;
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGPathRef path = CGPathCreateWithEllipseInRect(self.bounds, NULL);
CGContextAddPath(ctx, path);
CGContextClip(ctx);
[self.image drawInRect:rect];
if ( ( _strokeWidth > 0.0f ) && _strokeColor ) {
CGContextSetLineWidth(ctx, _strokeWidth*2); // Half border is clipped
[_strokeColor setStroke];
CGContextAddPath(ctx, path);
CGContextStrokePath(ctx);
}
CGPathRelease(path);
}
#end

Creating circular image view and thats quite easy with the below SO link, just have custom cells for your table view instead of allocating every thing in your cellForRowAtIndexPath method.
how to make button round with image background in IPhone?
The above link gives you example for buttons just use it for your image views.

If you showing the images over a solid background color, an easy solution could be to use an overlay image with a transparent circle in the middle.
This way you can still use square images and add the circle image above them to get the circular effect.
If you don't need to manipulate your images or show them on a complex background color, this can be a simple solution with no performance hit.

In swift, inside your viewDidLoad method, with the userImage outlet:
self.userImage.layer.borderWidth = 1;
self.userImage.layer.borderColor = UIColor.whiteColor().CGColor;
self.userImage.layer.cornerRadius = self.userImage.frame.size.width / 2;
self.userImage.clipsToBounds = true;

In Swift use this Extension for CircularImageView or RoundedImageView :
extension UIView {
func circular(borderWidth: CGFloat = 0, borderColor: UIColor = UIColor.whiteColor()) {
let radius = CGRectGetWidth(self.bounds) / 2
self.layer.cornerRadius = radius
self.layer.masksToBounds = true
self.layer.borderWidth = borderWidth
self.layer.borderColor = borderColor.CGColor
}
func roundedCorner(borderWidth: CGFloat = 0, borderColor: UIColor = UIColor.whiteColor()) {
let radius = CGRectGetWidth(self.bounds) / 2
self.layer.cornerRadius = radius / 5
self.layer.masksToBounds = true
self.layer.borderWidth = borderWidth
self.layer.borderColor = borderColor.CGColor
}
}
Usage:
self.ImageView.circular()
self.ImageView.roundedCorner()

For circular imageview, use the below code.
self.imageView.layer.cornerRadius = self.imageView.frame.size.width / 2;
self.imageView.clipsToBounds = true
Don't forget to change the cornerRadius in viewDidLayoutSubviews method of your UIView.
Example:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.imageView.layer.cornerRadius = self.imageView.frame.size.width / 2;
self.imageView.clipsToBounds = true
}

Related

How to make Circle image like Instagram's story profile image

I want to create circle image like the one in Instagram's story profile image. It should has two circles, inner circle will be white, outer circle color will be self-color not gradient. I have tried that code but there is only one circle. How to add second circle to imageView layer ?
self.imageView.layer.cornerRadius = 30;
self.imageView.layer.borderWidth = 3;
self.imageView.layer.borderColor = [UIColor whiteColor].CGColor;
self.imageView.layer.masksToBounds = YES;
Could you help me?
Thanks :)
You can use this 3rd party library which provides multiple borders to an UIImageView.
And, If you want to do it by yourself, you can make UIImageView a subView of UIView. Then make the UIView round and set borderWidth and colour in it. Do the same for the UIImageView.
Here's an example:
Add your UIView and UIImageView like this.
Then add this code:
-(void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
_profileImage.layer.cornerRadius = 30;
_profileImage.layer.borderWidth = 3;
_profileImage.layer.borderColor = [UIColor whiteColor].CGColor;
_profileImage.layer.masksToBounds = YES;
_profileImage.clipsToBounds = true;
_profileImage.layer.cornerRadius = _profileImage.frame.size.height/2;
_superViewImage.clipsToBounds = true;
_superViewImage.layer.masksToBounds = true;
_superViewImage.layer.cornerRadius = _superViewImage.frame.size.height/2;
_superViewImage.layer.borderWidth = 0.5;
_superViewImage.layer.borderColor = (__bridge CGColorRef _Nullable)([UIColor greenColor]);
}
And result image will be this:
Set the colours you want for both borders.
Hope it helps. :)

UILabel Background Color Leaks to Border

I'm creating a UILabel to which I set the background color and corner radius with the following code:
self.scoreLabel.backgroundColor = [UIColor DISRed];// custom red`
self.scoreLabel.layer.masksToBounds = YES;
self.scoreLabel.layer.cornerRadius = self.scoreLabel.frame.size.width/2;
self.scoreLabel.layer.borderWidth = 8.0;
self.scoreLabel.layer.borderColor = [[UIColor DISNavy] CGColor];
However the background's color seems to be leaking to the edge of the border (see image). Any ideas why? Any idea on how to fix it?
I was also facing the same problem. It was a silly mistake. I always forget to tick clipToBounds in case of cornerRadius.
So, just ticking the Clip to Bounds for UILabel in Storyboard fixed my problem.
And yes, we need to keep the below code too:
label.layer.masksToBounds = true
I ran into the same problem with the UIButton's background color leaking around the edge of its border.
Instead of setting the UIButton background color on the UIButton, set it on the UIButton's layer.
Replace:
self.scoreLabel.backgroundColor = [UIColor DISRed];// custom red`
With this:
self.scoreLabel.layer.backgroundColor = [[UIColor DISRed] CGColor];// custom red`
I created my own UILabel and background colour does not seem to be leaking.
Write this in .h file of your project.
UILabel *label;
Write this in .m file of your project.
label=[[UILabel alloc]initWithFrame:CGRectMake(100, 300, 100, 100)];//Set frame of label in your viewcontroller.
[label setBackgroundColor:[UIColor redColor]];//Set background color of label.
[label setText:#"Label"];//Set text in label.
[label setTextColor:[UIColor blackColor]];//Set text color in label.
[label setTextAlignment:NSTextAlignmentCenter];//Set text alignment in label.
[label.layer setCornerRadius:50.0];//Set corner radius of label to change the shape.
[label.layer setBorderWidth:8.0f];//Set border width of label.
[label setClipsToBounds:YES];//Set its to YES for Corner radius to work.
[label.layer setBorderColor:[UIColor greenColor].CGColor];//Set Border color.
[self.view addSubview:label];//Add it to the view of your choice.
It is probably anti aliasing issue. you can better fix it by adding a bezier path around the corners.
CAShapeLayer *subLayer = [[CAShapeLayer alloc] init];
[subLayer setFillColor:[UIColor clearColor].CGColor];
[subLayer setStrokeColor:[UIColor whiteColor].CGColor];
[subLayer setLineWidth:1.0];
[subLayer setPath:[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.layer.cornerRadius].CGPath];
[imageView.layer addSublayer:subLayer];
For those who are still facing the issue of border color leaking out:
Go through the below code, please note you will need to set frames & border width as per your requirement, I'm setting the position as view's center
let badgeSize: CGFloat = 10
let redBadge = UIView(frame: CGRect(x: view.center.x, y:view.center.y, width: badgeSize, height: badgeSize))
redBadge.layer.borderColor = UIColor.white.cgColor
redBadge.layer.borderWidth = 2
redBadge.backgroundColor = .red
redBadge.layer.cornerRadius = badgeSize * 0.5
redBadge.clipsToBounds = true
redBadge.layer.masksToBounds = true
redBadge.maskLayerOnView(radius: badgeSize * 0.5)
view.addSubview(redBadge)
Secondly, we need to write an extension on UIView
extension UIView{
func maskLayerOnView(radius: CGFloat){
let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.allCorners],
cornerRadii: CGSize(width: radius,
height: radius)).cgPath
self.layer.mask = maskLayer
}
}
This code snippet removes the border color separating out, one can replicate this behaviour on any kind of views.
For detailed explanation please see this article.
Well, a lot of answers...
I found the problem persists, as long as the UIViews background color is used and not the background color of the UIViews 'layer'. In addition, of course, masking needs to be enabled.
For a UICollectionViewCell subclass the code is:
- (void)prepare
{
self.contentView.layer.backgroundColor = UIColor.redColor.CGColor;
self.contentView.layer.cornerRadius = 25.0;
self.contentView.layer.borderColor = UIColor.blackColor.CGColor;
self.contentView.layer.borderWidth = 1.0;
//Enable to optimize image views: self.contentView.layer.shouldRasterize = YES;
//Enable to optimize image views: self.contentView.layer.rasterizationScale = UIScreen.mainScreen.scale;
self.contentView.layer.masksToBounds = YES;
self.contentView.clipsToBounds = YES;
}
To make the setting of the background color more comfortable and less error prone, some could add this:
/*
setBackgroundColor:
*/
- (void)setBackgroundColor:(UIColor *)p_BackgroundColor
{
super.backgroundColor = nil;
self.contentView.layer.backgroundColor = p_BackgroundColor.CGColor;
self.contentView.layer.masksToBounds = YES;
self.contentView.clipsToBounds = YES;
}

iOS - Shadow won't resize correctly

I have a tableView with dynamic cell sizes, and having trouble resizing the shadow when the cells are being reused. This solution works on iOS8 but not on iOS7. Also the shadows show up correctly based on the cell size the first time the shadow is create, but they break after cells are being reused.
#implementation
static const CGFloat shadowWithInset = 2;
static const CGFloat shadowHeightOutset = 4;
- (void)awakeFromNib {
CALayer *layer = self.innerView.layer;
layer.shadowOffset = CGSizeMake(1, 1);
layer.shadowRadius = 3.0f;
layer.shadowColor = [[UIColor gray] CGColor];
layer.shadowOpacity = 0.8f;
//layer.shouldRasterize = YES;
//layer.rasterizationScale = [UIScreen mainScreen].scale;
CGSize size = layer.bounds.size;
layer.shadowPath = [UIBezierPath bezierPathWithRect:CGRectMake(shadowWithInset, size.height-shadowHeightOutset, size.width-(shadowWithInset*2), shadowHeightOutset)].CGPath;
}
- (void)layoutSubViews {
[super layoutSubviews];
CGSize size = self.innerView.bounds.size;
layer.shadowPath = [UIBezierPath bezierPathWithRect:CGRectMake(shadowWithInset, size.height-shadowHeightOutset, size.width-(shadowWithInset*2), shadowHeightOutset)].CGPath;
}
#end
After populating all textfields in cellForRowAtIndexPath I ended up doing this:
[self setNeedsLayout];
[self layoutIfNeeded];

Trying to create a custom horizontal UIPickerView using UIScrollView - how do I fade the edges out like in UIPickerView?

In iOS 7, the UIPickerViews have a nice shadow on the text as it gets closer to the edges:
If I'm using a UIScrollView, is it possible to achieve a similar effect where the text at the edges is slightly shadowed/blended into the background?
See here and here for my answers to similar questions.
My solution was to subclass UIScrollView, and create a mask layer in the layoutSubviews method.
The code in the above answers is also here on github.
Hope that helps!
I did a similar thing with this class to fade the left and right edges of a scrolling marquee. Much less hassle than using two images as you can resize or recolor it easily:
#interface MarqueeFadeOverlay : UIView
#property(assign, nonatomic) CGGradientRef gradientLayer;
- (id)initWithFrame:(CGRect)frame andEndFadeWidth:(float)fadeWidth andFadeBarColor:(UIColor *)color;
#end
#implementation MarqueeFadeOverlay {
UIColor *fadeBarColor;
float endFadeWidth;
}
- (id)initWithFrame:(CGRect)frame andEndFadeWidth:(float)fadeWidth andFadeBarColor:(UIColor *)color {
self = [super initWithFrame:frame];
if (self) {
endFadeWidth = fadeWidth;
fadeBarColor = color;
[self setOpaque:NO];
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawLinearGradient(context, self.gradientLayer, CGPointMake(0.0, 0.0),
CGPointMake(self.frame.size.width, 0.0), kCGGradientDrawsBeforeStartLocation);
}
- (CGGradientRef)gradientLayer {
if (_gradientLayer == nil) {
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat *colorComponents = [self rgbaFrom:fadeBarColor];
if (endFadeWidth == 0) endFadeWidth = 0.1;
CGFloat locations[] = {0.0, endFadeWidth, 1.0 - endFadeWidth, 1.0};
CGFloat colors[] = {colorComponents[0], colorComponents[1], colorComponents[2], 1.00,
colorComponents[0], colorComponents[1], colorComponents[2], 0,
colorComponents[0], colorComponents[1], colorComponents[2], 0,
colorComponents[0], colorComponents[1], colorComponents[2], 1.00};
_gradientLayer = CGGradientCreateWithColorComponents(colorSpace, colors, locations, sizeof(colors) / (sizeof(colors[0]) * 4));
CGColorSpaceRelease(colorSpace);
}
return _gradientLayer;
}
-(CGFloat *)rgbaFrom:(UIColor *)color {
CGColorRef colorRef = [color CGColor];
return (CGFloat *) CGColorGetComponents(colorRef);
}
You just init one with a frame, a fade width between 0 and 1 and a color to match the background, and add to the parent view.
I would place two custom overlay views for this that you place on top of the scroll view. The views could either contain a pre-made image with a gradient from full white to an alpha of 0, or you could draw the image in code, either with Core Graphics or a CAGradientLayer.
If you set userInteractionEnabled = NO; on the views, they won't interfere with the touch handling.

IOS - Quartzcore custom UIView with shadow

I'm implementing a custom UIView to display inside a UIScrollview. The thing is that I need the view to drop a shadow so I did:
#import <QuartzCore/QuartzCore.h>
#implementation CustomView
-(void)setupView{
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowOpacity = 0.5;
self.layer.shadowRadius = 1;
self.layer.shadowOffset = CGSizeMake(.6f, .6f);
self.layer.cornerRadius = 2;
[...]
}
-(id)initWithFrame:(CGRect)frame{
if((self = [super initWithFrame:frame])){
[self setupView];
}
return self;
}
[...]
The point is that when I build and run this the scrollview is so slow and I just need to remove those lines where I was hacking the "self.layer" and The scrollview goes fast and smooth again.
whats the proper way to add shadows to my custom View?
This has to do with the all the redrawing that the UIView has to do when moving.
If you rasterize the layer it will become way smoother, this should do the trick:
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
self.layer.shouldRasterize = YES;
You could try to add a shadow path:
self.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.bounds];
This might help a bit more.

Resources