Внедрите Как и в каком круге прогресса истории приложения - PullRequest
2 голосов
/ 06 апреля 2020

Я пытаюсь выполнить следующий прогресс

enter image description here

Используя MKMagneticProgress, я смог применить нижнее пространство, но теперь я застрял на том, как сделать пробелы в круге, например, на этом изображении у нас есть прогресс 7 пунктов из 10.

Я хочу обновить код MKMagneticProgress, чтобы добавить пробелы.

код, полученный от MKMagneticProgress с моим обновлением (я добавил общее)

import UIKit
// MARK: - Line Cap Enum

public enum LineCap : Int{
    case round, butt, square

    public func style() -> String {
        switch self {
            case .round:
                return CAShapeLayerLineCap.round.rawValue
            case .butt:
                return CAShapeLayerLineCap.butt.rawValue
            case .square:
                return CAShapeLayerLineCap.square.rawValue
        }
    }
}

// MARK: - Orientation Enum

public enum Orientation: Int  {
    case left, top, right, bottom

}

@IBDesignable
open class CircleProgress: UIView {

    // MARK: - Variables
    private let titleLabelWidth:CGFloat = 100

    private let percentLabel = UILabel(frame: .zero)
    @IBInspectable open var titleLabel = UILabel(frame: .zero)

    /// Stroke background color
    @IBInspectable open var clockwise: Bool = true {
        didSet {
            layoutSubviews()
        }
    }

    /// Stroke background color
    @IBInspectable open var backgroundShapeColor: UIColor = UIColor(white: 0.9, alpha: 0.5) {
        didSet {
            updateShapes()
        }
    }

    /// Progress stroke color
    @IBInspectable open var progressShapeColor: UIColor   = .blue {
        didSet {
            updateShapes()
        }
    }

    /// Line width
    @IBInspectable open var lineWidth: CGFloat = 8.0 {
        didSet {
            updateShapes()
        }
    }

    /// Space value
    @IBInspectable open var spaceDegree: CGFloat = 45.0 {
        didSet {
            //            if spaceDegree < 45.0{
            //                spaceDegree = 45.0
            //            }
            //
            //            if spaceDegree > 135.0{
            //                spaceDegree = 135.0
            //            }

            layoutSubviews()

            updateShapes()
        }
    }

    /// The progress shapes line width will be the `line width` minus the `inset`.
    @IBInspectable open var inset: CGFloat = 0.0 {
        didSet {
            updateShapes()
        }
    }

    // The progress percentage label(center label) format
    @IBInspectable open var percentLabelFormat: String = "%.f %%" {
        didSet {
            percentLabel.text = String(format: percentLabelFormat, progress * 100)
        }
    }

    @IBInspectable open var percentColor: UIColor = UIColor(white: 0.9, alpha: 0.5) {
        didSet {
            percentLabel.textColor = percentColor
        }
    }


    /// progress text (progress bottom label)
    @IBInspectable open var title: String = "" {
        didSet {
            titleLabel.text = title
        }
    }

    @IBInspectable open var titleColor: UIColor = UIColor(white: 0.9, alpha: 0.5) {
        didSet {
            titleLabel.textColor = titleColor
        }
    }


    // progress text (progress bottom label)
    @IBInspectable  open var font: UIFont = .systemFont(ofSize: 13) {
        didSet {
            titleLabel.font = font
            percentLabel.font = font
        }
    }


    // progress Orientation
    open var orientation: Orientation = .bottom {
        didSet {
            updateShapes()
        }
    }

    /// Progress shapes line cap.
    open var lineCap: LineCap = .round {
        didSet {
            updateShapes()
        }
    }

    /// Returns the total Items
    private var _total: CGFloat = 1.0
    @IBInspectable open var total: CGFloat {
        set {
            self._total = newValue
            self.setProgress(progress: self.progress)
        }
        get {
            return self._total
        }
    }
    /// Returns the current progress.
    @IBInspectable open private(set) var progress: CGFloat {
        set {
            progressShape?.strokeEnd = newValue
        }
        get {
            return progressShape.strokeEnd
        }
    }

    /// Duration for a complete animation from 0.0 to 1.0.
    open var completeDuration: Double = 2.0

    private var backgroundShape: CAShapeLayer!
    private var progressShape: CAShapeLayer!

    private var progressAnimation: CABasicAnimation!

