{"id":1681,"date":"2015-03-02T15:04:30","date_gmt":"2015-03-02T21:04:30","guid":{"rendered":"http:\/\/jamesonquave.com\/blog\/?p=1681"},"modified":"2015-07-28T09:25:26","modified_gmt":"2015-07-28T15:25:26","slug":"fun-with-cashapelayer","status":"publish","type":"post","link":"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/","title":{"rendered":"Fun with CAShapeLayer"},"content":{"rendered":"<p><em>CAShapeLayer<\/em> is a specialized subclass of <em>CALayer<\/em> that draws itself using the shape you define via the <em>path<\/em> property. <em>path<\/em> is an instance of <em>CGPath<\/em>. We could leverage the convenient <em>UIBezierPath<\/em> APIs to create a path, and then retrieve the <em>CGPath<\/em> from it. Besides all the animatable properties inherited from <em>CALayer<\/em>, there are other animatable properties that let you control attributes such as fill color, stroke color, line thickness, etc. In this tutorial, I will illustrate some visual effects using these properties.<\/p>\n<div style=\"border: 1px solid #eee; margin: 5px;\">\n<center><br \/>\n<i>Looking for something more in-depth? Try my book &#038; video courses<\/i><br \/>\n<a href=\"http:\/\/jamesonquave.com\/swiftebook\/\" alt=\"Get The Swift Book\"><img decoding=\"async\" src=\"http:\/\/jamesonquave.com\/swiftebook\/img\/sm-cover.png\" height=\"150\" alt=\"Get The Swift Book\" \/><br \/>\nLearn About My Book &#038; Video Packages &raquo;<\/a><br \/>\n<\/center>\n<\/div>\n<h1>Let\u2019s Get Started!<\/h1>\n<p>First, you need to download the starter project from <a href=\"https:\/\/github.com\/GuanshanLiu\/CAShapeLayerDemo\/archive\/starter.zip\">here<\/a>.<\/p>\n<h2>Rounded Corners<\/h2>\n<p>There are different ways to draw rounded rectangles.<\/p>\n<p>Go to <em>RoundedCornersViewController<\/em>\u00a0class, add the following codes at the end of <em>viewDidAppear<\/em> method.<\/p>\n<pre class=\"brush: js;\">\r\n\/\/ 1\r\nrectShape1.backgroundColor = UIColor.redColor().CGColor\r\nrectShape1.cornerRadius = 20\r\n\/\/ 2\r\nrectShape2.fillColor = UIColor.greenColor().CGColor\r\nrectShape2.path = UIBezierPath(roundedRect: rectShape2.bounds, cornerRadius: 20).CGPath\r\n\/\/ 3\r\nrectShape3.fillColor = UIColor.blueColor().CGColor\r\nrectShape3.path = UIBezierPath(roundedRect: rectShape3.bounds, byRoundingCorners: .BottomLeft | .TopRight, cornerRadii: CGSize(width: 20, height: 20)).CGPath\r\n<\/pre>\n<p>1. The first way to draw a rounded rectangle is to change a layer\u2019s <em>cornerRadius<\/em>\u00a0property. This applies to all CALayerS.<br \/>\n2. We could also use <em>path<\/em>\u00a0to draw a rounded rectangle. Assign a rounded rectangle path via this convenient method on <em>UIBezierPath<\/em>. By doing this, we have to use <em>fillColor<\/em>\u00a0instead of <em>backgroundColor<\/em>. Because <em>backgroundColor<\/em>\u00a0is color of the layer\u2019s background, while <em>fillColor<\/em>\u00a0is the color used to fill the shape\u2019s path.<br \/>\n3. Using <em>path<\/em>, we are not limited to round all corners. We could specify which corner we want to round. In this example, I only change bottom left and top right corners.<\/p>\n<p>Run and select Rounded Corners cell.<br \/>\n<img decoding=\"async\" src=\"https:\/\/github.com\/GuanshanLiu\/CAShapeLayerDemo\/blob\/master\/Screenshots\/pic1.png?raw=true\" width=\"250\" \/><\/p>\n<h2>Path Animation<\/h2>\n<p><em>path<\/em>\u00a0is also an animatable property. We could achieve the basic Material-Design-feel effect by animating it.<\/p>\n<p>Go to <em>PathViewController<\/em>\u00a0class, add the following codes at the end of <em>viewDidAppear<\/em>\u00a0method.<\/p>\n<pre class=\"brush: js;\">\r\n\/\/ fill with yellow\r\nrectShape.fillColor = UIColor.yellowColor().CGColor\r\n\r\n\/\/ 1\r\n\/\/ begin with a circle with a 50 points radius\r\nlet startShape = UIBezierPath(roundedRect: bounds, cornerRadius: 50).CGPath\r\n\/\/ animation end with a large circle with 500 points radius\r\nlet endShape = UIBezierPath(roundedRect: CGRect(x: -450, y: -450, width: 1000, height: 1000), cornerRadius: 500).CGPath\r\n\r\n\/\/ set initial shape\r\nrectShape.path = startShape\r\n\r\n\/\/ 2\r\n\/\/ animate the `path`\r\nlet animation = CABasicAnimation(keyPath: \"path\")\r\nanimation.toValue = endShape\r\nanimation.duration = 1 \/\/ duration is 1 sec\r\n\/\/ 3\r\nanimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) \/\/ animation curve is Ease Out\r\nanimation.fillMode = kCAFillModeBoth \/\/ keep to value after finishing\r\nanimation.removedOnCompletion = false \/\/ don't remove after finishing\r\n\/\/ 4\r\nrectShape.addAnimation(animation, forKey: animation.keyPath)\r\n<\/pre>\n<p>1. Calculate begin and end shapes for the animation. Then assign the <em>startShape<\/em>\u00a0to <em>path<\/em>.<br \/>\n2. Use <em>CABasicAnimation<\/em> to animate <em>path<\/em>. Destination value for <em>path<\/em>\u00a0is the end shape we defined before. Set it to <em>toValue<\/em>. Then set the animation duration to 1 second.<br \/>\n3. Set the animation curve to ease out, making it look more natural. With <em>removedOnCompletion<\/em>\u00a0set to false, <em>fillMode<\/em>\u00a0to kCAFillModeBoth, when the animation finishes, <em>rectShape<\/em>\u00a0will remain the end shape.<br \/>\n4. Add the animation to the layer.<\/p>\n<p>Run and select path Animation cell. See the animation:<\/p>\n<p><a href=\"https:\/\/github.com\/GuanshanLiu\/CAShapeLayerDemo\/blob\/master\/Screenshots\/pic2.mov\">View Movie<\/p>\n<p><video controls=\"controls\" name=\"CAShapeLayerDemo Animated Path\" src=\"https:\/\/github.com\/GuanshanLiu\/CAShapeLayerDemo\/blob\/master\/Screenshots\/pic2.mov?raw=true\"  width=\"250\"><\/video><\/p>\n<p><\/a><\/p>\n<h2>Line Width Animation<\/h2>\n<p><em>lineWidth<\/em>\u00a0defines the stroke line width of the shape\u2019s path, and it\u2019s also animatable. There are some cool effects we could make via <em>lineWidth<\/em>.<\/p>\n<p>Go to <em>LineWidthViewController<\/em>\u00a0class, add the following codes at the end of <em>viewDidAppear<\/em>\u00a0method.<\/p>\n<pre class=\"brush: js;\">\r\n\/\/ setup\r\nlet rect = CGRect(x: 0, y: 0, width: view.bounds.width, height: 1)\r\nrectShape.bounds = rect\r\nrectShape.position = view.center\r\nrectShape.path = UIBezierPath(rect:rect).CGPath\r\n\r\n\/\/ 1\r\nrectShape.lineWidth = 10\r\nrectShape.strokeColor = UIColor.blueColor().CGColor\r\n\r\n\/\/ animate\r\nlet animation = CABasicAnimation(keyPath: \"lineWidth\")\r\n\/\/ 2\r\nanimation.toValue = 1000\r\nanimation.duration = 1 \/\/ duration is 1 sec\r\nanimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) \/\/ animation curve is Ease Out\r\nanimation.fillMode = kCAFillModeBoth \/\/ keep to value after finishing\r\nanimation.removedOnCompletion = false \/\/ don't remove after finishing\r\nrectShape.addAnimation(animation, forKey: animation.keyPath)\r\n<\/pre>\n<p>1. Give an initial line width of 10 points. To set the line color, we use <em>strokeColor<\/em>.<br \/>\n2. Similar to path animation, animate line width to 1000.<\/p>\n<p>Run and select lineWidth Animation cell. See the animation:<\/p>\n<p><video controls=\"controls\" width=\"250\" name=\"Core Animation Tutorial\" src=\"https:\/\/github.com\/GuanshanLiu\/CAShapeLayerDemo\/blob\/master\/Screenshots\/pic3.mov?raw=true\"><\/video><\/p>\n<p><\/a><\/p>\n<h2>Stroke Animation<\/h2>\n<p><em>strokeStart<\/em>\u00a0and <em>strokeEnd<\/em>\u00a0defines the relative location at which to begin stroking the path, ranging from 0 to 1. Many cool activity indicator can be made by using them. Here is a simple example that show you how to animate these properties.<\/p>\n<p>Go to <em>StrokeViewController<\/em>\u00a0class, add the following codes at the end of <em>viewDidAppear<\/em>\u00a0method.<\/p>\n<pre class=\"brush: js;\">\r\n\/\/ 1\r\nrectShape.path = UIBezierPath(ovalInRect: rectShape.bounds).CGPath\r\n\r\nrectShape.lineWidth = 4.0\r\nrectShape.strokeColor = UIColor.lightGrayColor().CGColor\r\nrectShape.fillColor = UIColor.clearColor().CGColor\r\n\r\n\/\/ 2\r\nrectShape.strokeStart = 0\r\nrectShape.strokeEnd = 0.5\r\n\r\n\/\/ 3\r\nlet start = CABasicAnimation(keyPath: \"strokeStart\")\r\nstart.toValue = 0.7\r\nlet end = CABasicAnimation(keyPath: \"strokeEnd\")\r\nend.toValue = 1\r\n\r\n\/\/ 4\r\nlet group = CAAnimationGroup()\r\ngroup.animations = [start, end]\r\ngroup.duration = 1.5\r\ngroup.autoreverses = true\r\ngroup.repeatCount = HUGE \/\/ repeat forver\r\nrectShape.addAnimation(group, forKey: nil)\r\n<\/pre>\n<p>1. Here is another way to draw a circle using another <em>UIBezierPath<\/em>\u2019s convenient initializer.<br \/>\n2. Set the initial values for <em>strokeStart<\/em>\u00a0and <em>strokeEnd<\/em>.<br \/>\n3. Create animations like before.<br \/>\n4. Group two animation together. Because we want to both animations to happen simultaneously. The duration is 1.5 seconds. It will auto reverse the animations upon finishing. And it will repeat forever.<\/p>\n<p>Run and select Stroke Animation cell. See the animation:<\/p>\n<p><video controls=\"controls\" width=\"250\" name=\"Core Animation Tutorial\" src=\"https:\/\/github.com\/GuanshanLiu\/CAShapeLayerDemo\/blob\/master\/Screenshots\/pic4.mov?raw=true\"><\/video><\/p>\n<p><\/a><\/p>\n<h1>Conclusions<\/h1>\n<p>You could find the complete project <a href=\"https:\/\/github.com\/GuanshanLiu\/CAShapeLayerDemo\">on Github<\/a>. If you have further questions, you can leave a comment or ask me <a href=\"https:\/\/twitter.com\/guanshanliu\">on Twitter<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>CAShapeLayer is a specialized subclass of CALayer that draws itself using the shape you define via the path property. path is an instance of CGPath. We could leverage the convenient UIBezierPath APIs to create a path, and then retrieve the CGPath from it. Besides all the animatable properties inherited from CALayer, there are other animatable&#8230;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_links_to":"","_links_to_target":""},"categories":[32,1],"tags":[42,33],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.13 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Fun with CAShapeLayer - Jameson Quave<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Fun with CAShapeLayer - Jameson Quave\" \/>\n<meta property=\"og:description\" content=\"CAShapeLayer is a specialized subclass of CALayer that draws itself using the shape you define via the path property. path is an instance of CGPath. We could leverage the convenient UIBezierPath APIs to create a path, and then retrieve the CGPath from it. Besides all the animatable properties inherited from CALayer, there are other animatable...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/\" \/>\n<meta property=\"og:site_name\" content=\"Jameson Quave\" \/>\n<meta property=\"article:published_time\" content=\"2015-03-02T21:04:30+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2015-07-28T15:25:26+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/jamesonquave.com\/swiftebook\/img\/sm-cover.png\" \/>\n<meta name=\"author\" content=\"Guanshan Liu\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Guanshan Liu\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/\",\"url\":\"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/\",\"name\":\"Fun with CAShapeLayer - Jameson Quave\",\"isPartOf\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/#website\"},\"datePublished\":\"2015-03-02T21:04:30+00:00\",\"dateModified\":\"2015-07-28T15:25:26+00:00\",\"author\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/d0ef4e53cd95e21e389aefe6277f0811\"},\"breadcrumb\":{\"@id\":\"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/jamesonquave.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Fun with CAShapeLayer\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/#website\",\"url\":\"https:\/\/jamesonquave.com\/blog\/\",\"name\":\"Jameson Quave\",\"description\":\"Using computer technology to educate, and improve lives.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/jamesonquave.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/d0ef4e53cd95e21e389aefe6277f0811\",\"name\":\"Guanshan Liu\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/8f00053e85112a67cee95968f2f36ab2?s=96&d=retro&r=pg\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/8f00053e85112a67cee95968f2f36ab2?s=96&d=retro&r=pg\",\"caption\":\"Guanshan Liu\"},\"description\":\"Guanshan is an iOS Developer currently working as an iOS Engineer at Alibaba Inc. Before working at Alibaba, Guanshan worked with 2K Games on Civilization Revolution 1 and 2 for iOS. He has a Masters in Software Engineering from the University of York, UK as well as a Bachelors of Engineering in Information Security from Nanjing University of Aeronautics and Astronautics. Find him on twitter, @guanshanliu.\",\"sameAs\":[\"https:\/\/guanshanliu.github.io\"],\"url\":\"https:\/\/jamesonquave.com\/blog\/author\/guanshanliu\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Fun with CAShapeLayer - Jameson Quave","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/","og_locale":"en_US","og_type":"article","og_title":"Fun with CAShapeLayer - Jameson Quave","og_description":"CAShapeLayer is a specialized subclass of CALayer that draws itself using the shape you define via the path property. path is an instance of CGPath. We could leverage the convenient UIBezierPath APIs to create a path, and then retrieve the CGPath from it. Besides all the animatable properties inherited from CALayer, there are other animatable...","og_url":"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/","og_site_name":"Jameson Quave","article_published_time":"2015-03-02T21:04:30+00:00","article_modified_time":"2015-07-28T15:25:26+00:00","og_image":[{"url":"http:\/\/jamesonquave.com\/swiftebook\/img\/sm-cover.png"}],"author":"Guanshan Liu","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Guanshan Liu","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/","url":"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/","name":"Fun with CAShapeLayer - Jameson Quave","isPartOf":{"@id":"https:\/\/jamesonquave.com\/blog\/#website"},"datePublished":"2015-03-02T21:04:30+00:00","dateModified":"2015-07-28T15:25:26+00:00","author":{"@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/d0ef4e53cd95e21e389aefe6277f0811"},"breadcrumb":{"@id":"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/jamesonquave.com\/blog\/fun-with-cashapelayer\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/jamesonquave.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Fun with CAShapeLayer"}]},{"@type":"WebSite","@id":"https:\/\/jamesonquave.com\/blog\/#website","url":"https:\/\/jamesonquave.com\/blog\/","name":"Jameson Quave","description":"Using computer technology to educate, and improve lives.","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/jamesonquave.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/d0ef4e53cd95e21e389aefe6277f0811","name":"Guanshan Liu","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/jamesonquave.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/8f00053e85112a67cee95968f2f36ab2?s=96&d=retro&r=pg","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/8f00053e85112a67cee95968f2f36ab2?s=96&d=retro&r=pg","caption":"Guanshan Liu"},"description":"Guanshan is an iOS Developer currently working as an iOS Engineer at Alibaba Inc. Before working at Alibaba, Guanshan worked with 2K Games on Civilization Revolution 1 and 2 for iOS. He has a Masters in Software Engineering from the University of York, UK as well as a Bachelors of Engineering in Information Security from Nanjing University of Aeronautics and Astronautics. Find him on twitter, @guanshanliu.","sameAs":["https:\/\/guanshanliu.github.io"],"url":"https:\/\/jamesonquave.com\/blog\/author\/guanshanliu\/"}]}},"_links":{"self":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/1681"}],"collection":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/comments?post=1681"}],"version-history":[{"count":7,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/1681\/revisions"}],"predecessor-version":[{"id":1905,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/posts\/1681\/revisions\/1905"}],"wp:attachment":[{"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/media?parent=1681"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/categories?post=1681"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jamesonquave.com\/blog\/wp-json\/wp\/v2\/tags?post=1681"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}