Before we begin it's worth remembering, there's many ways to skin a 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.
<!--
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
.
// 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
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).
<Label
id="label"
text="Shadow Games"
margin="10"
borderRadius="20"
androidElevation="12"
backgroundColor="white"/>
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.
<Label
id="label"
text="Shadow Games"
margin="10"
android:borderRadius="20"
androidElevation="12"
android:backgroundColor="white"/>
Voila, we have our cross platform shadows 🤘.
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.
// ...
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 with the animations in full effect.
Top comments (2)
Thanks for the very useful post!
One small remark: the animationWithKeyPath should be:
CABasicAnimation.animationWithKeyPath('backgroundColor');
It seems to work with just “background”. Did you run into an issue using “background”, or is there some performance reason etc..?