    // MARK: - Init

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    public override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    private func setup() {

        backgroundShape = CAShapeLayer()
        backgroundShape.fillColor = nil
        backgroundShape.strokeColor = backgroundShapeColor.cgColor
        layer.addSublayer(backgroundShape)

        progressShape = CAShapeLayer()
        progressShape.fillColor   = nil
        progressShape.strokeStart = 0.0
        progressShape.strokeEnd   = 0.1
        layer.addSublayer(progressShape)

        progressAnimation = CABasicAnimation(keyPath: "strokeEnd")

        percentLabel.frame = self.bounds
        percentLabel.textAlignment = .center
        //        percentLabel.textColor = self.progressShapeColor
        self.addSubview(percentLabel)
        percentLabel.text = String(format: "%.1f%%", progress * 100)


        titleLabel.frame = CGRect(x: (self.bounds.size.width-titleLabelWidth)/2, y: self.bounds.size.height-21, width: titleLabelWidth, height: 21)

        titleLabel.textAlignment = .center
        //        titleLabel.textColor = self.progressShapeColor
        titleLabel.text = title
        titleLabel.contentScaleFactor = 0.3
        //        textLabel.adjustFontSizeToFit()
        titleLabel.numberOfLines = 2

        //textLabel.adjustFontSizeToFit()
        titleLabel.adjustsFontSizeToFitWidth = true
        self.addSubview(titleLabel)
    }

    // MARK: - Progress Animation

    public func setProgress(progress: CGFloat, animated: Bool = true) {
        let actualProgress =  progress/self._total
        if actualProgress > 1.0 {
            return
        }

        var start = progressShape.strokeEnd
        if let presentationLayer = progressShape.presentation(){
            if let count = progressShape.animationKeys()?.count, count > 0  {
                start = presentationLayer.strokeEnd
            }
        }

        let duration = abs(Double(progress - start)) * completeDuration
        percentLabel.text = String(format: percentLabelFormat, actualProgress * 100)
        progressShape.strokeEnd = actualProgress

        if animated {
            progressAnimation.fromValue = start
            progressAnimation.toValue   = actualProgress
            progressAnimation.duration  = duration
            progressShape.add(progressAnimation, forKey: progressAnimation.keyPath)
        }
    }

    // MARK: - Layout

    open override func layoutSubviews() {

        super.layoutSubviews()

        backgroundShape.frame = bounds
        progressShape.frame   = bounds

        let rect = rectForShape()
        backgroundShape.path = pathForShape(rect: rect).cgPath
        progressShape.path   = pathForShape(rect: rect).cgPath

        self.titleLabel.frame = CGRect(x: (self.bounds.size.width - titleLabelWidth)/2, y: self.bounds.size.height-50, width: titleLabelWidth, height: 42)

        updateShapes()

        percentLabel.frame = self.bounds
    }

    private func updateShapes() {
        backgroundShape?.lineWidth  = lineWidth
        backgroundShape?.strokeColor = backgroundShapeColor.cgColor
        backgroundShape?.lineCap     = CAShapeLayerLineCap(rawValue: lineCap.style())

        progressShape?.strokeColor = progressShapeColor.cgColor
        progressShape?.lineWidth   = lineWidth - inset
        progressShape?.lineCap     = CAShapeLayerLineCap(rawValue: lineCap.style())

        switch orientation {
            case .left:
                titleLabel.isHidden = true
                self.progressShape.transform = CATransform3DMakeRotation( CGFloat.pi / 2, 0, 0, 1.0)
                self.backgroundShape.transform = CATransform3DMakeRotation(CGFloat.pi / 2, 0, 0, 1.0)
            case .right:
                titleLabel.isHidden = true
                self.progressShape.transform = CATransform3DMakeRotation( CGFloat.pi * 1.5, 0, 0, 1.0)
                self.backgroundShape.transform = CATransform3DMakeRotation(CGFloat.pi * 1.5, 0, 0, 1.0)
            case .bottom:
                titleLabel.isHidden = false
                UIView.animate(withDuration: 0.3, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.0, options: [] , animations: { [weak self] in
                    if let temp = self{
                        temp.titleLabel.frame = CGRect(x: (temp.bounds.size.width - temp.titleLabelWidth)/2, y: temp.bounds.size.height-50, width: temp.titleLabelWidth, height: 42)
                    }

                    }, completion: nil)
                self.progressShape.transform = CATransform3DMakeRotation( CGFloat.pi * 2, 0, 0, 1.0)
                self.backgroundShape.transform = CATransform3DMakeRotation(CGFloat.pi * 2, 0, 0, 1.0)
            case .top:
                titleLabel.isHidden = false
                UIView.animate(withDuration: 0.3, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.0, options: [] , animations: { [weak self] in
                    if let temp = self{
                        temp.titleLabel.frame = CGRect(x: (temp.bounds.size.width - temp.titleLabelWidth)/2, y: 0, width: temp.titleLabelWidth, height: 42)
                    }

                    }, completion: nil)
                self.progressShape.transform = CATransform3DMakeRotation( CGFloat.pi, 0, 0, 1.0)
                self.backgroundShape.transform = CATransform3DMakeRotation(CGFloat.pi, 0, 0, 1.0)
        }
    }

