So I am currently trying to add a image inside the circularShapeLayer in the below code.
因此,我目前正在尝试在下面的代码中的CircularShapeLayer中添加一个图像。
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?
但不知何故,图像并没有显示出来。我已经尝试更改图像的tintColor,也尝试了其他图像。我做错了什么?我如何才能做到这一点有什么建议或想法吗?
更多回答
Using layers, you don't need to override draw()
... it would be helpful if you show us a pic of how you want it to look.
使用层,您不需要重写DRAW()...如果你给我们看一张你希望它看起来是什么样子的照片,那将是很有帮助的。
Are you trying to get something like this? i.stack.imgur.com/Y7t0h.png ... top one has Height: 60
-- bottom one has Height: 30
你想得到这样的东西吗?i.stack.imgur.com/Y7t0h.png.顶部的高度:60 -底部的高度:30
@DonMag Yes, like that image.
@DonMag是的,喜欢这张照片。
Instead of overriding draw(_:)
, let's manipulate layers.
让我们操作层,而不是覆盖DRAW(_:)。
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:
因此,让我们使用:
private let backgroundLayer = CALayer()
private let backgroundMaskLayer = CAShapeLayer()
private let progressLayer = CALayer()
// Create an UIImageView for the system image
private let systemImageView = UIImageView()
and we will add progressLayer
as a sublayer of backgroundLayer
, then mask the backgroundLayer
.
我们将添加ProgressLayer作为BackekLayer的子层,然后遮罩BackekLayer。
We'll also add systemImageView
as a subview instead of a layer.
我们还将添加system ImageView作为子视图,而不是层。
We'll do the main setup like this:
我们将像这样进行主要设置:
private func setupLayers() {
layer.addSublayer(backgroundLayer)
backgroundLayer.addSublayer(progressLayer)
backgroundLayer.mask = backgroundMaskLayer
backgroundLayer.backgroundColor = baseColor.cgColor
systemImageView.image = UIImage(systemName: "bolt.fill") // Set the system image
systemImageView.tintColor = .white // Set the image color
addSubview(systemImageView)
backgroundColor = .clear
}
Instead of overriding draw(_:)
we'll update everything in layoutSubviews()
:
我们将更新layoutSubview()中的所有内容,而不是覆盖DRAW(_:):
override func layoutSubviews() {
super.layoutSubviews()
// colors may have been changed after init
backgroundLayer.backgroundColor = baseColor.cgColor
progressLayer.backgroundColor = color.cgColor
systemImageView.backgroundColor = circularShapeColor
backgroundLayer.frame = bounds
backgroundMaskLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: bounds.height * 0.75).cgPath
let progressRect = CGRect(origin: .zero, size: CGSize(width: bounds.width * progress, height: bounds.height))
progressLayer.frame = progressRect
// Calculate the position and size of the circular shape
let w: CGFloat = bounds.height
let circularSize = CGSize(width: w, height: w)
let circularOrigin = CGPoint(x: progressRect.maxX - w / 2.0, y: progressRect.midY - (w / 2.0))
// Set the frame and image for the system image view
systemImageView.frame = .init(origin: circularOrigin, size: circularSize)
systemImageView.layer.cornerRadius = w * 0.5
}
That will give us this:
这将给我们带来这样的结果:
and we're looking pretty good.
我们看起来很不错。
Except... if we're close to 0%
or 100%
the position of the circular-view won't look great:
除了..。如果我们接近0%或100%,圆形视图的位置看起来不会很好:
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:
并修改我们的定位计算:
let w: CGFloat = bounds.height
let circularSize = CGSize(width: w, height: w)
// we want 0% to have the circle-image center at leading Plus one-half circle-width
// we want 100% to have the circle-image center at trailing Minus one-half circle-width
let progressRange: CGFloat = (bounds.width - w)
// calculate the circle-image centerX
let circleCenterX: CGFloat = w * 0.5 + progressRange * progress
let progressRect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: circleCenterX, height: bounds.height))
progressLayer.frame = progressRect
let circularOrigin = CGPoint(x: circleCenterX - w / 2.0, y: progressRect.midY - (w / 2.0))
// Set the frame for the system image view
systemImageView.frame = CGRect(origin: circularOrigin, size: circularSize)
and it's looking better:
而且它看起来更好了:
Here's your complete class, with all of those modifications:
下面是您的完整类,其中包含所有这些修改:
@IBDesignable
class PlainHorizontalProgressBar: UIView {
@IBInspectable
// default: very light gray
var baseColor: UIColor = UIColor(white: 0.9, alpha: 1.0) {
didSet { setNeedsLayout() }
}
@IBInspectable
// default: gray
var color: UIColor = .gray {
didSet { setNeedsLayout() }
}
@IBInspectable
// default: orange
var circularShapeColor: UIColor = .orange {
didSet { setNeedsLayout() }
}
@IBInspectable
// default: 0.0
var progress: CGFloat = 0.0 {
didSet { setNeedsLayout() }
}
private let backgroundLayer = CALayer()
private let backgroundMaskLayer = CAShapeLayer()
private let progressLayer = CALayer()
// Create an UIImageView for the system image
private let systemImageView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
setupLayers()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupLayers()
}
private func setupLayers() {
layer.addSublayer(backgroundLayer)
backgroundLayer.addSublayer(progressLayer)
backgroundLayer.mask = backgroundMaskLayer
backgroundLayer.backgroundColor = baseColor.cgColor
systemImageView.image = UIImage(systemName: "bolt.fill") // Set the system image
systemImageView.tintColor = .white // Set the image color
addSubview(systemImageView)
backgroundColor = .clear
}
override func layoutSubviews() {
super.layoutSubviews()
// colors may have been changed after init
backgroundLayer.backgroundColor = baseColor.cgColor
progressLayer.backgroundColor = color.cgColor
systemImageView.backgroundColor = circularShapeColor
backgroundLayer.frame = bounds
backgroundMaskLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: bounds.height * 0.75).cgPath
// Calculate the position and size of the circular shape
let w: CGFloat = bounds.height
let circularSize = CGSize(width: w, height: w)
// we want 0% to have the circle-image center at leading Plus one-half circle-width
// we want 100% to have the circle-image center at trailing Minus one-half circle-width
let progressRange: CGFloat = (bounds.width - w)
// calculate the circle-image centerX
let circleCenterX: CGFloat = w * 0.5 + progressRange * progress
let progressRect = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: circleCenterX, height: bounds.height))
progressLayer.frame = progressRect
let circularOrigin = CGPoint(x: circleCenterX - w / 2.0, y: progressRect.midY - (w / 2.0))
// Set the frame for the system image view
systemImageView.frame = CGRect(origin: circularOrigin, size: circularSize)
// let's use .layer.cornerRadius instead of another layer mask
systemImageView.layer.cornerRadius = systemImageView.frame.width * 0.5
}
}
Edit
编辑
If you want the "circular view" to be larger than the "progress bar" (as in the first image I posted)...
如果你想让“圆形视图”比“进度条”大(就像我发布的第一张图片一样).
In layoutSubviews()
change:
在layoutSubview()中更改:
backgroundLayer.frame = bounds
backgroundMaskLayer.path = UIBezierPath(roundedRect: bounds, cornerRadius: bounds.height * 0.75).cgPath
to:
致:
backgroundLayer.frame = bounds
let r: CGRect = bounds.insetBy(dx: 0.0, dy: bounds.height * 0.25)
backgroundMaskLayer.path = UIBezierPath(roundedRect: r, cornerRadius: r.height * 0.5).cgPath
That will make the "bar height" one-half of the view height, centered vertically.
这将使“条形高度”为视图高度的一半,垂直居中。
A couple of things:
有几件事:
Don't override draw(_:)
if you're constructing a view out of layers.
如果你在层外构造视图,不要覆盖draw(_:)。
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.
不能将视图的内容层安装为另一个层的子层。使用基本层。您可以将图像安装为“常规”CALayer的内容,然后将该层安装为Shape Layer的子层。这会奏效的。
更多回答
Wow, thanks you so much! Very cell explained. I really liked this: i.stack.imgur.com/gdMl7.png. Is it possible to edit the code so the bolt circle is bigger than the progress view like the image?
哇,太感谢你了!非常细胞解释说。我真的很喜欢这个:i.stack.imgur.com/gdMl7.png。是否可以编辑代码,使螺栓圆大于进度视图(如图像)?
@Saland - see the Edit at the bottom of my answer.
@Saland-请参阅我答案底部的编辑。
Again, thank you so much! I really appreciate that you actually take your time to explain the code also, and the steps. +1 to you.
再次,非常感谢你们!我真的很感激你花时间来解释代码和步骤。加1给你。
我是一名优秀的程序员,十分优秀!