iOS - 圆形渐变 [英] iOS - Circle shaped gradient

查看:93
本文介绍了iOS - 圆形渐变的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试绘制圆形渐变。

 让backgroundView:UIView = UIView()
let backgroundLayer:CAShapeLayer = CAShapeLayer()
let gradient:CAGradientLayer = CAGradientLayer()
...

backgroundLayer.frame = CGRectMake(0,0,backgroundDiameter,backgroundDiameter)
backgroundLayer.backgroundColor = UIColor.clearColor()。CGColor
backgroundLayer.strokeColor = backgroundStrokeColor
backgroundLayer.fillColor = backgroundFillColor


gradient.colors = [UIColor(红色:0.5,绿色:0.5,蓝色:0.9,alpha:1.0).CGColor,
UIColor(红色:0.9,绿色:0.9,蓝色:0.3,alpha:1.0).CGColor]
渐变。 locations = [0.01,0.8]
gradient.frame = backgroundLayer.frame

backgroundView.frame = CGRectMake(0,0,backgroundDiameter,backgroundDiameter)
backgroundView.backgroundColor = UIColor。 clearColor()
backgroundView.center = ringControlCenter
backgroundLayer.insertSublayer(gradient,atInd例如:1)
backgroundLayer.path = CGPathCreateWithEllipseInRect(backgroundLayer.frame,nil)

backgroundView.layer.addSublayer(backgroundLayer)
self.addSubview(backgroundView)

然而,渐变似乎不受以下因素的影响:

  backgroundLayer.path = CGPathCreateWithEllipseInRect(backgroundLayer.frame,nil)

并且仍然有其初始形状。
有没有办法用椭圆形图层掩盖渐变而不使用CGContext *指令?



谢谢,



MG

解决方案

我已翻译



也适用于动画




I am trying to draw a circular shaped gradient.

let backgroundView:UIView = UIView()
let backgroundLayer:CAShapeLayer = CAShapeLayer()
let gradient:CAGradientLayer = CAGradientLayer()
...

backgroundLayer.frame = CGRectMake(0, 0, backgroundDiameter, backgroundDiameter)
backgroundLayer.backgroundColor = UIColor.clearColor().CGColor
backgroundLayer.strokeColor = backgroundStrokeColor
backgroundLayer.fillColor = backgroundFillColor


gradient.colors = [UIColor(red: 0.5, green: 0.5, blue: 0.9, alpha: 1.0).CGColor,  
UIColor(red: 0.9, green: 0.9, blue: 0.3, alpha: 1.0).CGColor]
gradient.locations = [0.01, 0.8]
gradient.frame = backgroundLayer.frame

backgroundView.frame = CGRectMake(0, 0, backgroundDiameter, backgroundDiameter)
backgroundView.backgroundColor = UIColor.clearColor()
backgroundView.center = ringControlCenter
backgroundLayer.insertSublayer(gradient, atIndex: 1)
backgroundLayer.path = CGPathCreateWithEllipseInRect(backgroundLayer.frame, nil)

backgroundView.layer.addSublayer(backgroundLayer)
self.addSubview(backgroundView)

However the gradient does not seem to be impacted by:

backgroundLayer.path = CGPathCreateWithEllipseInRect(backgroundLayer.frame, nil)

And still have its initial shape. Is there a way to mask the gradient with an ellipse shaped layer without using CGContext* instructions?

Thank you,

MG

解决方案

I've translated Leo library's in Swift 3.x , in other words , the good WCGradientCircleLayer writed in objective-C (it work exactly as aspected)

import UIKit

class WCGraintCircleLayer: CALayer {

    override init () {
        super.init()
    }