    // MARK: - Helper

    private func rectForShape() -> CGRect {
        return bounds.insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0)
    }
    private func pathForShape(rect: CGRect) -> UIBezierPath {
        let startAngle:CGFloat!
        let endAngle:CGFloat!

        if clockwise{
            startAngle = CGFloat(spaceDegree * .pi / 180.0) + (0.5 * .pi)
            endAngle = CGFloat((360.0 - spaceDegree) * (.pi / 180.0)) + (0.5 * .pi)
        }else{
            startAngle = CGFloat((360.0 - spaceDegree) * (.pi / 180.0)) + (0.5 * .pi)
            endAngle = CGFloat(spaceDegree * .pi / 180.0) + (0.5 * .pi)
        }
        let path = UIBezierPath(arcCenter: CGPoint(x: rect.midX, y: rect.midY), radius: rect.size.width / 2.0, startAngle: startAngle, endAngle: endAngle
            , clockwise: clockwise)

        return path
    }
}

Мой вывод: enter image description here Спасибо.

1 Ответ

0 голосов
/ 08 апреля 2020

Мне удалось выяснить, как обновить код для добавления нескольких дуг, вот обновленный код

import UIKit
// MARK: - Line Cap Enum

public enum LineCap : Int{
    case round, butt, square

    public func style() -> String {
        switch self {
            case .round:
                return CAShapeLayerLineCap.round.rawValue
            case .butt:
                return CAShapeLayerLineCap.butt.rawValue
            case .square:
                return CAShapeLayerLineCap.square.rawValue
        }
    }
}

// MARK: - Orientation Enum

public enum Orientation: Int  {
    case left, top, right, bottom

}

@IBDesignable
open class CircleProgress: UIView {

    // MARK: - Variables
    private let titleLabelWidth:CGFloat = 100

    private let percentLabel = UILabel(frame: .zero)
    @IBInspectable open var titleLabel = UILabel(frame: .zero)

    /// Stroke background color
    @IBInspectable open var clockwise: Bool = true {
        didSet {
            layoutSubviews()
        }
    }

    /// Stroke background color
    @IBInspectable open var backgroundShapeColor: UIColor = UIColor(white: 0.9, alpha: 0.5) {
        didSet {
            updateShapes()
        }
    }

    /// Progress stroke color
    @IBInspectable open var progressShapeColor: UIColor   = .blue {
        didSet {
            updateShapes()
        }
    }

    /// Line width
    @IBInspectable open var lineWidth: CGFloat = 5.0 {
        didSet {
            updateShapes()
        }
    }

    /// Space value
    @IBInspectable open var spaceDegree: CGFloat = 45.0 {
        didSet {
            //            if spaceDegree < 45.0{
            //                spaceDegree = 45.0
            //            }
            //
            //            if spaceDegree > 135.0{
            //                spaceDegree = 135.0
            //            }

            layoutSubviews()

            updateShapes()
        }
    }

    /// The progress shapes line width will be the `line width` minus the `inset`.
    @IBInspectable open var inset: CGFloat = 0.0 {
        didSet {
            updateShapes()
        }
    }

    // The progress percentage label(center label) format
    @IBInspectable open var percentLabelFormat: String = "%.f %%" {
        didSet {
            percentLabel.text = String(format: percentLabelFormat, progress * 100)
        }
    }

    @IBInspectable open var percentColor: UIColor = UIColor(white: 0.9, alpha: 0.5) {
        didSet {
            percentLabel.textColor = percentColor
        }
    }


    /// progress text (progress bottom label)
    @IBInspectable open var title: String = "" {
        didSet {
            titleLabel.text = title
        }
    }

    @IBInspectable open var titleColor: UIColor = UIColor(white: 0.9, alpha: 0.5) {
        didSet {
            titleLabel.textColor = titleColor
        }
    }


    // progress text (progress bottom label)
    @IBInspectable  open var font: UIFont = .systemFont(ofSize: 13) {
        didSet {
            titleLabel.font = font
            percentLabel.font = font
        }
    }


