【问题标题】:Circular Progress Bars in IOSIOS 中的圆形进度条
【发布时间】:2012-11-14 10:49:15
【问题描述】:

我想创建一个圆形进度条,如下所示:

如何使用 Objective-C 和 Cocoa 做到这一点?

我是如何开始创建 UIView 并编辑 drawRect 的,但我有点迷茫。任何帮助将不胜感激。

谢谢!

【问题讨论】:

  • 顺便说一句,您可以在问题中包含图片。它使我们免于跳转到另一个网站来查看它们。
  • 只有rep够高的用户才能收录图片,不确定31够不够高。
  • @WDuk 它一定是一个低级别,因为我确信我看到有人发布图像的次数少于 100。我刚刚检查了元堆栈溢出,他们建议 10 代表是发布图像的最低要求。
  • 自己做,不用担心。
  • 这正是你要找的:github.com/marshluca/AudioPlayer 你也可以参考一些资料:github.com/lipka/LLACircularProgressView

标签: objective-c cocoa-touch ios5 ios4 ios6


【解决方案1】:

基本概念是利用UIBezierPath 类来发挥自己的优势。您可以绘制弧线,从而达到您所追求的效果。我只有半个小时左右的时间来解决这个问题,但我的尝试如下。

非常简陋,它只是在路径上使用笔划,但我们开始了。您可以根据您的确切需要更改/修改它,但进行弧形倒计时的逻辑将非常相似。

在视图类中:

@interface TestView () {
    CGFloat startAngle;
    CGFloat endAngle;
}

@end

@implementation TestView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        self.backgroundColor = [UIColor whiteColor];

        // Determine our start and stop angles for the arc (in radians)
        startAngle = M_PI * 1.5;
        endAngle = startAngle + (M_PI * 2);

    }
    return self;
}

- (void)drawRect:(CGRect)rect
{
    // Display our percentage as a string
    NSString* textContent = [NSString stringWithFormat:@"%d", self.percent];

    UIBezierPath* bezierPath = [UIBezierPath bezierPath];

    // Create our arc, with the correct angles
    [bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2) 
                          radius:130 
                      startAngle:startAngle
                        endAngle:(endAngle - startAngle) * (_percent / 100.0) + startAngle
                       clockwise:YES];

    // Set the display for the path, and stroke it
    bezierPath.lineWidth = 20;
    [[UIColor redColor] setStroke];
    [bezierPath stroke];

    // Text Drawing
    CGRect textRect = CGRectMake((rect.size.width / 2.0) - 71/2.0, (rect.size.height / 2.0) - 45/2.0, 71, 45);
    [[UIColor blackColor] setFill];
    [textContent drawInRect: textRect withFont: [UIFont fontWithName: @"Helvetica-Bold" size: 42.5] lineBreakMode: NSLineBreakByWordWrapping alignment: NSTextAlignmentCenter];
}

对于视图控制器:

@interface ViewController () {    
    TestView* m_testView;
    NSTimer* m_timer;
}

@end

- (void)viewDidLoad
{
    // Init our view
    [super viewDidLoad];
    m_testView = [[TestView alloc] initWithFrame:self.view.bounds];
    m_testView.percent = 100;
    [self.view addSubview:m_testView];
}

- (void)viewDidAppear:(BOOL)animated
{
    // Kick off a timer to count it down
    m_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(decrementSpin) userInfo:nil repeats:YES];
}

- (void)decrementSpin
{
    // If we can decrement our percentage, do so, and redraw the view
    if (m_testView.percent > 0) {
        m_testView.percent = m_testView.percent - 1;
        [m_testView setNeedsDisplay];
    }
    else {
       [m_timer invalidate];
       m_timer = nil;
    }
}

【讨论】:

  • 你知道我怎样才能让背景图片看起来像上面的图片吗?
  • 我怎样才能得到这个有 ronnded 结束?
  • @Siriss 你试过在UIBezierPath上更改lineCapStyle吗?
  • 谢谢!是的,我终于想通了,只是忘记回来更新了。
  • 好帖子,谢谢!给绝对初学者的一些注意事项:
    - [self.view addSubview:m_webView]; 当然应该是[self.view addSubview: m_testView]; - TestView.h 应该是这样的:
    #import <UIKit/UIKit.h> @interface UICircle : UIView @property (nonatomic) double percent; @end
【解决方案2】:

我已经为 iOS 实现了一个简单的库来做这件事。它基于 UILabel 类,因此您可以在进度条中显示您想要的任何内容,但您也可以将其留空。

一旦初始化,你只有一行代码来设置进度:

[_myProgressLabel setProgress:(50/100))];

库被命名为KAProgressLabel

【讨论】:

【解决方案3】:

