0

I need to draw a shape like that one: enter image description here And animate the color path when you are scrolling. I have been for 2 hours trying several things but don't reach the final way to overcome that. I have tried creating a custom UIView, create a CShapeLayer and then a UIBezierPath to that layer, and finally adding the subview to a view in the viewdidload, but this does not work. What else can I do to approach that? I will start with the shapes that are the most complex things, as the label will be just aligned to the shape.

----FIRST PART SOLVED, DRAWRECT NOW APPEARS, BUT HOW DO I ANIMATE?----

That's my updated code in my drawRect method.

class CustomOval: UIView {

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

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

override func drawRect(rect: CGRect) {

    //SHAPE 2
    let rectanglePath2 = UIBezierPath(roundedRect: CGRectMake(135, 177, 20, 70), cornerRadius: 0)

    let shapeLayer2 = CAShapeLayer()
    shapeLayer2.path = rectanglePath2.CGPath
    shapeLayer2.bounds = CGRect(x: 135, y: 177, width: 20, height: 70)
    shapeLayer2.lineWidth = 5.0

    let label2 = UILabel()
    label2.frame = CGRectMake(150 + rectanglePath2.bounds.width, rectanglePath2.bounds.height + 120, 100, 50)
    label2.text = "Label 2"

    self.addSubview(label2)

    //// Rectangle Drawing
    UIColor.blueColor().setFill()
    rectanglePath2.fill()

    //SHAPE 3
    let rectanglePath3 = UIBezierPath(roundedRect: CGRectMake(135, 237, 20, 70), cornerRadius: 0)

    let shapeLayer3 = CAShapeLayer()
    shapeLayer3.path = rectanglePath3.CGPath
    shapeLayer3.bounds = CGRect(x: 135, y: rectanglePath2.bounds.maxY, width: 20, height: 70)
    shapeLayer3.lineWidth = 5.0

    //// Rectangle Drawing
    UIColor.redColor().setFill()
    rectanglePath3.fill()

    let label3 = UILabel()
    label3.frame = CGRectMake(rectanglePath3.bounds.width + 150, rectanglePath3.bounds.height + 190, 100, 50)
    label3.text = "Label 3"

    self.addSubview(label3)

    //SHAPE 1
    let rectanglePath = UIBezierPath(roundedRect: CGRectMake(104, 24, 80, 155), cornerRadius: 40)

    let shapeLayer = CAShapeLayer()
    shapeLayer.path = rectanglePath.CGPath
    shapeLayer.bounds = CGRect(x: 104, y: 24, width: 80, height: 155)
    shapeLayer.lineWidth = 5.0

    //// Rectangle Drawing
    UIColor.grayColor().setFill()
    rectanglePath.fill()

}

}

-UPDATE ANIMATION-

var shapeLayer = CAShapeLayer()

override init(frame: CGRect) {
    shapeLayer = CAShapeLayer()
    super.init(frame: frame)
    animateShape1()

}

required init(coder aDecoder: NSCoder) {
    shapeLayer = CAShapeLayer()
    super.init(coder: aDecoder)!
    animateShape1()
}

func animateShape1(){

    let animation = CABasicAnimation(keyPath: "fillColor")

    animation.fromValue = UIColor.whiteColor().CGColor
    animation.toValue = UIColor.redColor().CGColor
    animation.duration = 5 //2 sec

    shapeLayer.fillColor = UIColor.redColor().CGColor //color end value
    layer.addAnimation(animation, forKey: "somekey")


}

My main questions now are:

  • How do I set an image inside the CAShapeLayer? I have tried with: FIXED Calling it from the init

func addImage(){

    let imageSubLayer = CALayer()
    let image = UIImage(named: "paint.png")
    imageSubLayer.contents = image?.CGImage
    imageSubLayer.bounds = (frame: CGRect(x: shapeLayer.bounds.width/2, y:   shapeLayer.bounds.height/2, width: 50, height: 50))
    shapeLayer.addSublayer(imageSubLayer)

}

I have also tried, and the one that has worked is: But i dont want a tiled image. I have also tried without tiled and this does not work

CGContextSaveGState(context)
rectanglePath.addClip()
CGContextScaleCTM(context, 0, (image?.size.height)!)
CGContextDrawTiledImage(context, CGRectMake(20, 20, 50, 50), image!.CGImage)
CGContextRestoreGState(context)
  • I also need to animate the process, and I have tried with the reply of @s0urce but unfortunately, it is not drawing. Do I need to do something else?
Sergio
  • 82
  • 7

1 Answers1