    // progress Orientation
    open var orientation: Orientation = .bottom {
        didSet {
            updateShapes()
        }
    }

    /// Progress shapes line cap.
    open var lineCap: LineCap = .round {
        didSet {
            updateShapes()
        }
    }

    /// Returns the total Items
    private var _total: Int = 1
    @IBInspectable open var total: Int {
        set {
            self._total = newValue
            self.addProgressShapes()
        }
        get {
            return self._total
        }
    }
    /// Returns the current progress.
    private var _progress: CGFloat = 0.0
    @IBInspectable open private(set) var progress: CGFloat {
        set {
            self._progress = newValue
        }
        get {
            return self._progress
        }
    }

    /// Duration for a complete animation from 0.0 to 1.0.
    open var completeDuration: Double = 1.0

    private var backgroundShape: CAShapeLayer!
    private var progressShapes: [CAShapeLayer]!

    private var progressAnimation: CABasicAnimation!

    // MARK: - Init

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

    public override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    private func setup() {

        backgroundShape = CAShapeLayer()
        backgroundShape.fillColor = nil
        backgroundShape.strokeColor = backgroundShapeColor.cgColor
        layer.addSublayer(backgroundShape)


        progressAnimation = CABasicAnimation(keyPath: "strokeEnd")

        percentLabel.frame = self.bounds
        percentLabel.textAlignment = .center
        //        percentLabel.textColor = self.progressShapeColor
        self.addSubview(percentLabel)
        percentLabel.text = String(format: "%.1f%%", progress * 100)


        titleLabel.frame = CGRect(x: (self.bounds.size.width-titleLabelWidth)/2, y: self.bounds.size.height-21, width: titleLabelWidth, height: 21)

        titleLabel.textAlignment = .center
        //        titleLabel.textColor = self.progressShapeColor
        titleLabel.text = title
        titleLabel.contentScaleFactor = 0.3
        //        textLabel.adjustFontSizeToFit()
        titleLabel.numberOfLines = 2

        //textLabel.adjustFontSizeToFit()
        titleLabel.adjustsFontSizeToFitWidth = true
        self.addSubview(titleLabel)
    }

    // MARK: - Progress Animation

    private func addProgressShapes(){
        self.progressShapes = []
        let progressSize = 1.0
        var size:CGFloat = CGFloat(progressSize / Double(self.total))
        let padingPercent = 0.2
        let pading: Double =  padingPercent * Double(self.total)/10 * Double(progressSize / Double(self.total - 1))
        size = size  - CGFloat(pading)
        print("")
        print("size: \(size) | pading: \(pading)")
        print("------------------------")
        var start: CGFloat = 0.0
        var end: CGFloat = size
        print("start: \(start) | end: \(end) ")
        print("------------------------")

        let progressShape: CAShapeLayer!
        progressShape = CAShapeLayer()
        progressShape.fillColor   = nil
        progressShape.strokeStart = start
        progressShape.strokeEnd   = end
        layer.addSublayer(progressShape)
        self.progressShapes.append(progressShape)

        for _ in 1..<self._total {
            start = CGFloat(end +  CGFloat(pading))
            end = CGFloat(Double(start + size))
            print("------------------------")
            print("start: \(start) | end: \(end) ")
            let progressShape: CAShapeLayer!
            progressShape = CAShapeLayer()
            progressShape.fillColor   = nil
            progressShape.strokeStart = start
            progressShape.strokeEnd   = end
            layer.addSublayer(progressShape)
            self.progressShapes.append(progressShape)
        }
    }
    private func setProgressShapeFrame(){
        guard self.progressShapes != nil else { return }
        for shape in self.progressShapes {
            shape.frame   = bounds
            let rect = rectForShape()
            shape.path   = pathForShape(rect: rect).cgPath
        }
    }
    private func updateShapesWidth(){
        guard self.progressShapes != nil else { return }
        for i in 0..<self._total {
            let shape = self.progressShapes[i]
            let color = CGFloat(i) >= self.progress ? UIColor.white.withAlphaComponent(0.1).cgColor:progressShapeColor.cgColor
            shape.strokeColor = color
            shape.lineWidth   = lineWidth - inset
            shape.lineCap     = CAShapeLayerLineCap(rawValue: lineCap.style())
        }
    }
    private func transformShapes(_ transform: CATransform3D){
        guard self.progressShapes != nil else { return }
        for shape in self.progressShapes {
            shape.transform = transform
        }
    }
    public func setProgress(progress: CGFloat, animated: Bool = true) {
        self._progress = progress
        //        let actualProgress =  progress/self._total
        //        if actualProgress > 1.0 {
        //            return
        //        }
        //
        //        var start = progressShape.strokeEnd
        //        if let presentationLayer = progressShape.presentation(){
        //            if let count = progressShape.animationKeys()?.count, count > 0  {
        //                start = presentationLayer.strokeEnd
        //            }
        //        }
        //
        //        let duration = abs(Double(progress - start)) * completeDuration
        //        percentLabel.text = String(format: percentLabelFormat, actualProgress * 100)
        //        progressShape.strokeEnd = actualProgress
        //
        //        if animated {
        //            progressAnimation.fromValue = start
        //            progressAnimation.toValue   = actualProgress
        //            progressAnimation.duration  = duration
        //            progressShape.add(progressAnimation, forKey: progressAnimation.keyPath)
        //        }
    }

