mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-24 21:33:09 +09:00
187 lines
6.5 KiB
Swift
187 lines
6.5 KiB
Swift
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
// ADAPTED FROM:
|
|
//
|
|
// GradientProgressBar.swift
|
|
// GradientProgressBar
|
|
//
|
|
// Created by Felix Mau on 01.03.17.
|
|
// Copyright © 2017 Felix Mau. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import UIKit
|
|
|
|
open class GradientProgressBar: UIProgressView {
|
|
|
|
private struct DefaultValues {
|
|
static let backgroundColor = UIColor.clear
|
|
static let animationDuration = 0.2 // CALayer default animation duration
|
|
}
|
|
|
|
var gradientColors: [CGColor] = []
|
|
// Alpha mask for visible part of gradient.
|
|
private var alphaMaskLayer: CALayer = CALayer()
|
|
|
|
// Gradient layer.
|
|
open var gradientLayer: CAGradientLayer = CAGradientLayer()
|
|
|
|
// Duration for "setProgress(animated: true)"
|
|
open var animationDuration = DefaultValues.animationDuration
|
|
|
|
// Workaround to handle orientation change, as "layoutSubviews()" gets triggered each time
|
|
// the progress value is changed.
|
|
override open var bounds: CGRect {
|
|
didSet {
|
|
updateAlphaMaskLayerWidth()
|
|
}
|
|
}
|
|
|
|
// Update layer mask on direct changes to progress value.
|
|
override open var progress: Float {
|
|
didSet {
|
|
updateAlphaMaskLayerWidth()
|
|
}
|
|
}
|
|
|
|
func setGradientColors(startColor: UIColor, endColor: UIColor) {
|
|
gradientColors = [startColor, endColor, startColor, endColor, startColor, endColor, startColor].map { $0.cgColor }
|
|
gradientLayer.colors = gradientColors
|
|
}
|
|
|
|
func commonInit() {
|
|
setupProgressViewColors()
|
|
setupAlphaMaskLayer()
|
|
setupGradientLayer()
|
|
|
|
layer.insertSublayer(gradientLayer, at: 0)
|
|
updateAlphaMaskLayerWidth()
|
|
}
|
|
|
|
override public init(frame: CGRect) {
|
|
gradientColors = []
|
|
super.init(frame: frame)
|
|
commonInit()
|
|
}
|
|
|
|
required public init?(coder aDecoder: NSCoder) {
|
|
super.init(coder: aDecoder)
|
|
commonInit()
|
|
}
|
|
|
|
// MARK: - Setup UIProgressView
|
|
|
|
private func setupProgressViewColors() {
|
|
backgroundColor = DefaultValues.backgroundColor
|
|
trackTintColor = .clear
|
|
progressTintColor = .clear
|
|
}
|
|
|
|
// MARK: - Setup layers
|
|
|
|
private func setupAlphaMaskLayer() {
|
|
alphaMaskLayer.frame = bounds
|
|
alphaMaskLayer.cornerRadius = 3
|
|
|
|
alphaMaskLayer.anchorPoint = CGPoint(x: 0, y: 0)
|
|
alphaMaskLayer.position = CGPoint(x: 0, y: 0)
|
|
|
|
alphaMaskLayer.backgroundColor = UIColor.white.cgColor
|
|
}
|
|
|
|
private func setupGradientLayer() {
|
|
// Apply "alphaMaskLayer" as a mask to the gradient layer in order to show only parts of the current "progress"
|
|
gradientLayer.mask = alphaMaskLayer
|
|
|
|
gradientLayer.frame = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.size.width * 2, height: bounds.size.height)
|
|
gradientLayer.colors = gradientColors
|
|
gradientLayer.locations = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.0]
|
|
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
|
|
gradientLayer.endPoint = CGPoint(x: 1, y: 0)
|
|
gradientLayer.drawsAsynchronously = true
|
|
}
|
|
|
|
func hideProgressBar() {
|
|
guard progress == 1 else {
|
|
return
|
|
}
|
|
|
|
CATransaction.begin()
|
|
let moveAnimation = CABasicAnimation(keyPath: "position")
|
|
moveAnimation.duration = DefaultValues.animationDuration
|
|
moveAnimation.fromValue = gradientLayer.position
|
|
moveAnimation.toValue = CGPoint(x: gradientLayer.frame.width, y: gradientLayer.position.y)
|
|
moveAnimation.fillMode = kCAFillModeForwards
|
|
moveAnimation.isRemovedOnCompletion = false
|
|
|
|
CATransaction.setCompletionBlock {
|
|
self.resetProgressBar()
|
|
}
|
|
|
|
gradientLayer.add(moveAnimation, forKey: "position")
|
|
|
|
CATransaction.commit()
|
|
}
|
|
|
|
func resetProgressBar() {
|
|
// Call on super instead so no animation layers are created
|
|
super.setProgress(0, animated: false)
|
|
isHidden = true // The URLBar will unhide the view before starting the next animation.
|
|
}
|
|
|
|
override open func layoutSubviews() {
|
|
super.layoutSubviews()
|
|
self.gradientLayer.frame = CGRect(x: bounds.origin.x - 4, y: bounds.origin.y, width: bounds.size.width * 2, height: bounds.size.height)
|
|
}
|
|
|
|
func animateGradient() {
|
|
let gradientChangeAnimation = CABasicAnimation(keyPath: "locations")
|
|
gradientChangeAnimation.duration = DefaultValues.animationDuration * 4
|
|
gradientChangeAnimation.toValue = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.0]
|
|
gradientChangeAnimation.fromValue = [0.0, 0.0, 0.0, 0.2, 0.4, 0.6, 0.8]
|
|
gradientChangeAnimation.fillMode = kCAFillModeForwards
|
|
gradientChangeAnimation.isRemovedOnCompletion = false
|
|
gradientChangeAnimation.repeatCount = .infinity
|
|
gradientLayer.add(gradientChangeAnimation, forKey: "colorChange")
|
|
}
|
|
|
|
// MARK: - Update gradient
|
|
|
|
open func updateAlphaMaskLayerWidth(animated: Bool = false) {
|
|
CATransaction.begin()
|
|
// Workaround for non animated progress change
|
|
// Source: https://stackoverflow.com/a/16381287/3532505
|
|
CATransaction.setAnimationDuration(animated ? DefaultValues.animationDuration : 0.0)
|
|
alphaMaskLayer.frame = bounds.updateWidth(byPercentage: CGFloat(progress))
|
|
if progress == 1 {
|
|
// Delay calling hide until the last animation has completed
|
|
CATransaction.setCompletionBlock({
|
|
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DefaultValues.animationDuration, execute: {
|
|
self.hideProgressBar()
|
|
})
|
|
})
|
|
}
|
|
CATransaction.commit()
|
|
}
|
|
|
|
override open func setProgress(_ progress: Float, animated: Bool) {
|
|
if progress < self.progress && self.progress != 1 {
|
|
return
|
|
}
|
|
// Setup animations
|
|
gradientLayer.removeAnimation(forKey: "position")
|
|
if gradientLayer.animation(forKey: "colorChange") == nil {
|
|
animateGradient()
|
|
}
|
|
super.setProgress(progress, animated: animated)
|
|
updateAlphaMaskLayerWidth(animated: animated)
|
|
}
|
|
}
|
|
|
|
extension CGRect {
|
|
func updateWidth(byPercentage percentage: CGFloat) -> CGRect {
|
|
return CGRect(x: origin.x, y: origin.y, width: size.width * percentage, height: size.height)
|
|
}
|
|
}
|