Cannot layout CALayer frame correctly
Here is the simple fix for your issue provide the bounds
instead of the frame
as the frame for the CALayer
, here's how:
darkShadow.frame = bounds
To understand how this works you need to know the basics of UIKit
layout and the difference between frame
and bounds
(You might wanna google this). In simple words, frame is the coordinates of the view with respect to super and bounds is with respect to itself.
Note: (I'm adding this here just as a guideline) It's a common beginner mistake. There are plenty of online resources that explain the difference between frame and bounds. Although you might find it a bit overwhelming in the beginning you will get to know the difference eventually. Here are the ones I found useful and could be helpful for you as well :- Youtube Video of Saun Alen and Article from hacking with swift.
CALayer bounds don't change after setting them to a certain value
The way to change the apparent drawing size of a layer is not to change its bounds
but to change its transform
. To make the layer look larger, including its drawing, apply a scale transform.
CALayers didn't get resized on its UIView's bounds change. Why?
I used the same approach that Solin used, but there's a typo in that code. The method should be:
- (void)layoutSubviews {
[super layoutSubviews];
// resize your layers based on the view's new bounds
mylayer.frame = self.bounds;
}
For my purposes, I always wanted the sublayer to be the full size of the parent view. Put that method in your view class.
CALayer content goes out of bounds - iOS
You should put the layer you are scaling inside of another layer and mask that one instead (the superlayer). The same thing works with views.
I.e. You have two views / layers: clippingView
and scalingView
where scalingView is the subview of clippingView and clippingView is the view that actually clips to it's bounds.
[clippingView addSubview:scalingView];
clippingView.clipsToBounds = YES;
or using layers
[clippingLayer addSublayer:scalingLayer];
clippingLayer.masksToBounds = YES;
why isnt my CaLayer showing up on top of the UIView?
There are a few things going wrong:
The
frame
/bounds
of the CALayer need to be set accurately. Keep in mind that this could change from its initial placement. For example, in my first test, the layer believed that it was a0x0
rectangle.The path of the curve has to be correctly set (related to #1)
Your current time of
.infinity
would've made the animation take an infinite amount of time to complete. I think what you want instead is an animation that takes a certain amount of time and repeats infinitely.
There may be been another change I'm now forgetting about, but this should get you started:
class LoadingView: UIView {
let circleLayer = CAShapeLayer()
private var circlePath : UIBezierPath = .init()
let size : CGFloat = 50
init() {
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func startLoading() {
if let topView = UIApplication.topViewController()?.view {
topView.addSubview(self)
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.centerYAnchor.constraint(equalTo: topView.centerYAnchor),
self.centerXAnchor.constraint(equalTo: topView.centerXAnchor),
self.widthAnchor.constraint(equalToConstant: CGFloat(size)),
self.heightAnchor.constraint(equalToConstant: CGFloat(size)),
])
self.layer.cornerRadius = frame.height/3
backgroundColor = .secondarySystemBackground
self.layer.addSublayer(circleLayer)
calculateCirclePath()
animateCircle(duration: 1, repeats: true)
}
}
func calculateCirclePath() {
self.circlePath = UIBezierPath(arcCenter: CGPoint(x: size / 2, y: size / 2), radius: size / 2, startAngle: 0.0, endAngle: CGFloat(Double.pi*2), clockwise: true)
}
override func layoutSubviews() {
circleLayer.frame = self.layer.bounds
}
private func animateCircle(duration: TimeInterval, repeats: Bool) {
// Setup the CAShapeLayer with the path, colors, and line width
circleLayer.path = circlePath.cgPath
circleLayer.fillColor = UIColor.clear.cgColor
circleLayer.strokeColor = UIColor.blue.cgColor
circleLayer.lineWidth = 1.0
// Don't draw the circle initially
circleLayer.strokeEnd = 0.0
// Add the circleLayer to the view's layer's sublayers
self.layer.addSublayer(circleLayer)
// We want to animate the strokeEnd property of the circleLayer
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = duration
animation.repeatCount = repeats ? .infinity : 1
animation.fromValue = 0
animation.toValue = 1
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
circleLayer.strokeEnd = 0
circleLayer.add(animation, forKey: "animateCircle")
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
rotationAnimation.repeatCount = repeats ? .infinity : 1
rotationAnimation.fromValue = 0.0
rotationAnimation.toValue = Double.pi*3
rotationAnimation.duration = duration
rotationAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
circleLayer.add(rotationAnimation, forKey: nil)
let fadeAnimation = CABasicAnimation(keyPath: "opacity")
fadeAnimation.repeatCount = repeats ? .infinity : 1
fadeAnimation.fromValue = 1
fadeAnimation.toValue = 0
fadeAnimation.duration = duration
fadeAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
circleLayer.add(fadeAnimation, forKey: nil)
}
func stopLoading() {
self.removeFromSuperview()
}
}
iOS - CALayer protruding bounds on orientation change
You are inserting a new layer each time when layoutSubviews
method call. That means it shows the line of the first layer.
Just update the frame from layoutSubviews
and call setup method from awakeFromNib
.
class CustomView: UIView {
private let gradient = CAGradientLayer()
override init (frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
gradient.frame = bounds
}
override func awakeFromNib() {
super.awakeFromNib()
setupView()
}
private func setupView() {
gradient.colors = [UIColor.blue.cgColor, UIColor.yellow.cgColor]
layer.insertSublayer(gradient, at: 0)
}
}
Related Topics
Swift 4 "Extra Argument in Call" Rxswift
I Opened My App in Xcode 10 and Now I Have Errors in 9.4.1: Sdkapplicationdelegate (Facebookcore)
How to Set Default Clouse Param in View Method
Pass and Print All Cases in an Enum in Swift
Avspeechsynthesizer Isspeaking Not Working in Swift
How to Create Generic Closures in Swift
How to Reconstruct Grayscale Image from Intensity Values
How to Disable a Button of a Nstoolbar of Macos X in Swift
Implementing Undo and Redo in a UItextview with Attributedtext
Swift Video to Document Directory
What Do Detached and Assigncurrentcontext Meaning
Decoding Different Type with and Without Array
Using Auto Layout to Orientate Stack Views Vertically in Portrait and Horizontally in Landscape
How to Detect Touches on UIimageview of UItableviewcell in Swift
Generating Random Doable Math Problems Swift
How to Dynamically Hide Navigation Back Button in Swiftui
Occasional Blank Frames After Exporting Asset - Avexportsession
How to Build a Recursive Function in Swift to Return a String