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

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

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]

Aligning CAShapeLayer center of my UIView

I've been trying to solve this for days, and none of the suggestions solved my problem. I have a UIView inside a UITableView. I've been trying to draw my CAShapeLayer inside my UIView, but I just can't get it right.
I need my yellow CAShapeLayer to be exactly center within the gray UIView.
func DrawActivityRect(cell: CalorieDashboardTVCell){
let rectangle = UIBezierPath(rect: cell.calorieBurnUIView.bounds)
let trackLayer = CAShapeLayer()
trackLayer.frame = cell.calorieBurnUIView.bounds
trackLayer.path = rectangle.cgPath
trackLayer.position = cell.calorieBurnUIView.center
trackLayer.strokeColor = UIColor.yellow.cgColor
trackLayer.lineWidth = 10
trackLayer.fillColor = UIColor.clear.cgColor
trackLayer.lineCap = kCALineCapRound
cell.calorieBurnUIView.layer.addSublayer(trackLayer)
}
Result:
I suspect the cell size change after you have set the layer. The easiest place to fix this is in the cell's layoutSubviews(). Since your path is using absolute coordinates you need to update both the layer frame and the path.
override func layoutSubviews() {
super.layoutSubviews()
// You need to keep trackLayer around after you created it
let rectangle = UIBezierPath(rect: cell.calorieBurnUIView.bounds)
self.trackLayer.frame = cell.calorieBurnUIView.bounds
self.trackLayer.path = rectangle.cgPath
self.trackLayer.position = cell.calorieBurnUIView.center
}

Partially fill a CAShapeLayer() with a colour

I am working with swift on xcode 9.2 to create bounding boxes on iOS 11. What I have here is a bounding box that can either be completely clear (so that the object being bounded can be seen) or completely filled with color (green here, so that the object the box bounds is completely covered in green). What I would like to do, is go partway with the color fill so that the bounding box is translucent. For instance, a bounding box with a green tint where the object being bounded is still visible. Any help on how to accomplish this will be very helpful
class BoundingBox {
let shapeLayer: CAShapeLayer
let textLayer: CATextLayer
init() {
shapeLayer = CAShapeLayer()
//shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.fillColor = UIColor.green.cgColor
shapeLayer.lineWidth = 4
shapeLayer.isHidden = true
textLayer = CATextLayer()
textLayer.foregroundColor = UIColor.black.cgColor
textLayer.isHidden = true
textLayer.contentsScale = UIScreen.main.scale
textLayer.fontSize = 14
textLayer.font = UIFont(name: "Avenir", size: textLayer.fontSize)
textLayer.alignmentMode = kCAAlignmentCenter
}
The .fillColor property on line 6 and 7 is what I am currently manipulating to change the bounding box properties

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 can UIVisualEffectView be used inside of a circle?

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:

Resources