How can UIVisualEffectView be used inside of a circle? - ios

I'm making a custom control circle. Part of the circle may be transparent. It would make more visual sense and look better if it was translucent instead of transparent.
Because views are rectangular, and I only want the circle to be translucent, not the rest of the rectangle, this is a problem.
The UIVisualEffectView is behind the custom control.
(Without anything rendered inside of the circle for debugging purposes)
As you can see, the view is blurring things outside of the circle.
I don't know how to blur inside only the view, and the prerelease documentation is practically empty. My only thought is to create many 1x1 views to cover the circle, but this seems like it would not really work, and even if it did it would be a slow and ugly solution. How can I blur the content inside the view, without blurring anything outside it?

Set the circle view's layer mask to a filled circle:
CircleControlView *circleView = yourCircleView();
CAShapeLayer *mask = [CAShapeLayer layer];
mask.path = [UIBezierPath bezierPathWithOvalInRect:circleView.bounds].CGPath;
circleView.layer.mask = mask;
UPDATE
Swift playground example:
import UIKit
import XCPlayground
import QuartzCore
UIGraphicsBeginImageContext(CGSize(width: 100, height: 100))
let viewPath = UIBezierPath(ovalInRect:CGRect(x:1,y:1,width:98,height:98))
viewPath.lineWidth = 2
viewPath.stroke()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let view = UIView(frame:CGRect(x:0,y:0,width:100,height:100))
XCPShowView("view", view)
let label = UILabel(frame:view.bounds)
label.text = "This is the text in the background behind the circle."
label.numberOfLines = 0
view.addSubview(label)
let effectView = UIVisualEffectView(effect:UIBlurEffect(style:.ExtraLight))
effectView.frame = view.bounds
view.addSubview(effectView)
let circleView = UIImageView(image:image)
effectView.addSubview(circleView)
let maskPath = UIBezierPath(ovalInRect:circleView.bounds)
let mask = CAShapeLayer()
mask.path = maskPath.CGPath
effectView.layer.mask = mask
Result:

Related

In iOS, how to have view with rounded corners and drop shadow mask its subview?

I'm trying to create something like this image except for those purple corners. Notice how the blue (shown as purple) contentView is not being clipped or masked by its parent containerView.
The requirements are to have a view which:
Has rounded corners.
Has a shadow.
It's subviews don't leak outside of it or its corners.
Here is the code I'm playing around with and I'm not sure exactly how to get this to work.
let containerView = UIView(frame: CGRect(x: 300, y: 100, width: 200, height: 200))
containerView.backgroundColor = .green
containerView.layer.cornerRadius = 40
containerView.layer.shadowRadius = 50
containerView.layer.shadowOffset = .zero
containerView.layer.shadowColor = UIColor.red.cgColor
containerView.layer.shadowOpacity = 1
view.addSubview(containerView)
let backgroundLayer = CALayer()
backgroundLayer.frame = containerView.layer.bounds
backgroundLayer.backgroundColor = UIColor.black.cgColor
backgroundLayer.opacity = 0.5
backgroundLayer.cornerRadius = containerView.layer.cornerRadius
backgroundLayer.masksToBounds = true
containerView.layer.addSublayer(backgroundLayer)
let contentView = UIView(frame: containerView.bounds)
contentView.backgroundColor = .blue
contentView.alpha = 0.3
// Omitting this line will produce a green square with rounded corners
// and a red shadow.
containerView.addSubview(contentView)
Based on this example code, one of my more specific questions is why doesn't the backgroundLayer which sets masksToBounds = true, mask the view's subview?
Based on this example code, one of my more specific questions is why doesn't the backgroundLayer which sets masksToBounds = true, mask the view's subview?
Because the subview is the subview of the view, not of the backgroundLayer.
Views are layers. Clipping is masking. A layer masks its own drawing and that of its sublayers. The subview's layer is not a sublayer of the backgroundLayer. It is its sibling, not its child. Your view/layer hierarchy looks like this:
containerView
| \
[layer] backgroundLayer
| (clips itself and its sublayers,
contentView but it has no sublayers)
|
[layer]

ios swift create rounded profile image like twitter, instagram,

