So I am currently trying to add a image inside the circularShapeLayer in the below code.
Here is the full code:
import Foundation
import UIKit
@IBDesignable
class PlainHorizontalProgressBar: UIView {
@IBInspectable var color: UIColor = .gray {
didSet { setNeedsDisplay() }
}
var progress: CGFloat = 0 {
didSet { setNeedsDisplay() }
}
var circularShapeColor: UIColor = .blue {
didSet { setNeedsDisplay() }
}
private let progressLayer = CALayer()
private let backgroundMask = CAShapeLayer()
private let circularShapeLayer = CAShapeLayer()
// Create an UIImageView for the system image
private let systemImageView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
circularShapeColor = UIColor.systemOrange
setupLayers()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
circularShapeColor = UIColor.systemOrange
setupLayers()
}
private func setupLayers() {
layer.addSublayer(progressLayer)
layer.addSublayer(circularShapeLayer)
// Add the system image view to the circularShapeLayer
circularShapeLayer.addSublayer(systemImageView.layer)
}
override func draw(_ rect: CGRect) {
backgroundMask.path = UIBezierPath(roundedRect: rect, cornerRadius: rect.height * 0.75).cgPath
layer.mask = backgroundMask
let progressRect = CGRect(origin: .zero, size: CGSize(width: rect.width * progress, height: rect.height))
progressLayer.frame = progressRect
progressLayer.backgroundColor = color.cgColor
// Calculate the position and size of the circular shape
let circularSize = CGSize(width: rect.height, height: rect.height)
let circularOrigin = CGPoint(x: progressRect.maxX - circularSize.width / 2.0, y: (rect.height - circularSize.height) / 2.0)
let circularPath = UIBezierPath(ovalIn: CGRect(origin: circularOrigin, size: circularSize))
circularShapeLayer.path = circularPath.cgPath
// Set the frame and image for the system image view
systemImageView.frame = circularShapeLayer.bounds
systemImageView.image = UIImage(systemName: "bolt.fill") // Set the system image
systemImageView.tintColor = .white // Set the image color
}
}
But somehow the image is not showing. I have tried changing the tintColor of the image, and tried with other images also. What am I doing wrong? Any suggestions or ideas on how can I accomplish this?
2
Answers
A couple of things:
Don’t override
draw(_:)
if you’re constructing a view out of layers.You can’t install a view’s content layer as a sublayer of another layer. Use primitive layers. You can install an image as the contents of a "regular" CALayer, and then install that layer as a sublayer of your shape layer. That would work.
Instead of overriding
draw(_:)
, let’s manipulate layers.First note… we probably don’t want to mask the "base layer" (the entire view) because we might want to – at some point – "fancy it up" a little bit.
And, for example, we would want:
So let’s use:
and we will add
progressLayer
as a sublayer ofbackgroundLayer
, then mask thebackgroundLayer
.We’ll also add
systemImageView
as a subview instead of a layer.We’ll do the main setup like this:
Instead of overriding
draw(_:)
we’ll update everything inlayoutSubviews()
:That will give us this:
and we’re looking pretty good.
Except… if we’re close to
0%
or100%
the position of the circular-view won’t look great:We’ll "fix" that…
Let’s calculate the "progress range" – inset on each end by one-half of the circular-view width:
and modify our positioning calculations:
and it’s looking better:
Here’s your complete class, with all of those modifications: