使用 setNeedsDisplay 更新 UIView 不起作用

Posted

技术标签:

【中文标题】使用 setNeedsDisplay 更新 UIView 不起作用【英文标题】:Updating UIView with setNeedsDisplay does not work 【发布时间】:2014-10-07 10:28:21 【问题描述】:

我的应用中有一个圆形进度条,用于显示用户已花费的预算百分比。每次用户切换到带有圆形进度条的视图时,进度条都应该更新。每次用户打开应用时,它也应该更新。

目前,由于某种我无法理解的原因,它只会在应用程序打开时更新(从多任务处理中关闭),并且如果用户切换到该视图(它应该)则永远不会更新。我进行了大量研究,但未能找到解决方案。

这是创建圆圈的代码:

var progress: CGFloat = 0

class ProgressCircle: UIView 

override func drawRect(rect: CGRect) 
    var ctx = UIGraphicsGetCurrentContext()

    var innerRadiusRatio: CGFloat = 0.6

    var path: CGMutablePathRef = CGPathCreateMutable()
    var startAngle: CGFloat = CGFloat(-M_PI_2)
    var endAngle: CGFloat = CGFloat(-M_PI_2) + min(1.0, progress) * CGFloat(M_PI * 2)
    var outerRadius: CGFloat = CGRectGetWidth(self.bounds) * 0.5 - 1.0
    var innerRadius: CGFloat = outerRadius * innerRadiusRatio
    var center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect))

    //Code for background circle

    var context: CGContextRef = UIGraphicsGetCurrentContext()
    colourTheme = NSUserDefaults.standardUserDefaults().integerForKey("themeSettings")
    if colourTheme == 1 
        CGContextSetFillColorWithColor(context, (UIColorFromRGB(0xEAEAEA)).CGColor)
     else 
        CGContextSetFillColorWithColor(context, (UIColor.darkGrayColor()).CGColor)
    

    var circleSize: CGFloat = 0
    switch height 
    case 480, 568:
        circleSize = 171
    default:
        circleSize = 250
    

    var rectangle: CGRect = CGRectMake(0, 0, circleSize, circleSize)
    CGContextBeginPath(context)
    CGContextAddEllipseInRect(context, rectangle)
    CGContextDrawPath(context, kCGPathFill)

    UIGraphicsEndImageContext()

    if colourTheme == 1 
        CGContextSetFillColorWithColor(context, (UIColor.darkGrayColor()).CGColor)
     else 
        CGContextSetFillColorWithColor(context, (UIColorFromRGB(0xEAEAEA)).CGColor)
    

    var circleXY: CGFloat = 0
    var circleWH: CGFloat = 0
    switch height 
    case 480, 568:
        circleXY = 35.95
        circleWH = 99.1
    default:
        circleXY = 52
        circleWH = 146
    
    var rectangleSmall: CGRect = CGRectMake(circleXY, circleXY, circleWH, circleWH) //102.6
    CGContextBeginPath(context)
    CGContextAddEllipseInRect(context, rectangleSmall)
    CGContextDrawPath(context, kCGPathFill)

    UIGraphicsEndImageContext()

    //Code for progress radius

    CGPathAddArc(path, nil, center.x, center.y, innerRadius, startAngle, endAngle, false)
    CGPathAddArc(path, nil, center.x, center.y, outerRadius, endAngle, startAngle, true)
    CGPathCloseSubpath(path)
    CGContextAddPath(ctx, path)

    CGContextSaveGState(ctx)
    CGContextClip(ctx)
    if percent > 100 
        CGContextDrawImage(ctx, self.bounds, UIImage(named: "RadialProgressFillOver").CGImage)
     else 
        CGContextDrawImage(ctx, self.bounds, UIImage(named: "RadialProgressFill").CGImage)
    
    CGContextRestoreGState(ctx)

这是我用来(尝试)更新进度圈的代码。此代码在 viewDidAppear() 中:

override func viewDidAppear(animated: Bool) 
    //Show set up screen if user hasn't used AffordIt before.

    userReturned = NSUserDefaults.standardUserDefaults().boolForKey("userReturnedCheck")
    if userReturned == false 
        self.performSegueWithIdentifier("setupView", sender: self)
    

    //Add circular progress bar to budget display view.

    var circleSize: CGFloat = 0
    var yCoord: CGFloat = 0
    switch height 
    case 480, 568:
        circleSize = 171
        yCoord = 249
    case 667:
        circleSize = 250
        yCoord = 249
    default:
        circleSize = 250
        yCoord = 279
    

    progress = CGFloat(percent/100)
    var circleFrame = CGRect(x: (budgetDisplayView.bounds.width/2)-(circleSize/2), y: yCoord, width: circleSize, height: circleSize)
    var circle = ProgressCircle(frame: circleFrame)
    self.budgetDisplayView.addSubview(circle)
    self.budgetDisplayView.sendSubviewToBack(circle)
    circle.setNeedsDisplay()
    view.setNeedsDisplay()

    circle.backgroundColor = UIColor.clearColor()

我真的希望有人能提供解决方案,因为我已经遇到这个问题大约一周了。谢谢。

【问题讨论】:

【参考方案1】:

每次您的视图控制器出现并调用viewDidAppear() 时,您都会创建一个全新 ProgressCircle 视图并将其发送到后面。除非您在其他地方执行此操作,否则您似乎不会从 budgetDisplayView 中删除 ProgressCircle。我说的对吗?

如果我是对的,当应用程序从完全关闭状态打开时,viewDidAppear() 将在该视图控制器上首次调用,并且将创建一个 single 进度圈并显示为你在观察。下次显示视图控制器时,再次调用viewDidAppear()另一个进度圈被创建并添加到上次创建的后面!以后每次都会发生同样的事情。我想这就是你看不到它的原因。作为快速测试(但不是长期解决方案),请尝试评论以下行:self.budgetDisplayView.sendSubviewToBack(circle)。希望对您有所帮助。

【讨论】:

哇。确实是这个问题!我从来没有注意到这一点,非常感谢!

以上是关于使用 setNeedsDisplay 更新 UIView 不起作用的主要内容,如果未能解决你的问题,请参考以下文章

使用 setNeedsDisplay

在哪里调用 setNeedsDisplay 来更新 CALayer?

Cocoa - iOS - 更新 UILabel 的文本属性是不是需要 setNeedsDisplay?

setNeedsDisplay 是立即生效还是只是排队等候?

在 for 循环中调用 setNeedsDisplay

UIScrollView 子视图和 setNeedsDisplay