I would like to create a very simple image editor, same as twitter (for the profile image)
I know how to pinch or move an image.
But i don’t know how to create the "circle layer" and just keep this part of the image, like this :
Make sure to import QuarzCore.
func maskRoundedImage(image: UIImage, radius: CGFloat) -> UIImage {
let imgView: UIImageView = UIImageView(image: image)
let layer = imgView.layer
layer.masksToBounds = true
layer.cornerRadius = radius
UIGraphicsBeginImageContext(imgView.bounds.size)
layer.render(in: UIGraphicsGetCurrentContext()!)
let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return roundedImage!
}
https://github.com/andreaantonioni/AAPhotoCircleCrop
Each view has an underlying layer onto which you apply a corner radius. Then you must apply clipToBounds on that layer in order to apply that mask to the overlying UIView. The corner radius must be half the width of the view in order to get a circle effect. Otherwise the corners of the view will be rounded.
For example:
let square = UIView()
square.center = view.center
square.bounds.size = CGSize(width: 100, height: 100)
square.backgroundColor = .red
view.addSubview(square)
square.layer.cornerRadius = 50
square.clipsToBounds = true
Above mentioned ways are good, But you can not achieved exact output as in above image E.g You can not get alpha effect.
I suggest simple way to do it.
1) Add new image to project with transparent background with Opocity and circle.
2) Add new imageView above to mainview as below.
let image = UIImageView(frame: view.bounds)
mage.image = UIImage.init(named:"imageName")
view.addSubview(image)
Then output should be as per your requirement.

How to hide the CALayer's borders without causing rendering issue in iOS

I am having three UIView's one after the other horizontally, with each UiView.Layer.BorderColor set to Black and UiView.Layer.BorderWidth set to 0.25f like shown below. So this gives a border look for each of the view.
Now i have a requirement to only display the horizontal borders of the UiViews. Hence i create a mask layer and set path and frame to that mask layer to apply clip. Please refer the code snippet below to give you an idea of what am doing.
foreach (UIView UiView in myViews)
{
UiView.Layer.BorderColor = Color.Black.ToCGColor();
UiView.Layer.BorderWidth = 0.25f;
UIBezierPath maskPath = null;
CAShapeLayer maskLayer = new CAShapeLayer();
maskLayer.Frame = UiView.Bounds;
// Applying Clip to my layer
maskPath = UIBezierPath.FromRect(new CGRect(UiView.Bounds.Left + 0.5, UiView.Bounds.Top, UiView.Bounds.Width - 1, UiView.Bounds.Height));
maskLayer.Path = maskPath.CGPath;
view.Layer.Mask = maskLayer;
}
Yes i am aware that whatever the bounds i set for the maskPath is the Visible region, so given that each UiView is 60 in width , my visible region according to what i have written in code is , from 0.5 pixel upto 59.5 pixel of each UIView . Thus eliminating the borders present in the pixels from 0 to 0.5 and from 59.5 to 60. Hope this part is clear for you. Please refer the below image for a visual representation of my mask area. The Yellow borders denotes the mask bounds.
Now all is fine , but this 0.5 pixels border which is hidden causes a white space in my top and bottom borders of the UiView's put together continuously. Please refer the below image with the circled spots highlighting the void spaces in the top and bottom.
This can be easily reproduced in code too. Please suggest me on how to overcome this problem. What am i doing wrong. I know this is a very small blank space of half a pixel which is barely visible to the naked eye most of the times, but it will not be visually pleasing for my grid view application.
PS : Am developing in Xamarin.iOS , however native iOS solutions will also be helpful.
Thanks in advance
Try this for Swift.
class ViewController: UIViewController {
#IBOutlet weak var view1: UIView!
#IBOutlet weak var view2: UIView!
#IBOutlet weak var view3: UIView!
override func viewDidLoad() {
super.viewDidLoad()
var borderwidth = CGFloat()
borderwidth = 5
let topBorder = CALayer()
topBorder.backgroundColor = UIColor.black.cgColor
topBorder.frame = CGRect(x: self.view1.frame.origin.x, y: self.view1.frame.origin.y, width: self.view3.frame.origin.x + self.view3.frame.size.width - self.view1.frame.origin.x, height: borderwidth)
self.view.layer.addSublayer(topBorder)
let bottomBorder = CALayer()
bottomBorder.backgroundColor = UIColor.black.cgColor
bottomBorder.frame = CGRect(x: self.view1.frame.origin.x, y: self.view1.frame.origin.y + self.view1.frame.size.height - borderwidth, width: self.view3.frame.origin.x + self.view3.frame.size.width - self.view1.frame.origin.x, height: borderwidth)
self.view.layer.addSublayer(bottomBorder)
}
}
See this screenshot : 1
Masking is the problem. Try like this.
view.Layer.MasksToBounds = false;
view.Layer.AllowsEdgeAntialiasing = true;
layer.BorderColor = color.ToCGColor();
layer.BorderWidth = 0.5f;
if (layer.SuperLayer == null && layer.SuperLayer != view.Layer)
view.Layer.AddSublayer(layer);
view.Layer.BorderColor = Color.Transparent.ToCGColor();
layer.Frame = new CGRect(view.Bounds.Left, view.Bounds.Bottom - 0.5f, view.Bounds.Right, view.Bounds.Bottom);