    // MARK: - Layout

    open override func layoutSubviews() {

        super.layoutSubviews()

        backgroundShape.frame = bounds


        let rect = rectForShape()
        backgroundShape.path = pathForShape(rect: rect).cgPath

        setProgressShapeFrame()
        self.titleLabel.frame = CGRect(x: (self.bounds.size.width - titleLabelWidth)/2, y: self.bounds.size.height-50, width: titleLabelWidth, height: 42)

        updateShapes()

        percentLabel.frame = self.bounds
    }

    private func updateShapes() {
        backgroundShape?.lineWidth  = lineWidth
        backgroundShape?.strokeColor = backgroundShapeColor.cgColor
        backgroundShape?.lineCap     = CAShapeLayerLineCap(rawValue: lineCap.style())
        self.updateShapesWidth()


        switch orientation {
            case .left:
                titleLabel.isHidden = true
                self.transformShapes(CATransform3DMakeRotation( CGFloat.pi / 2, 0, 0, 1.0))
                self.backgroundShape.transform = CATransform3DMakeRotation(CGFloat.pi / 2, 0, 0, 1.0)
            case .right:
                titleLabel.isHidden = true
                self.transformShapes(CATransform3DMakeRotation( CGFloat.pi * 1.5, 0, 0, 1.0))
                self.backgroundShape.transform = CATransform3DMakeRotation(CGFloat.pi * 1.5, 0, 0, 1.0)
            case .bottom:
                titleLabel.isHidden = false
                UIView.animate(withDuration: 0.3, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.0, options: [] , animations: { [weak self] in
                    if let temp = self{
                        temp.titleLabel.frame = CGRect(x: (temp.bounds.size.width - temp.titleLabelWidth)/2, y: temp.bounds.size.height-50, width: temp.titleLabelWidth, height: 42)
                    }

                    }, completion: nil)
                self.transformShapes(CATransform3DMakeRotation( CGFloat.pi * 2, 0, 0, 1.0))
                self.backgroundShape.transform = CATransform3DMakeRotation(CGFloat.pi * 2, 0, 0, 1.0)
            case .top:
                titleLabel.isHidden = false
                UIView.animate(withDuration: 0.3, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.0, options: [] , animations: { [weak self] in
                    if let temp = self{
                        temp.titleLabel.frame = CGRect(x: (temp.bounds.size.width - temp.titleLabelWidth)/2, y: 0, width: temp.titleLabelWidth, height: 42)
                    }

                    }, completion: nil)
                self.transformShapes(CATransform3DMakeRotation( CGFloat.pi, 0, 0, 1.0))
                self.backgroundShape.transform = CATransform3DMakeRotation(CGFloat.pi, 0, 0, 1.0)
        }
    }

    // MARK: - Helper

    private func rectForShape() -> CGRect {
        return bounds.insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0)
    }
    private func pathForShape(rect: CGRect) -> UIBezierPath {
        let startAngle:CGFloat!
        let endAngle:CGFloat!

        if clockwise{
            startAngle = CGFloat(spaceDegree * .pi / 180.0) + (0.5 * .pi)
            endAngle = CGFloat((360.0 - spaceDegree) * (.pi / 180.0)) + (0.5 * .pi)
        }else{
            startAngle = CGFloat((360.0 - spaceDegree) * (.pi / 180.0)) + (0.5 * .pi)
            endAngle = CGFloat(spaceDegree * .pi / 180.0) + (0.5 * .pi)
        }
        let path = UIBezierPath(arcCenter: CGPoint(x: rect.midX, y: rect.midY), radius: rect.size.width / 2.0, startAngle: startAngle, endAngle: endAngle
            , clockwise: clockwise)

        return path
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...