Shadows in NativeScript
Tags: nativescript, ios, android, tutorial
Published: 2019-08-09 07:26:20

---
title: Shadows in NativeScript
published: true
description: On implementing shadows in NativeScript applications.
tags: NativeScript, iOS, Android, Tutorial
---

[shadow-ios]: https://thepracticaldev.s3.amazonaws.com/i/clcrapx4xs3ojzaazvq0.png
[shadow-ios--lost]: https://thepracticaldev.s3.amazonaws.com/i/ycruyu710dkjzaim3tqr.png
[shadow-android]: https://thepracticaldev.s3.amazonaws.com/i/5fbm4umfxn84arkfecco.png


Before we begin it's worth remembering, _there's many ways to skin a cat_.

## :smile_cat: (:hocho:,:scissors:,:dog:) :scream_cat:

But for this post we're going to look at implementing a __shadow__ in NativeScript using a single view; the `label` view shown below.

```xml
<!--
  main-page.xml

  This is the `xml` layout we'll be referring to throughout.
-->
<Page loaded="onLoaded" xmlns="http://schemas.nativescript.org/tns.xsd">
    <Label id="label" text="Shadow Games"/>
</Page>
```

---

# iOS Shadows

In the `xml` above you'll note that no `backgroundColor` or `borderRadius` have been set.

That's because on iOS the same view that's implementing a shadow can not also implement a `backgroundColor` or `borderRadius` _(a.k.a. cornerRadius)_. Well it can, but they'll need to be set with the shadow, on the views underlying `CALayer`.

```js
// main-page.js
function onLoaded(args) {
  const page = args.object;
  const label = page.getViewById("label");
  
  if(page.ios) {
    const layer = label.ios.layer;
    layer.backgroundColor = UIColor.whiteColor.CGColor;
    layer.shadowOffset = CGSizeMake(0, 1);
    layer.shadowOpacity = 1;
    layer.shadowRadius = 5;
    layer.cornerRadius = 20;
    /*
     You can also specify the shadow colour;
     (i.e. layer.shadowColor = UIColor.yellowColor.CGColor)
     
     But it will default to black if not set.
    */
  }
}
```

And now we have a shadow in iOS

| ![shadow-ios] |
| --- |

---

# Android Shadows
Since NativeScript 5.4 we have `androidElevation`. The feature is currently only available on Android, _hence the name_.

The `androidElevation` is an implementation of Material Designs elevation. To use it you just set the elevation and give your view a `backgroundColor` and a `margin` _(and a `borderRadius` if you like that)_.

```xml
<Label 
  id="label" 
  text="Shadow Games"

  margin="10"
  borderRadius="20"
  androidElevation="12"
  backgroundColor="white"/>
```

| ![shadow-android] | ![shadow-ios--lost] |
| --- | --- |
| Android shadow using elevation| iOS shadow has been lost |

Unfortunately, setting the `backgroundColor` and `borderRadius` will mean our iOS shadows will stop working. To fix that we can scope the offending properties to android.

```xml
<Label 
  id="label"
  text="Shadow Games"

  margin="10"
  android:borderRadius="20"
  androidElevation="12"
  android:backgroundColor="white"/>
```

Voila, we have our cross platform shadows :metal:.

| ![shadow-android] | ![shadow-ios] |
| --- | --- |
| Android shadow | iOS shadow |

---

# Where does it break down?
In a word, ___animation___. On Android you won't have any problems animating the views `backgroundColor`, but on iOS you'll lose the shadow. 

Thats because NativeScript animations animate the _view properties_, but we implemented the iOS `backgroundColor` _(and `borderRadius`)_ on the views underlying `CALayer`. So in order to animate the `backgroundColor` we're going to have to animate it down there.

```js
// ...
const layer = label.ios.layer;
const newColor = UIColor.yellowColor.CGColor;
const bgAnimation = CABasicAnimation.animationWithKeyPath("background");

bgAnimation.fromValue = layer.backgroundColor;
bgAnimation.toValue = newColor;
bgAnimation.duration = 2 // seconds

// triggers the animation to run.
layer.addAnimationForKey(bgAnimation, "bgAnimation");

// update layer prop to new value after animation, else it pops back to the initial value.
layer.backgroundColor = newColor;
```

And if you made it this far, here's a [Playground](https://play.nativescript.org/?template=play-js&id=vc8Ahs) with the animations in full effect.