How to remove CALayer mask margin? + how to position UILabel properly?

I am trying to create the effect of the text color inversing while being intercepted by the background layer.
As you can see in the screenshot below, i have a blue CAShapeLayer moving from left to right and a mask layer directly attached to it, moving accordingly. Strangely there is a gap between the two layers.
My Code for the layers:
//Add Shape Layer
let path = UIBezierPath(rect: self.contentView.frame)
initPostion = -contentView.frame.size.width+initialVisibility
colorStripeLayer.path = path.CGPath
colorStripeLayer.fillColor = UIColor(red:0.71, green:0.86, blue:0.84, alpha:1.0).CGColor
colorStripeLayer.frame = self.contentView.frame
colorStripeLayer.transform = CATransform3DMakeTranslation(initPostion, 0, 0)
self.contentView.layer.insertSublayer(colorStripeLayer, atIndex: 0)
//white UILabel Masking
maskLayer.path = path.CGPath
maskLayer.fillColor = UIColor.lightGrayColor().CGColor
maskLayer.frame = self.contentView.bounds
Additionaly I wanted to place the white text exactly underneath the black one, but even after setting all the attributes, it still positions quite randomly.
Code for the labels:
textLabel?.layer.mask = maskLayer
textLabel?.backgroundColor = UIColor.clearColor()
textLabel?.textColor = UIColor.blackColor()
let whiteLabel = UILabel(frame: textLabel!.frame)
whiteLabel.text = textLabel!.text
whiteLabel.font = textLabel!.font
whiteLabel.textAlignment = textLabel!.textAlignment
whiteLabel.textColor = UIColor.whiteColor()
contentView.insertSubview(whiteLabel, belowSubview: textLabel!)
Animation is just a simple x-axis translation, done in pop.
Can anyone help here?
2 Problems here

Add subview to self.view but let certain subviews content not be covered by the added subview

I want to add a subview to self.view that covers the whole area (like a dimming view). But underneath it there are some views that I don't want to be affected by the added subview (I don't want those views to have a dimmed effect). Is this possible to do?
I know there is an "easy" solution to it; to move those subviews that I don't want to be affected to the front (over the added subview) but that has unwanted effects for me so I can't use that solution.
I'm targeting iOS 8 and above.
You'll want to look into creating a CALayer mask to subtract the subviews frames below from your view's layer above.
var path = UIBezierPath(rect: topView.frame)
var subviewPath1 = UIBezierPath(rect: subview1.frame)
var subviewPath2 = UIBezierPath(rect: subview2.frame)
path.usesEvenOddFillRule = true
path.appendPath(subviewPath1)
path.appendPath(subviewPath2)
var maskLayer = CAShapeLayer()
maskLayer.path = path.CGPath
maskLayer.fillRule = kCAFillRuleEvenOdd
maskLayer.fillColor = UIColor.whiteColor().CGColor
topView.layer.mask = maskLayer

Resources