让我们看看一种不需要动画gif、or个图案图像的方法.
我们可以使用UIBezierPath
和CAShapeLayer
来在自定义视图子集中创建模式:
我们可以使用"字里行间" colored颜色 来设置视图的背景色:
现在我们可以使用CABasicAnimation(keyPath: "position.x")
将该层向左"滑动".我们将线路径创建得比视图更宽,这样我们就不会在右侧看到间隙(忽略此动画中的"打嗝",它之所以存在,只是因为它是部分捕获):
您的原始图像似乎也有轻微的渐变,因此我们可以为该外观叠加半透明渐变:
现在,我们可以通过将动画形状视图嵌入到"容器"视图中来创建自定义"进度"视图,并调整容器的宽度以反映进度:
这是一些示例代码.
AnimatedPatternView:所有尺寸、 colored颜色 、速度等属性都位于顶部,以便根据您的喜好轻松调整它们:
class AnimatedPatternView: UIView {
private let angledLinesShapeLayer = CAShapeLayer()
private let overlayGradLayer = CAGradientLayer()
// let's put all of our appearance variables here
// in one place, to make it easier to adjust them
// this will be the amount of time (in seconds) that the
// animation takes to slide "one tile" to the left
private let animSpeed: Double = 0.5
// "tile" width
private let tileWidth: CGFloat = 40.0
// we want an angled line, not the full width
private let angledLineBottomOffset: CGFloat = 32.0
// thickness of the lines
private let angledLineThickness: CGFloat = 14.0
// the angled-line and "space between" colors
private let angledLineColor: UIColor = UIColor(red: 0.557, green: 0.420, blue: 0.863, alpha: 1.0)
private let spaceBetweenColor: UIColor = UIColor(red: 0.624, green: 0.494, blue: 0.886, alpha: 1.0)
// soft overlay gradient
private let overlayGradientColors: [CGColor] = [
UIColor.black.withAlphaComponent(0.2).cgColor,
UIColor.white.withAlphaComponent(0.1).cgColor,
UIColor.black.withAlphaComponent(0.05).cgColor,
]
// gradient angle
private let gradStartPoint: CGPoint = .init(x: 0.250, y: 0.0)
private let gradEndPoint: CGPoint = .init(x: 0.750, y: 1.0)
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
self.backgroundColor = spaceBetweenColor
angledLinesShapeLayer.strokeColor = angledLineColor.cgColor
angledLinesShapeLayer.lineWidth = angledLineThickness
angledLinesShapeLayer.lineCap = .square
overlayGradLayer.colors = overlayGradientColors
overlayGradLayer.startPoint = gradStartPoint
overlayGradLayer.endPoint = gradEndPoint
self.layer.addSublayer(angledLinesShapeLayer)
self.layer.addSublayer(overlayGradLayer)
self.layer.masksToBounds = true
self.clipsToBounds = true
}
override func layoutSubviews() {
super.layoutSubviews()
let bez = UIBezierPath()
var x: CGFloat = 0.0
while x < bounds.width + tileWidth {
bez.move(to: .init(x: x, y: bounds.minY))
bez.addLine(to: .init(x: x + angledLineBottomOffset, y: bounds.maxY))
x += tileWidth
}
angledLinesShapeLayer.path = bez.cgPath
// gradient layer needs to match the view bounds
overlayGradLayer.frame = bounds
angledLinesShapeLayer.removeAllAnimations()
let animation = CABasicAnimation(keyPath: "position.x")
animation.fromValue = 0.0
animation.toValue = -tileWidth
animation.duration = animSpeed
animation.repeatCount = .infinity
angledLinesShapeLayer.add(animation, forKey: "stripeAnim")
}
}
EnergyProgressView:使用AnimatedPatternView
和容器视图来模拟进度条的查看子类别:
class EnergyProgressView: UIView {
public var progress: Float {
set { _progress = newValue }
get { return _progress }
}
private var _progress: Float = 1.0 {
didSet {
wConstraint.isActive = false
wConstraint = containerView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: CGFloat(_progress))
wConstraint.isActive = true
}
}
private let animPatternView = AnimatedPatternView()
private let containerView = UIView()
// this will control the width of the container
// using progress as a percentage of the width
private var wConstraint: NSLayoutConstraint!
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
animPatternView.translatesAutoresizingMaskIntoConstraints = false
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(animPatternView)
self.addSubview(containerView)
containerView.clipsToBounds = true
// we'll be modifying the width of containerView to reflect the progress
wConstraint = containerView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1.0)
NSLayoutConstraint.activate([
containerView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0.0),
containerView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0.0),
containerView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0.0),
wConstraint,
// even though animPatternView is a subview of containerView
// we constrain animPatternView to self so it doesn't resize
// when the progress changes
animPatternView.topAnchor.constraint(equalTo: self.topAnchor, constant: 0.0),
animPatternView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0.0),
animPatternView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0.0),
animPatternView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0.0),
])
self.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
self.layer.masksToBounds = true
self.layer.cornerRadius = 8.0
}
func setProgress(_ p: Float, animated: Bool = false) {
_progress = p
if animated {
UIView.animate(withDuration: 0.3, animations: {
self.layoutIfNeeded()
})
}
}
}
ViewController:示例视图控制器,具有自定义进度条和用于更新进度的滑动块:
class ViewController: UIViewController {
let myProgressView = EnergyProgressView()
// let's add a label and a slider so we can dynamically set the progress
let pctLabel = UILabel()
let slider = UISlider()
override func viewDidLoad() {
super.viewDidLoad()
myProgressView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myProgressView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
myProgressView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
myProgressView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
myProgressView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
myProgressView.heightAnchor.constraint(equalToConstant: 32.0),
])
pctLabel.textAlignment = .center
pctLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(pctLabel)
slider.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(slider)
NSLayoutConstraint.activate([
pctLabel.topAnchor.constraint(equalTo: myProgressView.bottomAnchor, constant: 40.0),
pctLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
pctLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
slider.topAnchor.constraint(equalTo: pctLabel.bottomAnchor, constant: 40.0),
slider.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
slider.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
])
slider.addTarget(self, action: #selector(handleSlider(_:)), for: .valueChanged)
slider.value = 0.5
myProgressView.progress = 0.5
updatePctLabel()
}
@objc func handleSlider(_ sender: UISlider) {
myProgressView.progress = sender.value
updatePctLabel()
}
func updatePctLabel() {
let v = myProgressView.progress
pctLabel.text = String(format: "%0.2f %%", v * 100.0)
}
}
请注意:这是100它应用作学习工具,不应被视为"生产就绪"."