我的幻数示例(为了更好地理解):

  CAShapeLayer *circle = [CAShapeLayer layer];
  circle.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(29, 29) radius:27 startAngle:-M_PI_2 endAngle:2 * M_PI - M_PI_2 clockwise:YES].CGPath;
  circle.fillColor = [UIColor clearColor].CGColor;
  circle.strokeColor = [UIColor greenColor].CGColor;
  circle.lineWidth = 4;

  CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
  animation.duration = 10;
  animation.removedOnCompletion = NO;
  animation.fromValue = @(0);
  animation.toValue = @(1);
  animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
  [circle addAnimation:animation forKey:@"drawCircleAnimation"];

  [imageCircle.layer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
  [imageCircle.layer addSublayer:circle];

【讨论】:

  • 你如何获得主角 CGPoint 的动画效果?
  • @whyoz,我使用了在中心绘制弧线的方法 bezierPathWithArcCenter
  • 好吧,我知道!哈哈,如果你知道如何做到这一点,如果我想在它围绕中心移动时跟踪那个点,我正在尝试从你那里获取更多信息。所以从 startAngle 的第一个点以 CGPoint 的形式获取“startPoint”..认为可能有一个快速的属性可以抓取..
  • @whyoz,您想实时跟踪 CGPoint 吗?我不知道简单的方法来做到这一点。但是你可以计算这一点。像这样: 1 获取当前角度 - [当前时间秒数 - 开始时间秒数] * 360 / [持续时间秒数]。 2 我们知道角度和半径。我们需要计算圆上的点。 x = 半径 * sin(当前角度),y = 半径 * cos(当前角度)。我希望这会对你有所帮助。
  • 我们可以用进度值而不是持续时间来显示这个动画吗?
【解决方案4】:

对于 Swift 使用这个,

let circle = UIView(frame: CGRectMake(0,0, 100, 100))

circle.layoutIfNeeded()

let centerPoint = CGPoint (x: circle.bounds.width / 2, y: circle.bounds.width / 2)
let circleRadius : CGFloat = circle.bounds.width / 2 * 0.83

var circlePath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: CGFloat(-0.5 * M_PI), endAngle: CGFloat(1.5 * M_PI), clockwise: true    )

let progressCircle = CAShapeLayer()
progressCircle.path = circlePath.CGPath
progressCircle.strokeColor = UIColor.greenColor().CGColor
progressCircle.fillColor = UIColor.clearColor().CGColor
progressCircle.lineWidth = 1.5
progressCircle.strokeStart = 0
progressCircle.strokeEnd = 0.22

circle.layer.addSublayer(progressCircle)

self.view.addSubview(circle)

参考:见Here

【讨论】:

    【解决方案5】:

    你可以查看我的库MBCircularProgressBar

    【讨论】:

    • 有没有办法在没有 pod 的情况下将你的库添加到 Xcode 项目中?谢谢
    • 当然,只需将图层和视图文件复制到您的项目中
    • 如何填写完整的进度条?
    • 是否有任何进度变更委托可用?我的意思是,一个人可以给 40 秒的时间来达到完整的进度值 100,如果有人想在进度值达到 60% 时做某事
    【解决方案6】:

    Swift 3 使用这个,

    带有动画的 CAShapeLayer :继续 Zaid Pathan ans。

        let circle = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
    
        circle.layoutIfNeeded()
    
        var progressCircle = CAShapeLayer()
    
        let centerPoint = CGPoint (x: circle.bounds.width / 2, y: circle.bounds.width / 2)
        let circleRadius : CGFloat = circle.bounds.width / 2 * 0.83
    
        let circlePath = UIBezierPath(arcCenter: centerPoint, radius: circleRadius, startAngle: CGFloat(-0.5 * M_PI), endAngle: CGFloat(1.5 * M_PI), clockwise: true    )
    
        progressCircle = CAShapeLayer ()
        progressCircle.path = circlePath.cgPath
        progressCircle.strokeColor = UIColor.green.cgColor
        progressCircle.fillColor = UIColor.clear.cgColor
        progressCircle.lineWidth = 2.5
        progressCircle.strokeStart = 0
        progressCircle.strokeEnd = 1.0
         circle.layer.addSublayer(progressCircle)
    
    
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = 0
        animation.toValue = 1.0
        animation.duration = 5.0
        animation.fillMode = kCAFillModeForwards
        animation.isRemovedOnCompletion = false
         progressCircle.add(animation, forKey: "ani")
    
        self.view.addSubview(circle)
    

    【讨论】:

    • 0.83 是干什么用的? width / 2 是不是圆半径不够?
    【解决方案7】:

    这里有一个 Swift 示例,说明如何制作一个简单的、未封闭的(为长数字留出空间)带有圆角和动画的圆形进度条。

    open_circular_progress_bar.jpg

    func drawBackRingFittingInsideView(lineWidth: CGFloat, lineColor: UIColor) {
    
        let halfSize:CGFloat = min( bounds.size.width/2, bounds.size.height/2)
    
        let desiredLineWidth:CGFloat = lineWidth
    
        let circle = CGFloat(Double.pi * 2)
    
        let startAngle = CGFloat(circle * 0.1)
    
        let endAngle = circle – startAngle
    
        let circlePath = UIBezierPath(
    
            arcCenter: CGPoint(x:halfSize, y:halfSize),
    
            radius: CGFloat( halfSize – (desiredLineWidth/2) ),
    
            startAngle: startAngle,
    
            endAngle: endAngle,
    
            clockwise: true)
    
        let shapeBackLayer = CAShapeLayer()
    
            shapeBackLayer.path = circlePath.cgPath
    
            shapeBackLayer.fillColor = UIColor.clear.cgColor
    
            shapeBackLayer.strokeColor = lineColor.cgColor
    
            shapeBackLayer.lineWidth = desiredLineWidth
    
            shapeBackLayer.lineCap = .round
    
        layer.addSublayer(shapeBackLayer)
    
    }
    

    还有动画功能。

     func animateCircle(duration: TimeInterval) {
    
        let animation = CABasicAnimation(keyPath: “strokeEnd”)
    
        animation.duration = duration
    
        animation.fromValue = 0
    
        animation.toValue = 1
    
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)        
    
        shapeLayer.strokeEnd = 1.0
    
        shapeLayer.add(animation, forKey: “animateCircle”)
    
    }
    

    有一个很好的blog 示例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-24
      • 1970-01-01
      • 1970-01-01
      • 2015-08-13
      • 2022-01-17
      • 2023-04-08
      相关资源
      最近更新 更多