2013-05-11 80 views
6

我正在爲自定義遊戲中心成就視圖開發一個圓形進度欄,並且我有種「碰壁」的感覺。我在這個問題上掙扎了兩個多小時,仍然無法實現。使用帶有蒙版圖像的圓形進度條?

事情是,我需要一個圓形的進度條,在那裏我可以設置(至少)軌道(填充)圖像。因爲我的進度填充是像梯度的彩虹,我無法僅使用代碼獲得相同的結果。

我搞砸了CALayers和drawRect方法,但是我沒有成功。你能否給我一點指導?

下面是圓形進度條的例子: https://github.com/donnellyk/KDGoalBar https://github.com/danielamitay/DACircularProgress

我只需要填充是一個屏蔽的圖像,根據進展情況。如果你甚至可以使它工作的進展將動畫,那將是非常酷的,但我不要求與:)

感謝幫助,尼克

+0

因此,它是掩蓋或畫一個「彩虹」像漸變,阻止你?還是把這兩塊拼在一起? – 2013-05-11 15:28:36

+0

我可能不清楚,對不起 - 我不想繪製漸變,我在Photoshop中製作的進度條有一個很好的漸變圓形填充,我只需要掩飾它,所以它是填寫進度條。你懂我的意思嗎? :) – 2013-05-11 16:09:06

回答

23

你基本上只需要構造一個路徑定義要填充的區域(例如使用CGPathAddArc),使用CGContextClip將圖形上下文剪切到該路徑,然後繪製圖像。

這裏有一個drawRect:方法,你可以在自定義視圖中使用的例子:

- (void)drawRect:(CGRect)rect 
{ 
    CGContextRef ctx = UIGraphicsGetCurrentContext(); 

    CGFloat progress = 0.7f; //This would be a property of your view 
    CGFloat innerRadiusRatio = 0.5f; //Adjust as needed 

    //Construct the path: 
    CGMutablePathRef path = CGPathCreateMutable(); 
    CGFloat startAngle = -M_PI_2; 
    CGFloat endAngle = -M_PI_2 + MIN(1.0f, progress) * M_PI * 2; 
    CGFloat outerRadius = CGRectGetWidth(self.bounds) * 0.5f - 1.0f; 
    CGFloat innerRadius = outerRadius * innerRadiusRatio; 
    CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); 
    CGPathAddArc(path, NULL, center.x, center.y, innerRadius, startAngle, endAngle, false); 
    CGPathAddArc(path, NULL, center.x, center.y, outerRadius, endAngle, startAngle, true); 
    CGPathCloseSubpath(path); 
    CGContextAddPath(ctx, path); 
    CGPathRelease(path); 

    //Draw the image, clipped to the path: 
    CGContextSaveGState(ctx); 
    CGContextClip(ctx); 
    CGContextDrawImage(ctx, self.bounds, [[UIImage imageNamed:@"RadialProgressFill"] CGImage]); 
    CGContextRestoreGState(ctx); 
} 

爲了簡單起見,我已經硬編碼了一些東西 - 你顯然需要增加一個屬性progress並在設置者中呼叫setNeedsDisplay。這也假定您的項目中有一個名爲RadialProgressFill的圖像。

下面是什麼,將大致看起來像一個例子:

Screenshot

我希望你有一個更好看的背景圖片。 ;)

+0

我希望會有越來越多的人喜歡你 - 這比我預期的要多得多!非常感謝你的詳細答案 - 我已經獎勵你:) – 2013-05-14 18:27:20

+0

@DominikHadl如何以及在哪裏調用此方法 - (void)drawRect:(CGRect)rect – 2013-09-03 05:13:44

+0

@omz它真的有幫助.. :) – 2013-09-04 09:00:06

0

omz提出的解決方案工作laggy當我試圖動畫屬性,所以我一直在尋找。 找到了很好的解決方案 - 使用Quartz Core框架和名爲CADisplayLink的包裝器。你的數據極有可能在每幀改變「這個類是專門爲動畫哪裏製造‘’,它試圖將消息發送到目標每次都會有一些重繪屏幕」 Source

library那這樣工作。

編輯: 但有更有效的解決方案。使圖像上應用於圖層的遮罩動起來。我不知道爲什麼我從一開始就沒有這樣做。 CABasicAnimation比任何自定義繪圖都快。下面是我的實現:

class ProgressBar: UIView { 
    var imageView:UIImageView! 

    var maskLayer: CAShapeLayer! 
    var currentValue: CGFloat = 1 

    override init(frame: CGRect) { 
     super.init(frame: frame) 
     updateUI() 
    } 

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

    func updateUI(){ 
     makeGradient() 
     setUpMask() 
    } 

    func animateCircle(strokeEnd: CGFloat) { 
     let oldStrokeEnd = maskLayer.strokeEnd 
     maskLayer.strokeEnd = strokeEnd 

     //override strokeEnd implicit animation 
     let animation = CABasicAnimation(keyPath: "strokeEnd") 
     animation.duration = 0.5 
     animation.fromValue = oldStrokeEnd 
     animation.toValue = strokeEnd 
     animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) 
     maskLayer.add(animation, forKey: "animateCircle") 

     currentValue = strokeEnd 
    } 

    func setUpMask(){ 

     let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width/2.0, y: frame.size.height/2.0), 
             radius: (frame.size.width - 10)/2, 
             startAngle: -CGFloat.pi/2, 
             endAngle: CGFloat.pi*1.5, 
             clockwise: true) 

     maskLayer = CAShapeLayer() 
     maskLayer.path = circlePath.cgPath 
     maskLayer.fillColor = UIColor.clear.cgColor 
     maskLayer.strokeColor = UIColor.red.cgColor 
     maskLayer.lineWidth = 8.0; 
     maskLayer.strokeEnd = currentValue 
     maskLayer.lineCap = "round" 
     imageView.layer.mask = maskLayer 
    } 

    func makeGradient(){ 
     imageView = UIImageView(image: UIImage(named: "gradientImage")) 
     imageView.frame = bounds 
     imageView.contentMode = .scaleAspectFill 
     addSubview(imageView) 
    } 

} 

順便說一句,如果你正在尋找成爲動畫更好,我建議一個偉大的書「IOS核心動畫:高級技術 書由尼克·洛克伍德」