    convenience init(bounds:CGRect,position:CGPoint,fromColor:UIColor,toColor:UIColor,linewidth:CGFloat,toValue:CGFloat) {
        self.init()
        self.bounds = bounds
        self.position = position
        let colors : [UIColor] = self.graint(fromColor: fromColor, toColor:toColor, count:4)
        for i in 0..<colors.count-1 {
            let graint = CAGradientLayer()
            graint.bounds = CGRect(origin:CGPoint.zero, size: CGSize(width:bounds.width/2,height:bounds.height/2))
            let valuePoint = self.positionArrayWith(bounds: self.bounds)[i]
            graint.position = valuePoint
            print("iesimo graint position: \(graint.position)")
            let fromColor = colors[i]
            let toColor = colors[i+1]
            let colors : [CGColor] = [fromColor.cgColor,toColor.cgColor]
            let stopOne: CGFloat = 0.0
            let stopTwo: CGFloat = 1.0
            let locations : [CGFloat] = [stopOne,stopTwo]
            graint.colors = colors
            graint.locations = locations as [NSNumber]? // with Swift 2 and Swift 3 you can cast directly a `CGFloat` value to `NSNumber` and back
            graint.startPoint = self.startPoints()[i]
            graint.endPoint = self.endPoints()[i]
            self.addSublayer(graint)
            //Set mask
            let shapelayer = CAShapeLayer()
            let rect = CGRect(origin:CGPoint.zero,size:CGSize(width:self.bounds.width - 2 * linewidth,height: self.bounds.height - 2 * linewidth))
            shapelayer.bounds = rect
            shapelayer.position = CGPoint(x:self.bounds.width/2,y: self.bounds.height/2)
            shapelayer.strokeColor = UIColor.blue.cgColor
            shapelayer.fillColor = UIColor.clear.cgColor
            shapelayer.path = UIBezierPath(roundedRect: rect, cornerRadius: rect.width/2).cgPath
            shapelayer.lineWidth = linewidth
            shapelayer.lineCap = kCALineCapRound
            shapelayer.strokeStart = 0.010
            let finalValue = (toValue*0.99)
            shapelayer.strokeEnd = finalValue//0.99;
            self.mask = shapelayer
        }
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func layerWithWithBounds(bounds:CGRect, position:CGPoint, fromColor:UIColor, toColor:UIColor, linewidth : CGFloat,toValue:CGFloat) -> WCGraintCircleLayer {
        let layer = WCGraintCircleLayer(bounds: bounds,position: position,fromColor:fromColor, toColor: toColor,linewidth: linewidth,toValue:toValue )
        return layer
    }

    func graint(fromColor:UIColor, toColor:UIColor, count:Int) -> [UIColor]{
        var fromR:CGFloat = 0.0,fromG:CGFloat = 0.0,fromB:CGFloat = 0.0,fromAlpha:CGFloat = 0.0
        fromColor.getRed(&fromR,green: &fromG,blue: &fromB,alpha: &fromAlpha)

        var toR:CGFloat = 0.0,toG:CGFloat = 0.0,toB:CGFloat = 0.0,toAlpha:CGFloat = 0.0
        toColor.getRed(&toR,green: &toG,blue: &toB,alpha: &toAlpha)

        var result : [UIColor]! = [UIColor]()

        for i in 0...count {
            let oneR:CGFloat = fromR + (toR - fromR)/CGFloat(count) * CGFloat(i)
            let oneG : CGFloat = fromG + (toG - fromG)/CGFloat(count) * CGFloat(i)
            let oneB : CGFloat = fromB + (toB - fromB)/CGFloat(count) * CGFloat(i)
            let oneAlpha : CGFloat = fromAlpha + (toAlpha - fromAlpha)/CGFloat(count) * CGFloat(i)
            let oneColor = UIColor.init(red: oneR, green: oneG, blue: oneB, alpha: oneAlpha)
            result.append(oneColor)
            print(oneColor)

        }
        return result
    }

    func positionArrayWith(bounds:CGRect) -> [CGPoint]{
        let first = CGPoint(x:(bounds.width/4)*3,y: (bounds.height/4)*1)
        let second = CGPoint(x:(bounds.width/4)*3,y: (bounds.height/4)*3)
        let third = CGPoint(x:(bounds.width/4)*1,y: (bounds.height/4)*3)
        let fourth = CGPoint(x:(bounds.width/4)*1,y: (bounds.height/4)*1)
        print([first,second,third,fourth])
        return [first,second,third,fourth]
    }

    func startPoints() -> [CGPoint] {
        return [CGPoint.zero,CGPoint(x:1,y:0),CGPoint(x:1,y:1),CGPoint(x:0,y:1)]
    }

    func endPoints() -> [CGPoint] {
        return [CGPoint(x:1,y:1),CGPoint(x:0,y:1),CGPoint.zero,CGPoint(x:1,y:0)]
    }

    func midColorWithFromColor(fromColor:UIColor, toColor:UIColor, progress:CGFloat) -> UIColor {
        var fromR:CGFloat = 0.0,fromG:CGFloat = 0.0,fromB:CGFloat = 0.0,fromAlpha:CGFloat = 0.0
        fromColor.getRed(&fromR,green: &fromG,blue: &fromB,alpha: &fromAlpha)

        var toR:CGFloat = 0.0,toG:CGFloat = 0.0,toB:CGFloat = 0.0,toAlpha:CGFloat = 0.0
        toColor.getRed(&toR,green: &toG,blue: &toB,alpha: &toAlpha)

        let oneR = fromR + (toR - fromR) * progress
        let oneG = fromG + (toG - fromG) * progress
        let oneB = fromB + (toB - fromB) * progress
        let oneAlpha = fromAlpha + (toAlpha - fromAlpha) * progress
        let oneColor = UIColor.init(red: oneR, green: oneG, blue: oneB, alpha: oneAlpha)
        return oneColor
    }

    // This is what you call if you want to draw a full circle.
    func animateCircle(duration: TimeInterval) {
        animateCircleTo(duration: duration, fromValue: 0.010, toValue: 0.99)
    }

    // This is what you call to draw a partial circle.
    func animateCircleTo(duration: TimeInterval, fromValue: CGFloat, toValue: CGFloat){
        // We want to animate the strokeEnd property of the circleLayer
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.isRemovedOnCompletion = true
        // Set the animation duration appropriately
        animation.duration = duration

        // Animate from 0.010 (no circle) to 0.99 (full circle)
        animation.fromValue = 0.010
        animation.toValue = toValue

        // Do an easeout. Don't know how to do a spring instead
        //animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)

        // Set the circleLayer's strokeEnd property to 0.99 now so that it's the
        // right value when the animation ends.
        let circleMask = self.mask as! CAShapeLayer
        circleMask.strokeEnd = toValue

        // Do the actual animation
        circleMask.removeAllAnimations()
        circleMask.add(animation, forKey: "animateCircle")
    }
}

And this is a little example, how to use in Swift 3.x:

let gradientRingLayer = WCGraintCircleLayer(bounds: CGRect(origin: CGPoint.zero,size:CGSize(width: 150, height: 150)), position:CGPoint(x: 200, y: 300),fromColor:UIColor.blue, toColor:UIColor.white, linewidth:4.0, toValue:0)
self.view.layer.addSublayer(gradientRingLayer)
let duration = 3.0
gradientRingLayer.animateCircleTo(duration: duration, fromValue: 0, toValue: 0.99)

This is the code in Swift 2.x:

import UIKit

class WCGraintCircleLayer: CALayer {

override init () {
    super.init()
}

convenience init(bounds:CGRect,position:CGPoint,fromColor:UIColor,toColor:UIColor,linewidth:CGFloat,toValue:CGFloat) {
    self.init()
    self.bounds = bounds
    self.position = position
    let colors : [UIColor] = self.graintFromColor(fromColor, toColor:toColor, count:4)
    for i in 0..<colors.count-1 {
        let graint = CAGradientLayer()
        graint.bounds = CGRectMake(0,0,CGRectGetWidth(bounds)/2,CGRectGetHeight(bounds)/2)
        let valuePoint = self.positionArrayWithMainBounds(self.bounds)[i]
        graint.position = valuePoint
        print("iesimo graint position: \(graint.position)")
        let fromColor = colors[i]
        let toColor = colors[i+1]
        let colors : [CGColorRef] = [fromColor.CGColor,toColor.CGColor]
        let stopOne: CGFloat = 0.0
        let stopTwo: CGFloat = 1.0
        let locations : [CGFloat] = [stopOne,stopTwo]
        graint.colors = colors
        graint.locations = locations
        graint.startPoint = self.startPoints()[i]
        graint.endPoint = self.endPoints()[i]
        self.addSublayer(graint)
        //Set mask
        let shapelayer = CAShapeLayer()
        let rect = CGRectMake(0,0,CGRectGetWidth(self.bounds) - 2 * linewidth, CGRectGetHeight(self.bounds) - 2 * linewidth)
        shapelayer.bounds = rect
        shapelayer.position = CGPointMake(CGRectGetWidth(self.bounds)/2, CGRectGetHeight(self.bounds)/2)
        shapelayer.strokeColor = UIColor.blueColor().CGColor
        shapelayer.fillColor = UIColor.clearColor().CGColor
        shapelayer.path = UIBezierPath(roundedRect: rect, cornerRadius: CGRectGetWidth(rect)/2).CGPath
        shapelayer.lineWidth = linewidth
        shapelayer.lineCap = kCALineCapRound
        shapelayer.strokeStart = 0.010
        let finalValue = (toValue*0.99)
        shapelayer.strokeEnd = finalValue//0.99;
        self.mask = shapelayer
    }
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

func layerWithWithBounds(bounds:CGRect, position:CGPoint, fromColor:UIColor, toColor:UIColor, linewidth : CGFloat,toValue:CGFloat) -> WCGraintCircleLayer {
    let layer = WCGraintCircleLayer(bounds: bounds,position: position,fromColor:fromColor, toColor: toColor,linewidth: linewidth,toValue:toValue )
    return layer
}

func graintFromColor(fromColor:UIColor, toColor:UIColor, count:Int) -> [UIColor]{
    var fromR:CGFloat = 0.0,fromG:CGFloat = 0.0,fromB:CGFloat = 0.0,fromAlpha:CGFloat = 0.0
    fromColor.getRed(&fromR,green: &fromG,blue: &fromB,alpha: &fromAlpha)

    var toR:CGFloat = 0.0,toG:CGFloat = 0.0,toB:CGFloat = 0.0,toAlpha:CGFloat = 0.0
    toColor.getRed(&toR,green: &toG,blue: &toB,alpha: &toAlpha)

    var result : [UIColor]! = [UIColor]()

    for i in 0...count {
        let oneR:CGFloat = fromR + (toR - fromR)/CGFloat(count) * CGFloat(i)
        let oneG : CGFloat = fromG + (toG - fromG)/CGFloat(count) * CGFloat(i)
        let oneB : CGFloat = fromB + (toB - fromB)/CGFloat(count) * CGFloat(i)
        let oneAlpha : CGFloat = fromAlpha + (toAlpha - fromAlpha)/CGFloat(count) * CGFloat(i)
        let oneColor = UIColor.init(red: oneR, green: oneG, blue: oneB, alpha: oneAlpha)
        result.append(oneColor)
        print(oneColor)

    }
    return result
}

func positionArrayWithMainBounds(bounds:CGRect) -> [CGPoint]{
    let first = CGPointMake((CGRectGetWidth(bounds)/4)*3, (CGRectGetHeight(bounds)/4)*1)
    let second = CGPointMake((CGRectGetWidth(bounds)/4)*3, (CGRectGetHeight(bounds)/4)*3)
    let third = CGPointMake((CGRectGetWidth(bounds)/4)*1, (CGRectGetHeight(bounds)/4)*3)
    let fourth = CGPointMake((CGRectGetWidth(bounds)/4)*1, (CGRectGetHeight(bounds)/4)*1)
    print([first,second,third,fourth])
    return [first,second,third,fourth]
}

func startPoints() -> [CGPoint] {
    return [CGPointMake(0,0),CGPointMake(1,0),CGPointMake(1,1),CGPointMake(0,1)]
}

func endPoints() -> [CGPoint] {
    return [CGPointMake(1,1),CGPointMake(0,1),CGPointMake(0,0),CGPointMake(1,0)]
}

func midColorWithFromColor(fromColor:UIColor, toColor:UIColor, progress:CGFloat) -> UIColor {
    var fromR:CGFloat = 0.0,fromG:CGFloat = 0.0,fromB:CGFloat = 0.0,fromAlpha:CGFloat = 0.0
    fromColor.getRed(&fromR,green: &fromG,blue: &fromB,alpha: &fromAlpha)

    var toR:CGFloat = 0.0,toG:CGFloat = 0.0,toB:CGFloat = 0.0,toAlpha:CGFloat = 0.0
    toColor.getRed(&toR,green: &toG,blue: &toB,alpha: &toAlpha)

    let oneR = fromR + (toR - fromR) * progress
    let oneG = fromG + (toG - fromG) * progress
    let oneB = fromB + (toB - fromB) * progress
    let oneAlpha = fromAlpha + (toAlpha - fromAlpha) * progress
    let oneColor = UIColor.init(red: oneR, green: oneG, blue: oneB, alpha: oneAlpha)
    return oneColor
}

// This is what you call if you want to draw a full circle.
func animateCircle(duration: NSTimeInterval) {
    animateCircleTo(duration, fromValue: 0.010, toValue: 0.99)
}

// This is what you call to draw a partial circle.
func animateCircleTo(duration: NSTimeInterval, fromValue: CGFloat, toValue: CGFloat){
    // We want to animate the strokeEnd property of the circleLayer
    let animation = CABasicAnimation(keyPath: "strokeEnd")
    animation.removedOnCompletion = true
    // Set the animation duration appropriately
    animation.duration = duration

    // Animate from 0.010 (no circle) to 0.99 (full circle)
    animation.fromValue = 0.010
    animation.toValue = toValue

    // Do an easeout. Don't know how to do a spring instead
    //animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)

    // Set the circleLayer's strokeEnd property to 0.99 now so that it's the
    // right value when the animation ends.
    let circleMask = self.mask as! CAShapeLayer
    circleMask.strokeEnd = toValue

    // Do the actual animation
    circleMask.removeAllAnimations()
    circleMask.addAnimation(animation, forKey: "animateCircle")
}

}

And this is a little example, how to use in Swift 2.x:

let gradientRingLayer = WCGraintCircleLayer(bounds: CGRectMake(0, 0, 150, 150), position:CGPointMake(200,300) ,fromColor:UIColor.blueColor(), toColor:UIColor.whiteColor(),linewidth:4.0, toValue:0)
self.view.layer.addSublayer(gradientRingLayer)
let duration = 3.0
gradientRingLayer.animateCircleTo(duration, fromValue: 0, toValue: 0.99)

This is rapid copy/paste remote pastebin code

Available also with animation:

这篇关于iOS - 圆形渐变的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