1
  1. You should never add sublayers inside drawRect method. It would be much better to do this inside init or initWithFrame: method of a view or at least inside viewDidLoad method of view controller.

  2. You need to set bounds of CAShapeLayer otherwise it would be CGRectZero. Don't forget that points of UIBezierPath should be in the coordinate system of CAShapeLayer.

Color animation sample code:

let animation = CABasicAnimation(keyPath: "fillColor")

animation.fromValue = UIColor.whiteColor().CGColor
animation.toValue = UIColor.redColor().CGColor
animation.duration = 2 //2 sec

layer.fillColor = UIColor.redColor().CGColor //color end value
layer.addAnimation(animation, forKey: "somekey")
s0urcer
  • 312
  • 2
  • 7
  • Thank you. Following your answer I have updated my code to that one (see my question update). How can I animate the fill color on the cashapelayer? Is there any way I can group all of them to animate once? Thank you for your help – Sergio Apr 07 '16 at 22:24
  • @Sergio do you still setup layers in `drawRect:` ? This is not right as I told before. Please describe in more details what kind of animation do you need? – s0urcer Apr 07 '16 at 22:58
  • My code at the moment looks like the photo I have just uploaded on the question. This is the best result I have get with the code attached. I have attached the labels on the drawrect because this was the only way it was attached. Where should I attach them, on the viewdidload? But how do I get then the bounds frames? The animation I'd like to get would be some drawing animation or animation with the color, in order to put from 0.0 to the final point, and animate like a graphview filling the color of the drawrect elements. Is there any way I can mix all cashapelayer in one? – Sergio Apr 07 '16 at 23:02
  • @Sergio it would be easier to do some method like `initCustomSublayers`, move all the layer creation code there and call it from `init`, `initWithFrame` and (in case you load UIView from xib) `initWithCoder:`. Looks like sublayers' bounds are constant so you won't need view bounds. – s0urcer Apr 07 '16 at 23:09
  • @Sergio updated my answer with color animation sample – s0urcer Apr 07 '16 at 23:22
  • updated with code. Please take a look if you can. Thank you – Sergio Apr 09 '16 at 14:24
  • @Sergio Method `addImage()` looks ok to me. You can try adding image to `CAShapeLayer` parent layer rather than to itself for testing purposes. Also try setting image layer `frame` instead of `bounds`. It's better to use this method of adding image if you want to add some sort of animation. – s0urcer Apr 10 '16 at 10:12
  • Thank you. Finally the addImage is working for me :). My main problem now is the animation, which I'm not able to show, with that I'll finish my VC. I have added your code inside the UIView class and then call it from the viewdidload but nothing works. I have also assigned the cashapelayer to the correct one. – Sergio Apr 10 '16 at 10:15
  • @Sergio replace `layer.addAnimation(animation, forKey: "somekey")` with `shapeLayer.addAnimation(animation, forKey: "somekey")` – s0urcer Apr 10 '16 at 10:37
  • done. I have tried with that too http://stackoverflow.com/questions/36523886/animate-some-cashapelayer-and-link-them but nothing works (this is my updated question). I call it then on the viewDidLoad. Is someting wrong with my shape creation and because of that it does not create my animation? Im annoyed – Sergio Apr 10 '16 at 10:41
  • @Sergio You are still creating shape layers inside `darwRect:`, this may cause unpredictable behavior as `darwRect:` may be called any time. – s0urcer Apr 10 '16 at 10:58
  • Do you mean the label? or the shapeLayer? If you mean the label, where am I supposed to create it? I would really like to leave the drawrect as you tell me, but I don't know what to change and where to – Sergio Apr 10 '16 at 11:05
  • @Sergio you shuoldn't create any layers or views inside `drawRect:`. Just move creation inside `init:` or `viewDidLoad:` as I told you before. – s0urcer Apr 10 '16 at 11:08
  • I have movied all the code from drawRect to a method that I call after on viewdidload. Now only appears the labels, not the shapes. Did you mean only leave on drawRect the shapes? And then the animation method will work? – Sergio Apr 10 '16 at 11:18
  • @Sergio you just need to find out why shapes are not create when you move creation outside of `drawRect:` . Probably there is some sort of bug. After that adding animations should be more straightforward. – s0urcer Apr 10 '16 at 11:22
  • Can I not set the shapeFrames from the drawRect as I have already posted on my drawrect code in the question? Is that wrong? If I move that to another method, it does not show as it is not added to the screen. What else can I do? This is the fault :/ – Sergio Apr 10 '16 at 16:09