mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 10:01:08 +08:00
feat(ios): improved shadow handling with background UI rework (#10374)
BREAKING CHANGES: `CSSShadow` was renamed into `ShadowCSSValues`
This commit is contained in:

committed by
GitHub

parent
a959a797df
commit
39eed526c1
@ -15,6 +15,7 @@ export class BoxShadowModel extends Observable {
|
||||
// private _boxShadow: string = '5 5 5 10 rgba(255, 0, 0, .9)';
|
||||
|
||||
background: string;
|
||||
borderColor: string;
|
||||
borderWidth: number;
|
||||
borderRadius: number;
|
||||
appliedBoxShadow: string;
|
||||
@ -72,18 +73,23 @@ export class BoxShadowModel extends Observable {
|
||||
this.notifyPropertyChange('selectedBorderType', value);
|
||||
switch (value) {
|
||||
case 'solid':
|
||||
this.borderWidth = this.borderWidth ? 0 : 2;
|
||||
this.borderWidth = this.borderWidth ? 0 : 5;
|
||||
break;
|
||||
case 'rounded':
|
||||
this.borderRadius = this.borderRadius ? 0 : 10;
|
||||
break;
|
||||
case 'colorful':
|
||||
this.borderColor = this.borderColor ? null : 'green blue pink yellow';
|
||||
break;
|
||||
case 'none':
|
||||
this.borderColor = null;
|
||||
this.borderRadius = 0;
|
||||
this.borderWidth = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.notifyPropertyChange('borderColor', this.borderColor);
|
||||
this.notifyPropertyChange('borderRadius', this.borderRadius);
|
||||
this.notifyPropertyChange('borderWidth', this.borderWidth);
|
||||
}
|
||||
|
@ -9,129 +9,48 @@
|
||||
<!-- layouts -->
|
||||
<ScrollView height="100%" visibility="{{ selectedComponentType === 'layouts' ? 'visible' : 'collapsed' }}">
|
||||
<StackLayout padding="20">
|
||||
<StackLayout
|
||||
width="300"
|
||||
height="100"
|
||||
class="demo-component"
|
||||
boxShadow="{{ appliedBoxShadow }}"
|
||||
borderWidth="{{ borderWidth }}"
|
||||
borderRadius="{{ borderRadius }}"
|
||||
background="{{ background }}"
|
||||
tap="{{ toggleAnimation }}"
|
||||
>
|
||||
<Label text="StackLayout"></Label>
|
||||
<StackLayout width="300" height="100" class="demo-component" boxShadow="{{ appliedBoxShadow }}" borderColor="{{ borderColor }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" background="{{ background }}" tap="{{ toggleAnimation }}">
|
||||
<Label text="StackLayout" />
|
||||
</StackLayout>
|
||||
|
||||
<GridLayout
|
||||
width="300"
|
||||
height="100"
|
||||
class="demo-component"
|
||||
boxShadow="{{ appliedBoxShadow }}"
|
||||
borderWidth="{{ borderWidth }}"
|
||||
borderRadius="{{ borderRadius }}"
|
||||
background="{{ background }}"
|
||||
tap="{{ toggleAnimation }}"
|
||||
>
|
||||
<Label text="GridLayout"></Label>
|
||||
<GridLayout width="300" height="100" class="demo-component" boxShadow="{{ appliedBoxShadow }}" borderColor="{{ borderColor }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" background="{{ background }}" tap="{{ toggleAnimation }}">
|
||||
<Label text="GridLayout" />
|
||||
</GridLayout>
|
||||
|
||||
<AbsoluteLayout
|
||||
width="300"
|
||||
height="100"
|
||||
class="demo-component"
|
||||
boxShadow="{{ appliedBoxShadow }}"
|
||||
borderWidth="{{ borderWidth }}"
|
||||
borderRadius="{{ borderRadius }}"
|
||||
background="{{ background }}"
|
||||
tap="{{ toggleAnimation }}"
|
||||
>
|
||||
<Label text="AbsoluteLayout"></Label>
|
||||
<AbsoluteLayout width="300" height="100" class="demo-component" boxShadow="{{ appliedBoxShadow }}" borderColor="{{ borderColor }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" background="{{ background }}" tap="{{ toggleAnimation }}">
|
||||
<Label text="AbsoluteLayout" />
|
||||
</AbsoluteLayout>
|
||||
|
||||
<DockLayout
|
||||
width="300"
|
||||
height="100"
|
||||
class="demo-component"
|
||||
boxShadow="{{ appliedBoxShadow }}"
|
||||
borderWidth="{{ borderWidth }}"
|
||||
borderRadius="{{ borderRadius }}"
|
||||
background="{{ background }}"
|
||||
tap="{{ toggleAnimation }}"
|
||||
>
|
||||
<Label text="DockLayout"></Label>
|
||||
<DockLayout width="300" height="100" class="demo-component" boxShadow="{{ appliedBoxShadow }}" borderColor="{{ borderColor }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" background="{{ background }}" tap="{{ toggleAnimation }}">
|
||||
<Label text="DockLayout" />
|
||||
</DockLayout>
|
||||
|
||||
<FlexboxLayout
|
||||
width="300"
|
||||
height="100"
|
||||
class="demo-component"
|
||||
boxShadow="{{ appliedBoxShadow }}"
|
||||
borderWidth="{{ borderWidth }}"
|
||||
borderRadius="{{ borderRadius }}"
|
||||
background="{{ background }}"
|
||||
tap="{{ toggleAnimation }}"
|
||||
>
|
||||
<Label text="FlexboxLayout"></Label>
|
||||
<FlexboxLayout width="300" height="100" class="demo-component" boxShadow="{{ appliedBoxShadow }}" borderColor="{{ borderColor }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" background="{{ background }}" tap="{{ toggleAnimation }}">
|
||||
<Label text="FlexboxLayout" />
|
||||
</FlexboxLayout>
|
||||
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
|
||||
<!-- labels -->
|
||||
<GridLayout
|
||||
rows="*"
|
||||
height="100%"
|
||||
visibility="{{ selectedComponentType === 'labels' ? 'visible' : 'collapsed' }}">
|
||||
<GridLayout rows="*" height="100%" visibility="{{ selectedComponentType === 'labels' ? 'visible' : 'collapsed' }}">
|
||||
|
||||
<Label
|
||||
horizontalAlignment="center"
|
||||
verticalAlignment="center"
|
||||
class="demo-component"
|
||||
boxShadow="{{ appliedBoxShadow }}"
|
||||
borderWidth="{{ borderWidth }}"
|
||||
borderRadius="{{ borderRadius }}"
|
||||
background="{{ background }}"
|
||||
tap="{{ toggleAnimation }}"
|
||||
text="Label"></Label>
|
||||
<Label horizontalAlignment="center" verticalAlignment="center" class="demo-component" boxShadow="{{ appliedBoxShadow }}" borderColor="{{ borderColor }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" background="{{ background }}" tap="{{ toggleAnimation }}" text="Label" />
|
||||
|
||||
</GridLayout>
|
||||
|
||||
<!-- buttons -->
|
||||
<GridLayout
|
||||
rows="*"
|
||||
height="100%"
|
||||
visibility="{{ selectedComponentType === 'buttons' ? 'visible' : 'collapsed' }}">
|
||||
<GridLayout rows="*" height="100%" visibility="{{ selectedComponentType === 'buttons' ? 'visible' : 'collapsed' }}">
|
||||
|
||||
<Button
|
||||
horizontalAlignment="center"
|
||||
verticalAlignment="center"
|
||||
class="demo-component"
|
||||
boxShadow="{{ appliedBoxShadow }}"
|
||||
borderWidth="{{ borderWidth }}"
|
||||
borderRadius="{{ borderRadius }}"
|
||||
background="{{ background }}"
|
||||
tap="{{ toggleAnimation }}"
|
||||
text="button"
|
||||
></Button>
|
||||
<Button horizontalAlignment="center" verticalAlignment="center" class="demo-component" boxShadow="{{ appliedBoxShadow }}" borderColor="{{ borderColor }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" background="{{ background }}" tap="{{ toggleAnimation }}" text="button" />
|
||||
|
||||
</GridLayout>
|
||||
|
||||
<!-- images -->
|
||||
<GridLayout
|
||||
rows="*"
|
||||
height="100%"
|
||||
visibility="{{ selectedComponentType === 'images' ? 'visible' : 'collapsed' }}">
|
||||
<GridLayout rows="*" height="100%" visibility="{{ selectedComponentType === 'images' ? 'visible' : 'collapsed' }}">
|
||||
|
||||
<ContentView width="100"
|
||||
height="100"
|
||||
horizontalAlignment="center"
|
||||
verticalAlignment="center"
|
||||
class="demo-component"
|
||||
boxShadow="{{ appliedBoxShadow }}"
|
||||
borderWidth="{{ borderWidth }}"
|
||||
borderRadius="{{ borderRadius }}"
|
||||
tap="{{ toggleAnimation }}">
|
||||
<Image src="https://raw.githubusercontent.com/NativeScript/artwork/main/logo/export/NativeScript_Logo_Blue_Transparent.png"></Image>
|
||||
<ContentView width="100" height="100" horizontalAlignment="center" verticalAlignment="center" class="demo-component" boxShadow="{{ appliedBoxShadow }}" borderColor="{{ borderColor }}" borderWidth="{{ borderWidth }}" borderRadius="{{ borderRadius }}" tap="{{ toggleAnimation }}">
|
||||
<Image src="https://raw.githubusercontent.com/NativeScript/artwork/main/logo/export/NativeScript_Logo_Blue_Transparent.png" />
|
||||
</ContentView>
|
||||
|
||||
|
||||
@ -139,61 +58,46 @@
|
||||
|
||||
</StackLayout>
|
||||
|
||||
<GridLayout
|
||||
row="1"
|
||||
rows="auto"
|
||||
columns="auto, *, auto"
|
||||
class="box-shadow-prop-controls">
|
||||
<Label
|
||||
col="0"
|
||||
verticalAlignment="center"
|
||||
text="box-shadow:"></Label>
|
||||
<TextField
|
||||
col="1"
|
||||
placeholder="box-shadow"
|
||||
text="{{ boxShadow }}"
|
||||
textChange="{{ textChange }}"
|
||||
>
|
||||
<GridLayout row="1" rows="auto" columns="auto, *, auto" class="box-shadow-prop-controls">
|
||||
<Label col="0" verticalAlignment="center" text="box-shadow:" />
|
||||
<TextField col="1" placeholder="box-shadow" text="{{ boxShadow }}" textChange="{{ textChange }}">
|
||||
</TextField>
|
||||
<Button
|
||||
col="2"
|
||||
text="APPLY"
|
||||
class="btn-apply"
|
||||
tap="{{ applyBoxShadow }}"></Button>
|
||||
<Button col="2" text="APPLY" class="btn-apply" tap="{{ applyBoxShadow }}" />
|
||||
</GridLayout>
|
||||
<ScrollView row="2">
|
||||
<StackLayout padding="10" class="controls">
|
||||
<Label text="Components"></Label>
|
||||
<Label text="Components" />
|
||||
<FlexboxLayout flexDirection="row" flexWrap="wrap">
|
||||
<Button text="Layouts" componentType="layouts" tap="{{ selectComponentType }}"></Button>
|
||||
<Button text="Labels" componentType="labels" selectedAttr="{{ selectedComponentType }}" tap="{{ selectComponentType }}"></Button>
|
||||
<Button text="Buttons" componentType="buttons" selectedAttr="{{ selectedComponentType == 'buttons' }}" tap="{{ selectComponentType }}"></Button>
|
||||
<Button text="Images" componentType="images" selectedAttr="{{ selectedComponentType == 'images' }}" tap="{{ selectComponentType }}"></Button>
|
||||
<Button text="Layouts" componentType="layouts" tap="{{ selectComponentType }}" />
|
||||
<Button text="Labels" componentType="labels" selectedAttr="{{ selectedComponentType }}" tap="{{ selectComponentType }}" />
|
||||
<Button text="Buttons" componentType="buttons" selectedAttr="{{ selectedComponentType == 'buttons' }}" tap="{{ selectComponentType }}" />
|
||||
<Button text="Images" componentType="images" selectedAttr="{{ selectedComponentType == 'images' }}" tap="{{ selectComponentType }}" />
|
||||
</FlexboxLayout>
|
||||
|
||||
<Label text="Background"></Label>
|
||||
<Label text="Background" />
|
||||
<FlexboxLayout flexDirection="row" flexWrap="wrap">
|
||||
<Button text="Solid" backgroundType="solid" tap="{{ selectBackgroundType }}"></Button>
|
||||
<Button text="Transparent" backgroundType="transparent" tap="{{ selectBackgroundType }}"></Button>
|
||||
<Button text="Gradient" backgroundType="gradient" tap="{{ selectBackgroundType }}"></Button>
|
||||
<Button text="Solid" backgroundType="solid" tap="{{ selectBackgroundType }}" />
|
||||
<Button text="Transparent" backgroundType="transparent" tap="{{ selectBackgroundType }}" />
|
||||
<Button text="Gradient" backgroundType="gradient" tap="{{ selectBackgroundType }}" />
|
||||
</FlexboxLayout>
|
||||
|
||||
<Label text="Borders"></Label>
|
||||
<Label text="Borders" />
|
||||
<FlexboxLayout flexDirection="row" flexWrap="wrap">
|
||||
<Button text="Solid" borderType="solid" tap="{{ selectBorderType }}"></Button>
|
||||
<Button text="Rounded" borderType="rounded" tap="{{ selectBorderType }}"></Button>
|
||||
<Button text="None" borderType="none" tap="{{ selectBorderType }}"></Button>
|
||||
<Button text="Solid" borderType="solid" tap="{{ selectBorderType }}" />
|
||||
<Button text="Rounded" borderType="rounded" tap="{{ selectBorderType }}" />
|
||||
<Button text="Colorful" borderType="colorful" tap="{{ selectBorderType }}" />
|
||||
<Button text="None" borderType="none" tap="{{ selectBorderType }}" />
|
||||
</FlexboxLayout>
|
||||
|
||||
<Label text="Animations"></Label>
|
||||
<Label text="Tap on the component to start and stop animation" class="description"></Label>
|
||||
<Label text="Animations" />
|
||||
<Label text="Tap on the component to start and stop animation" class="description" />
|
||||
<FlexboxLayout flexDirection="row" flexWrap="wrap">
|
||||
<Button text="Width" animationType="width" tap="{{ selectAnimationType }}"></Button>
|
||||
<Button text="Height" animationType="height" tap="{{ selectAnimationType }}"></Button>
|
||||
<Button text="Opacity" animationType="opacity" tap="{{ selectAnimationType }}"></Button>
|
||||
<Button text="Translate" animationType="translate" tap="{{ selectAnimationType }}"></Button>
|
||||
<Button text="Scale" animationType="scale" tap="{{ selectAnimationType }}"></Button>
|
||||
<Button text="Rotate" animationType="rotate" tap="{{ selectAnimationType }}"></Button>
|
||||
<Button text="Width" animationType="width" tap="{{ selectAnimationType }}" />
|
||||
<Button text="Height" animationType="height" tap="{{ selectAnimationType }}" />
|
||||
<Button text="Opacity" animationType="opacity" tap="{{ selectAnimationType }}" />
|
||||
<Button text="Translate" animationType="translate" tap="{{ selectAnimationType }}" />
|
||||
<Button text="Scale" animationType="scale" tap="{{ selectAnimationType }}" />
|
||||
<Button text="Rotate" animationType="rotate" tap="{{ selectAnimationType }}" />
|
||||
</FlexboxLayout>
|
||||
|
||||
</StackLayout>
|
||||
|
@ -42,6 +42,14 @@ ScrollView {
|
||||
border-left-width: 20;
|
||||
}
|
||||
|
||||
.gradient {
|
||||
background: linear-gradient(to bottom, orangered, green, lightblue);
|
||||
}
|
||||
|
||||
.shadow {
|
||||
box-shadow: inset 0 0 5 5 #000000;
|
||||
}
|
||||
|
||||
.body {
|
||||
font-size: 11;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
</Page.actionBar>
|
||||
|
||||
<StackLayout>
|
||||
<StackLayout class="p-10" row="0">
|
||||
<StackLayout class="p-10">
|
||||
<Label text="Default style = scrolls out of container" class="body m-b-10" />
|
||||
<ScrollView height="40">
|
||||
<StackLayout>
|
||||
@ -17,7 +17,7 @@
|
||||
</ScrollView>
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10" row="1">
|
||||
<StackLayout class="p-10">
|
||||
<Label text="Adding border changes the height but fixes scrolling" textWrap="true" class="body m-b-10" />
|
||||
<ScrollView class="bordered" height="40">
|
||||
<StackLayout>
|
||||
@ -28,7 +28,7 @@
|
||||
</ScrollView>
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10" row="2">
|
||||
<StackLayout class="p-10">
|
||||
<Label text="border-radius" class="body m-b-10" />
|
||||
<ScrollView class="bordered fixed-height border-radius">
|
||||
<StackLayout>
|
||||
@ -39,7 +39,7 @@
|
||||
</ScrollView>
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10" row="2">
|
||||
<StackLayout class="p-10">
|
||||
<Label text="border-radius = weird" class="body m-b-10" />
|
||||
<ScrollView class="bordered fixed-height border-radius-nonuniform">
|
||||
<StackLayout>
|
||||
@ -50,7 +50,7 @@
|
||||
</ScrollView>
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10" row="3">
|
||||
<StackLayout class="p-10">
|
||||
<Label text="border-width = weird" class="body m-b-10" />
|
||||
<ScrollView class="bordered-nonuniform fixed-height">
|
||||
<StackLayout>
|
||||
@ -61,5 +61,38 @@
|
||||
</ScrollView>
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10">
|
||||
<Label text="border-width = rounded weird" class="body m-b-10" />
|
||||
<ScrollView class="bordered-nonuniform fixed-height border-radius">
|
||||
<StackLayout>
|
||||
<GridLayout width="30" height="30" backgroundColor="red" />
|
||||
<GridLayout width="30" height="30" backgroundColor="yellow" />
|
||||
<GridLayout width="30" height="30" backgroundColor="green" />
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10">
|
||||
<Label text="background = gradient" class="body m-b-10" />
|
||||
<ScrollView class="fixed-height gradient">
|
||||
<StackLayout>
|
||||
<GridLayout width="30" height="30" backgroundColor="red" />
|
||||
<GridLayout width="30" height="30" backgroundColor="yellow" />
|
||||
<GridLayout width="30" height="30" backgroundColor="green" />
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10">
|
||||
<Label text="box-shadow" class="body m-b-10" />
|
||||
<ScrollView class="fixed-height shadow">
|
||||
<StackLayout>
|
||||
<GridLayout width="30" height="30" backgroundColor="red" />
|
||||
<GridLayout width="30" height="30" backgroundColor="yellow" />
|
||||
<GridLayout width="30" height="30" backgroundColor="green" />
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
</StackLayout>
|
||||
|
||||
</StackLayout>
|
||||
</Page>
|
||||
|
@ -42,6 +42,14 @@ TextView {
|
||||
border-left-width: 20;
|
||||
}
|
||||
|
||||
.gradient {
|
||||
background: linear-gradient(to bottom, orangered, green, lightblue);
|
||||
}
|
||||
|
||||
.shadow {
|
||||
box-shadow: inset 0 0 5 5 #000000;
|
||||
}
|
||||
|
||||
.body {
|
||||
font-size: 11;
|
||||
}
|
||||
|
@ -6,27 +6,39 @@
|
||||
</Page.actionBar>
|
||||
|
||||
<StackLayout>
|
||||
<StackLayout class="p-10" row="0">
|
||||
<StackLayout class="p-10">
|
||||
<Label text="Default style = scrolls out of container" class="body m-b-10" />
|
||||
<TextView hint="Add your comment..." />
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10" row="1">
|
||||
<StackLayout class="p-10">
|
||||
<Label text="Adding border changes the height but fixes scrolling" textWrap="true" class="body m-b-10" />
|
||||
<TextView hint="Add your comment..." class="bordered" />
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10" row="2">
|
||||
<StackLayout class="p-10">
|
||||
<TextView hint="bordered fixed-height border-radius" class="bordered fixed-height border-radius" />
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10" row="2">
|
||||
<StackLayout class="p-10">
|
||||
<TextView hint="bordered fixed-height border-radius-nonuniform" class="bordered fixed-height border-radius-nonuniform" />
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10" row="3">
|
||||
<StackLayout class="p-10">
|
||||
<TextView hint="bordered-nonuniform fixed-height" class="bordered-nonuniform fixed-height" />
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10">
|
||||
<TextView hint="bordered-nonuniform fixed-height border-radius" class="bordered-nonuniform fixed-height border-radius" />
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10">
|
||||
<TextView hint="fixed-height gradient" class="fixed-height gradient" />
|
||||
</StackLayout>
|
||||
|
||||
<StackLayout class="p-10">
|
||||
<TextView hint="fixed-height shadow" class="fixed-height shadow" />
|
||||
</StackLayout>
|
||||
|
||||
</StackLayout>
|
||||
</Page>
|
||||
|
@ -6,6 +6,8 @@ import { View } from '../core/view';
|
||||
import { AnimationBase, Properties, CubicBezierAnimationCurve } from './animation-common';
|
||||
import { Trace } from '../../trace';
|
||||
import { opacityProperty, backgroundColorProperty, rotateProperty, rotateXProperty, rotateYProperty, translateXProperty, translateYProperty, scaleXProperty, scaleYProperty, heightProperty, widthProperty, PercentLength } from '../styling/style-properties';
|
||||
import { ios as iosBackground } from '../styling/background';
|
||||
import { ios as iosViewUtils, NativeScriptUIView } from '../utils';
|
||||
|
||||
import { ios as iosHelper } from '../../utils/native-helper';
|
||||
|
||||
@ -214,7 +216,6 @@ export class Animation extends AnimationBase {
|
||||
public cancel(): void {
|
||||
if (!this.isPlaying) {
|
||||
Trace.write('Animation is not currently playing.', Trace.categories.Animation, Trace.messageType.warn);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -222,8 +223,54 @@ export class Animation extends AnimationBase {
|
||||
for (let i = 0; i < this._mergedPropertyAnimations.length; i++) {
|
||||
const propertyAnimation = this._mergedPropertyAnimations[i];
|
||||
if (propertyAnimation) {
|
||||
if (propertyAnimation.target?.nativeViewProtected?.layer) {
|
||||
propertyAnimation.target.nativeViewProtected.layer.removeAllAnimations();
|
||||
if (propertyAnimation.target?.nativeViewProtected) {
|
||||
const nativeView: NativeScriptUIView = propertyAnimation.target.nativeViewProtected;
|
||||
if (nativeView.layer.mask) {
|
||||
nativeView.layer.mask.removeAllAnimations();
|
||||
}
|
||||
nativeView.layer.removeAllAnimations();
|
||||
|
||||
// Gradient background animations
|
||||
if (nativeView.gradientLayer) {
|
||||
nativeView.gradientLayer.removeAllAnimations();
|
||||
}
|
||||
|
||||
// Border animations
|
||||
if (nativeView.borderLayer) {
|
||||
if (nativeView.borderLayer.mask) {
|
||||
nativeView.borderLayer.mask.removeAllAnimations();
|
||||
}
|
||||
|
||||
const borderLayers = nativeView.borderLayer.sublayers;
|
||||
if (borderLayers?.count) {
|
||||
for (let i = 0, count = borderLayers.count; i < count; i++) {
|
||||
borderLayers[i].removeAllAnimations();
|
||||
}
|
||||
}
|
||||
|
||||
nativeView.borderLayer.removeAllAnimations();
|
||||
}
|
||||
|
||||
// Shadow animations
|
||||
if (nativeView.outerShadowContainerLayer) {
|
||||
if (nativeView.outerShadowContainerLayer.mask) {
|
||||
nativeView.outerShadowContainerLayer.mask.removeAllAnimations();
|
||||
}
|
||||
|
||||
const outerShadowLayers = nativeView.outerShadowContainerLayer.sublayers;
|
||||
if (outerShadowLayers?.count) {
|
||||
for (let i = 0, count = outerShadowLayers.count; i < count; i++) {
|
||||
const shadowLayer = outerShadowLayers[i];
|
||||
|
||||
if (shadowLayer.mask) {
|
||||
shadowLayer.mask.removeAllAnimations();
|
||||
}
|
||||
shadowLayer.removeAllAnimations();
|
||||
}
|
||||
}
|
||||
|
||||
nativeView.outerShadowContainerLayer.removeAllAnimations();
|
||||
}
|
||||
}
|
||||
if (propertyAnimation._propertyResetCallback) {
|
||||
propertyAnimation._propertyResetCallback(propertyAnimation._originalValue, this._valueSource);
|
||||
@ -262,14 +309,14 @@ export class Animation extends AnimationBase {
|
||||
private static _getNativeAnimationArguments(animation: PropertyAnimationInfo, valueSource: 'animation' | 'keyframe'): AnimationInfo {
|
||||
const view = animation.target;
|
||||
const style = view.style;
|
||||
const nativeView = <UIView>view.nativeViewProtected;
|
||||
const nativeView: NativeScriptUIView = view.nativeViewProtected;
|
||||
const parent = view.parent as View;
|
||||
|
||||
let propertyNameToAnimate = animation.property;
|
||||
let subPropertyNameToAnimate;
|
||||
let toValue = animation.value;
|
||||
let fromValue;
|
||||
if (nativeView?.layer) {
|
||||
if (nativeView) {
|
||||
const setLocal = valueSource === 'animation';
|
||||
|
||||
switch (animation.property) {
|
||||
@ -279,10 +326,11 @@ export class Animation extends AnimationBase {
|
||||
style[setLocal ? backgroundColorProperty.name : backgroundColorProperty.keyframe] = value;
|
||||
};
|
||||
fromValue = nativeView.layer.backgroundColor;
|
||||
|
||||
if (nativeView instanceof UILabel) {
|
||||
nativeView.setValueForKey(UIColor.clearColor, 'backgroundColor');
|
||||
}
|
||||
toValue = toValue.CGColor;
|
||||
toValue = toValue?.ios?.CGColor;
|
||||
break;
|
||||
case Properties.opacity:
|
||||
animation._originalValue = view.opacity;
|
||||
@ -395,7 +443,7 @@ export class Animation extends AnimationBase {
|
||||
const extentY = isHeight ? asNumber : currentBounds.size.height;
|
||||
fromValue = NSValue.valueWithCGRect(currentBounds);
|
||||
toValue = NSValue.valueWithCGRect(CGRectMake(currentBounds.origin.x, currentBounds.origin.y, extentX, extentY));
|
||||
animation._originalValue = view.height;
|
||||
animation._originalValue = view[isHeight ? 'height' : 'width'];
|
||||
animation._propertyResetCallback = (value, valueSource) => {
|
||||
const prop = isHeight ? heightProperty : widthProperty;
|
||||
style[setLocal ? prop.name : prop.keyframe] = value;
|
||||
@ -438,7 +486,7 @@ export class Animation extends AnimationBase {
|
||||
}
|
||||
|
||||
private static _createNativeAnimation(propertyAnimations: Array<PropertyAnimation>, index: number, playSequentially: boolean, args: AnimationInfo, animation: PropertyAnimation, valueSource: 'animation' | 'keyframe', finishedCallback: (cancelled?: boolean) => void) {
|
||||
const nativeView = <UIView>animation.target.nativeViewProtected;
|
||||
const nativeView: NativeScriptUIView = animation.target.nativeViewProtected;
|
||||
|
||||
let nativeAnimation;
|
||||
|
||||
@ -450,8 +498,16 @@ export class Animation extends AnimationBase {
|
||||
|
||||
const animationDelegate = AnimationDelegateImpl.initWithFinishedCallback(finishedCallback, animation, valueSource);
|
||||
nativeAnimation.setValueForKey(animationDelegate, 'delegate');
|
||||
if (nativeView?.layer) {
|
||||
if (nativeView) {
|
||||
nativeView.layer.addAnimationForKey(nativeAnimation, args.propertyNameToAnimate);
|
||||
if (args.propertyNameToAnimate === 'bounds') {
|
||||
this.animateNestedLayerSizeUsingBasicAnimation(nativeView, args.toValue.CGRectValue, animation, args, nativeAnimation);
|
||||
}
|
||||
|
||||
// Shadow layers do not inherit from animating view layer
|
||||
if (nativeView.outerShadowContainerLayer) {
|
||||
nativeView.outerShadowContainerLayer.addAnimationForKey(nativeAnimation, args.propertyNameToAnimate);
|
||||
}
|
||||
}
|
||||
let callback = undefined;
|
||||
if (index + 1 < propertyAnimations.length) {
|
||||
@ -512,7 +568,7 @@ export class Animation extends AnimationBase {
|
||||
}
|
||||
|
||||
private static _createNativeSpringAnimation(propertyAnimations: Array<PropertyAnimationInfo>, index: number, playSequentially: boolean, args: AnimationInfo, animation: PropertyAnimationInfo, valueSource: 'animation' | 'keyframe', finishedCallback: (cancelled?: boolean) => void) {
|
||||
const nativeView = <UIView>animation.target.nativeViewProtected;
|
||||
const nativeView: NativeScriptUIView = animation.target.nativeViewProtected;
|
||||
|
||||
let callback = undefined;
|
||||
let nextAnimation;
|
||||
@ -552,6 +608,10 @@ export class Animation extends AnimationBase {
|
||||
case Properties.width:
|
||||
animation._originalValue = animation.target[animation.property];
|
||||
nativeView.layer.setValueForKey(args.toValue, args.propertyNameToAnimate);
|
||||
|
||||
// Resize background during animation
|
||||
iosBackground.drawBackgroundVisualEffects(animation.target);
|
||||
|
||||
animation._propertyResetCallback = function (value) {
|
||||
animation.target[animation.property] = value;
|
||||
};
|
||||
@ -559,6 +619,12 @@ export class Animation extends AnimationBase {
|
||||
case _transform:
|
||||
animation._originalValue = nativeView.layer.transform;
|
||||
nativeView.layer.setValueForKey(args.toValue, args.propertyNameToAnimate);
|
||||
|
||||
// Shadow layers do not inherit from animating view layer
|
||||
if (nativeView.outerShadowContainerLayer) {
|
||||
nativeView.outerShadowContainerLayer.setValueForKey(args.toValue, args.propertyNameToAnimate);
|
||||
}
|
||||
|
||||
animation._propertyResetCallback = function (value) {
|
||||
nativeView.layer.transform = value;
|
||||
};
|
||||
@ -687,6 +753,182 @@ export class Animation extends AnimationBase {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static animateNestedLayerSizeUsingBasicAnimation(nativeView: NativeScriptUIView, bounds: CGRect, animation: PropertyAnimation, args: AnimationInfo, nativeAnimation: CABasicAnimation) {
|
||||
const view: View = animation.target;
|
||||
|
||||
// Gradient background animation
|
||||
if (nativeView.gradientLayer) {
|
||||
nativeView.gradientLayer.addAnimationForKey(nativeAnimation, 'bounds');
|
||||
}
|
||||
|
||||
let clipPath; // This is also used for animating shadow
|
||||
|
||||
// Clipping mask animation
|
||||
if (nativeView.layer.mask instanceof CAShapeLayer) {
|
||||
let toValue;
|
||||
|
||||
if (nativeView.maskType === iosViewUtils.LayerMask.BORDER) {
|
||||
toValue = iosBackground.generateNonUniformBorderOuterClipRoundedPath(view, bounds);
|
||||
} else if (nativeView.maskType === iosViewUtils.LayerMask.CLIP_PATH) {
|
||||
clipPath = iosBackground.generateClipPath(view, bounds);
|
||||
toValue = clipPath;
|
||||
} else {
|
||||
Trace.write('Unknown mask on animating view: ' + view, Trace.categories.Animation, Trace.messageType.info);
|
||||
}
|
||||
|
||||
if (toValue) {
|
||||
nativeView.layer.mask.addAnimationForKey(
|
||||
this._createBasicAnimation(
|
||||
{
|
||||
...args,
|
||||
propertyNameToAnimate: 'path',
|
||||
fromValue: nativeView.layer.mask.path,
|
||||
toValue,
|
||||
},
|
||||
animation
|
||||
),
|
||||
'path'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Border animations (uniform and non-uniform)
|
||||
if (nativeView.hasNonUniformBorder) {
|
||||
if (nativeView.borderLayer) {
|
||||
const innerClipPath = iosBackground.generateNonUniformBorderInnerClipRoundedPath(animation.target, bounds);
|
||||
|
||||
if (nativeView.hasNonUniformBorderColor) {
|
||||
const borderMask = nativeView.borderLayer.mask;
|
||||
if (borderMask instanceof CAShapeLayer) {
|
||||
borderMask.addAnimationForKey(
|
||||
this._createBasicAnimation(
|
||||
{
|
||||
...args,
|
||||
propertyNameToAnimate: 'path',
|
||||
fromValue: borderMask.path,
|
||||
toValue: innerClipPath,
|
||||
},
|
||||
animation
|
||||
),
|
||||
'path'
|
||||
);
|
||||
}
|
||||
|
||||
const borderLayers = nativeView.borderLayer.sublayers;
|
||||
if (borderLayers?.count) {
|
||||
const paths = iosBackground.generateNonUniformMultiColorBorderRoundedPaths(animation.target, bounds);
|
||||
|
||||
for (let i = 0, count = borderLayers.count; i < count; i++) {
|
||||
const layer = nativeView.borderLayer.sublayers[i];
|
||||
if (layer instanceof CAShapeLayer) {
|
||||
layer.addAnimationForKey(
|
||||
this._createBasicAnimation(
|
||||
{
|
||||
...args,
|
||||
propertyNameToAnimate: 'path',
|
||||
fromValue: layer.path,
|
||||
toValue: paths[i],
|
||||
},
|
||||
animation
|
||||
),
|
||||
'path'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nativeView.borderLayer.addAnimationForKey(
|
||||
this._createBasicAnimation(
|
||||
{
|
||||
...args,
|
||||
propertyNameToAnimate: 'path',
|
||||
fromValue: nativeView.borderLayer.path,
|
||||
toValue: innerClipPath,
|
||||
},
|
||||
animation
|
||||
),
|
||||
'path'
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: Animate border width when borders get support for percentage values
|
||||
// Uniform corner radius also relies on view size
|
||||
if (nativeView.layer.cornerRadius) {
|
||||
nativeView.layer.addAnimationForKey(
|
||||
this._createBasicAnimation(
|
||||
{
|
||||
...args,
|
||||
propertyNameToAnimate: 'cornerRadius',
|
||||
fromValue: nativeView.layer.cornerRadius,
|
||||
toValue: iosBackground.getUniformBorderRadius(animation.target, bounds),
|
||||
},
|
||||
animation
|
||||
),
|
||||
'cornerRadius'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Shadow layers do not inherit from animating view layer
|
||||
if (nativeView.outerShadowContainerLayer) {
|
||||
const shadowClipMask = nativeView.outerShadowContainerLayer.mask;
|
||||
|
||||
// This is for animating view clip path on shadow
|
||||
if (clipPath && shadowClipMask instanceof CAShapeLayer) {
|
||||
shadowClipMask.addAnimationForKey(
|
||||
this._createBasicAnimation(
|
||||
{
|
||||
...args,
|
||||
propertyNameToAnimate: 'path',
|
||||
fromValue: shadowClipMask.path,
|
||||
toValue: clipPath,
|
||||
},
|
||||
animation
|
||||
),
|
||||
'path'
|
||||
);
|
||||
}
|
||||
|
||||
const outerShadowLayers = nativeView.outerShadowContainerLayer.sublayers;
|
||||
if (outerShadowLayers?.count) {
|
||||
const { maskPath, shadowPath } = iosBackground.generateShadowLayerPaths(view, bounds);
|
||||
|
||||
for (let i = 0, count = outerShadowLayers.count; i < count; i++) {
|
||||
const shadowLayer = outerShadowLayers[i];
|
||||
|
||||
shadowLayer.addAnimationForKey(
|
||||
this._createBasicAnimation(
|
||||
{
|
||||
...args,
|
||||
propertyNameToAnimate: 'shadowPath',
|
||||
fromValue: shadowLayer.shadowPath,
|
||||
toValue: shadowPath,
|
||||
},
|
||||
animation
|
||||
),
|
||||
'shadowPath'
|
||||
);
|
||||
|
||||
if (shadowLayer.mask instanceof CAShapeLayer) {
|
||||
shadowLayer.mask.addAnimationForKey(
|
||||
this._createBasicAnimation(
|
||||
{
|
||||
...args,
|
||||
propertyNameToAnimate: 'path',
|
||||
fromValue: shadowLayer.mask.path,
|
||||
toValue: maskPath,
|
||||
},
|
||||
animation
|
||||
),
|
||||
'path'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function _getTransformMismatchErrorMessage(view: View): string {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ControlStateChangeListener } from '../core/control-state-change';
|
||||
import { ButtonBase } from './button-common';
|
||||
import { View, PseudoClassHandler } from '../core/view';
|
||||
import { backgroundColorProperty, borderTopWidthProperty, borderRightWidthProperty, borderBottomWidthProperty, borderLeftWidthProperty, paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty } from '../styling/style-properties';
|
||||
import { borderTopWidthProperty, borderRightWidthProperty, borderBottomWidthProperty, borderLeftWidthProperty, paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty } from '../styling/style-properties';
|
||||
import { textAlignmentProperty, whiteSpaceProperty, textOverflowProperty } from '../text-base';
|
||||
import { layout } from '../../utils';
|
||||
import { CoreTypes } from '../../core-types';
|
||||
@ -56,14 +56,6 @@ export class Button extends ButtonBase {
|
||||
}
|
||||
}
|
||||
|
||||
[backgroundColorProperty.getDefault](): UIColor {
|
||||
return this.nativeViewProtected.backgroundColor;
|
||||
}
|
||||
|
||||
[backgroundColorProperty.setNative](value: UIColor | Color) {
|
||||
this.nativeViewProtected.backgroundColor = value instanceof Color ? value.ios : value;
|
||||
}
|
||||
|
||||
[borderTopWidthProperty.getDefault](): CoreTypes.LengthType {
|
||||
return {
|
||||
value: this.nativeViewProtected.contentEdgeInsets.top,
|
||||
|
@ -957,6 +957,12 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition
|
||||
* Clean up references to the native view.
|
||||
*/
|
||||
public disposeNativeView() {
|
||||
// Unset those values so that view will check for resize after being removed and re-added to view-tree
|
||||
this._oldLeft = 0;
|
||||
this._oldTop = 0;
|
||||
this._oldRight = 0;
|
||||
this._oldBottom = 0;
|
||||
|
||||
this.notify({
|
||||
eventName: ViewBase.disposeNativeViewEvent,
|
||||
object: this,
|
||||
|
@ -24,7 +24,7 @@ import { accessibilityEnabledProperty, accessibilityHiddenProperty, accessibilit
|
||||
import { AccessibilityLiveRegion, AccessibilityRole, AndroidAccessibilityEvent, isAccessibilityServiceEnabled, sendAccessibilityEvent, updateAccessibilityProperties, updateContentDescription, AccessibilityState } from '../../../accessibility';
|
||||
import * as Utils from '../../../utils';
|
||||
import { SDK_VERSION } from '../../../utils/constants';
|
||||
import { CSSShadow } from '../../styling/css-shadow';
|
||||
import { BoxShadow } from '../../styling/box-shadow';
|
||||
import { _setAndroidFragmentTransitions, _getAnimatedEntries, _updateTransitions, _reverseTransitions, _clearEntry, _clearFragment, addNativeTransitionListener } from '../../frame/fragment.transitions';
|
||||
|
||||
export * from './view-common';
|
||||
@ -1150,15 +1150,15 @@ export class View extends ViewCommon {
|
||||
}
|
||||
}
|
||||
|
||||
protected _drawBoxShadow(boxShadow: CSSShadow) {
|
||||
protected _drawBoxShadow(boxShadow: BoxShadow) {
|
||||
const nativeView = this.nativeViewProtected;
|
||||
const config = {
|
||||
shadowColor: boxShadow.color.android,
|
||||
cornerRadius: Length.toDevicePixels(this.borderRadius as CoreTypes.LengthType, 0.0),
|
||||
spreadRadius: Length.toDevicePixels(boxShadow.spreadRadius, 0.0),
|
||||
blurRadius: Length.toDevicePixels(boxShadow.blurRadius, 0.0),
|
||||
offsetX: Length.toDevicePixels(boxShadow.offsetX, 0.0),
|
||||
offsetY: Length.toDevicePixels(boxShadow.offsetY, 0.0),
|
||||
spreadRadius: boxShadow.spreadRadius,
|
||||
blurRadius: boxShadow.blurRadius,
|
||||
offsetX: boxShadow.offsetX,
|
||||
offsetY: boxShadow.offsetY,
|
||||
};
|
||||
org.nativescript.widgets.Utils.drawBoxShadow(nativeView, JSON.stringify(config));
|
||||
}
|
||||
|
4
packages/core/ui/core/view/index.d.ts
vendored
4
packages/core/ui/core/view/index.d.ts
vendored
@ -7,7 +7,7 @@ import { GestureTypes, GesturesObserver } from '../../gestures';
|
||||
import { LinearGradient } from '../../styling/linear-gradient';
|
||||
import { AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, AccessibilityTrait, AccessibilityEventOptions } from '../../../accessibility/accessibility-types';
|
||||
import { CoreTypes } from '../../../core-types';
|
||||
import { CSSShadow } from '../../styling/css-shadow';
|
||||
import { ShadowCSSValues } from '../../styling/css-shadow';
|
||||
import { ViewCommon } from './view-common';
|
||||
|
||||
export * from './view-common';
|
||||
@ -332,7 +332,7 @@ export abstract class View extends ViewCommon {
|
||||
/**
|
||||
* Gets or sets the box shadow of the view.
|
||||
*/
|
||||
boxShadow: string | CSSShadow;
|
||||
boxShadow: string | ShadowCSSValues;
|
||||
|
||||
/**
|
||||
* Gets or sets the minimum width the view may grow to.
|
||||
|
@ -8,8 +8,8 @@ import { Trace } from '../../../trace';
|
||||
import { layout, iOSNativeHelper } from '../../../utils';
|
||||
import { isNumber } from '../../../utils/types';
|
||||
import { IOSHelper } from './view-helper';
|
||||
import { ios as iosBackground, Background } from '../../styling/background';
|
||||
import { perspectiveProperty, visibilityProperty, opacityProperty, rotateProperty, rotateXProperty, rotateYProperty, scaleXProperty, scaleYProperty, translateXProperty, translateYProperty, zIndexProperty, backgroundInternalProperty, clipPathProperty } from '../../styling/style-properties';
|
||||
import { ios as iosBackground, Background, BackgroundClearFlags } from '../../styling/background';
|
||||
import { perspectiveProperty, visibilityProperty, opacityProperty, rotateProperty, rotateXProperty, rotateYProperty, scaleXProperty, scaleYProperty, translateXProperty, translateYProperty, zIndexProperty, backgroundInternalProperty } from '../../styling/style-properties';
|
||||
import { profile } from '../../../profiling';
|
||||
import { accessibilityEnabledProperty, accessibilityHiddenProperty, accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityLanguageProperty, accessibilityLiveRegionProperty, accessibilityMediaSessionProperty, accessibilityRoleProperty, accessibilityStateProperty, accessibilityValueProperty, accessibilityIgnoresInvertColorsProperty } from '../../../accessibility/accessibility-properties';
|
||||
import { IOSPostAccessibilityNotificationType, isAccessibilityServiceEnabled, updateAccessibilityProperties, AccessibilityEventOptions, AccessibilityRole, AccessibilityState } from '../../../accessibility';
|
||||
@ -17,6 +17,7 @@ import { CoreTypes } from '../../../core-types';
|
||||
import type { ModalTransition } from '../../transition/modal-transition';
|
||||
import { SharedTransition } from '../../transition/shared-transition';
|
||||
import { GestureStateTypes, PanGestureEventData } from '../../gestures';
|
||||
import { NativeScriptUIView } from '../../utils';
|
||||
|
||||
export * from './view-common';
|
||||
// helpers (these are okay re-exported here)
|
||||
@ -72,6 +73,12 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
this._isLaidOut = false;
|
||||
this._hasTransform = false;
|
||||
this._hasPendingTransform = false;
|
||||
|
||||
// Make sure shadows get removed
|
||||
this.style.backgroundInternal.clearFlags |= BackgroundClearFlags.CLEAR_BOX_SHADOW;
|
||||
|
||||
// Perform background cleanup
|
||||
iosBackground.clearBackgroundVisualEffects(this);
|
||||
}
|
||||
|
||||
public requestLayout(): void {
|
||||
@ -116,7 +123,8 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
this.layoutNativeView(left, top, right, bottom);
|
||||
}
|
||||
|
||||
if (boundsChanged || (this._privateFlags & PFLAG_LAYOUT_REQUIRED) === PFLAG_LAYOUT_REQUIRED) {
|
||||
const needsLayout = boundsChanged || (this._privateFlags & PFLAG_LAYOUT_REQUIRED) === PFLAG_LAYOUT_REQUIRED;
|
||||
if (needsLayout) {
|
||||
let position = { left, top, right, bottom };
|
||||
if (this.nativeViewProtected && majorVersion > 10) {
|
||||
// on iOS 11+ it is possible to have a changed layout frame due to safe area insets
|
||||
@ -129,7 +137,7 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
this._privateFlags &= ~PFLAG_LAYOUT_REQUIRED;
|
||||
}
|
||||
|
||||
this.updateBackground(sizeChanged);
|
||||
this.updateBackground(sizeChanged, needsLayout);
|
||||
if (this._hasPendingTransform) {
|
||||
this.updateNativeTransform();
|
||||
this._hasPendingTransform = false;
|
||||
@ -137,12 +145,36 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
this._privateFlags &= ~PFLAG_FORCE_LAYOUT;
|
||||
}
|
||||
|
||||
private updateBackground(sizeChanged: boolean): void {
|
||||
private updateBackground(sizeChanged: boolean, needsLayout: boolean): void {
|
||||
if (sizeChanged) {
|
||||
this._onSizeChanged();
|
||||
} else if (this._nativeBackgroundState === 'invalid') {
|
||||
const background = this.style.backgroundInternal;
|
||||
this._redrawNativeBackground(background);
|
||||
} else {
|
||||
// Update layers that don't belong to view's layer (e.g. shadow layers)
|
||||
if (needsLayout) {
|
||||
this.layoutOuterShadows();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private layoutOuterShadows(): void {
|
||||
const nativeView: NativeScriptUIView = <NativeScriptUIView>this.nativeViewProtected;
|
||||
if (nativeView) {
|
||||
const frame = nativeView.frame;
|
||||
const needsUpdate: boolean = nativeView.outerShadowContainerLayer != null;
|
||||
|
||||
if (needsUpdate) {
|
||||
CATransaction.setDisableActions(true);
|
||||
|
||||
if (nativeView.outerShadowContainerLayer) {
|
||||
nativeView.outerShadowContainerLayer.bounds = nativeView.bounds;
|
||||
nativeView.outerShadowContainerLayer.position = CGPointMake(frame.origin.x + frame.size.width / 2, frame.origin.y + frame.size.height / 2);
|
||||
}
|
||||
|
||||
CATransaction.setDisableActions(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,8 +304,8 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
this._privateFlags &= ~PFLAG_FORCE_LAYOUT;
|
||||
this.setMeasuredDimension(width, height);
|
||||
|
||||
const { sizeChanged } = this._setCurrentLayoutBounds(left, top, right, bottom);
|
||||
this.updateBackground(sizeChanged);
|
||||
const { boundsChanged, sizeChanged } = this._setCurrentLayoutBounds(left, top, right, bottom);
|
||||
this.updateBackground(sizeChanged, boundsChanged);
|
||||
this._privateFlags &= ~PFLAG_LAYOUT_REQUIRED;
|
||||
}
|
||||
|
||||
@ -365,16 +397,11 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
}
|
||||
|
||||
const background = this.style.backgroundInternal;
|
||||
const backgroundDependsOnSize = (background.image && background.image !== 'none') || !background.hasUniformBorder() || background.hasBorderRadius();
|
||||
const backgroundDependsOnSize = (background.image && background.image !== 'none') || background.clipPath || !background.hasUniformBorder() || background.hasBorderRadius() || background.hasBoxShadow();
|
||||
|
||||
if (this._nativeBackgroundState === 'invalid' || (this._nativeBackgroundState === 'drawn' && backgroundDependsOnSize)) {
|
||||
this._redrawNativeBackground(background);
|
||||
}
|
||||
|
||||
const clipPath = this.style.clipPath;
|
||||
if (clipPath !== '' && this[clipPathProperty.setNative]) {
|
||||
this[clipPathProperty.setNative](clipPath);
|
||||
}
|
||||
}
|
||||
|
||||
public updateNativeTransform() {
|
||||
@ -386,6 +413,7 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
const scaleX = this.scaleX || 1e-6;
|
||||
const scaleY = this.scaleY || 1e-6;
|
||||
const perspective = this.perspective || 300;
|
||||
const nativeView: NativeScriptUIView = <NativeScriptUIView>this.nativeViewProtected;
|
||||
|
||||
let transform = new CATransform3D(CATransform3DIdentity);
|
||||
|
||||
@ -397,13 +425,24 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
transform = CATransform3DTranslate(transform, this.translateX, this.translateY, 0);
|
||||
transform = iOSNativeHelper.applyRotateTransform(transform, this.rotateX, this.rotateY, this.rotate);
|
||||
transform = CATransform3DScale(transform, scaleX, scaleY, 1);
|
||||
if (!CATransform3DEqualToTransform(this.nativeViewProtected.layer.transform, transform)) {
|
||||
|
||||
const needsTransform: boolean = !CATransform3DEqualToTransform(this.nativeViewProtected.layer.transform, transform) || (nativeView.outerShadowContainerLayer && !CATransform3DEqualToTransform(nativeView.outerShadowContainerLayer.transform, transform));
|
||||
|
||||
if (needsTransform) {
|
||||
const updateSuspended = this._isPresentationLayerUpdateSuspended();
|
||||
if (!updateSuspended) {
|
||||
CATransaction.begin();
|
||||
}
|
||||
// Disable CALayer animatable property changes
|
||||
CATransaction.setDisableActions(true);
|
||||
|
||||
this.nativeViewProtected.layer.transform = transform;
|
||||
if (nativeView.outerShadowContainerLayer) {
|
||||
nativeView.outerShadowContainerLayer.transform = transform;
|
||||
}
|
||||
this._hasTransform = this.nativeViewProtected && !CATransform3DEqualToTransform(this.nativeViewProtected.transform3D, CATransform3DIdentity);
|
||||
|
||||
CATransaction.setDisableActions(false);
|
||||
if (!updateSuspended) {
|
||||
CATransaction.commit();
|
||||
}
|
||||
@ -721,29 +760,45 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
return this.nativeViewProtected.hidden ? CoreTypes.Visibility.collapse : CoreTypes.Visibility.visible;
|
||||
}
|
||||
[visibilityProperty.setNative](value: CoreTypes.VisibilityType) {
|
||||
const nativeView: NativeScriptUIView = <NativeScriptUIView>this.nativeViewProtected;
|
||||
|
||||
switch (value) {
|
||||
case CoreTypes.Visibility.visible:
|
||||
this.nativeViewProtected.hidden = false;
|
||||
nativeView.hidden = false;
|
||||
break;
|
||||
case CoreTypes.Visibility.hidden:
|
||||
case CoreTypes.Visibility.collapse:
|
||||
this.nativeViewProtected.hidden = true;
|
||||
nativeView.hidden = true;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid visibility value: ${value}. Valid values are: "${CoreTypes.Visibility.visible}", "${CoreTypes.Visibility.hidden}", "${CoreTypes.Visibility.collapse}".`);
|
||||
}
|
||||
|
||||
// Apply visibility value to shadows as well
|
||||
if (nativeView.outerShadowContainerLayer) {
|
||||
nativeView.outerShadowContainerLayer.hidden = nativeView.hidden;
|
||||
}
|
||||
}
|
||||
|
||||
[opacityProperty.getDefault](): number {
|
||||
return this.nativeViewProtected.alpha;
|
||||
}
|
||||
[opacityProperty.setNative](value: number) {
|
||||
const nativeView = this.nativeViewProtected;
|
||||
const nativeView: NativeScriptUIView = <NativeScriptUIView>this.nativeViewProtected;
|
||||
const updateSuspended = this._isPresentationLayerUpdateSuspended();
|
||||
if (!updateSuspended) {
|
||||
CATransaction.begin();
|
||||
}
|
||||
// Disable CALayer animatable property changes
|
||||
CATransaction.setDisableActions(true);
|
||||
|
||||
nativeView.alpha = value;
|
||||
// Apply opacity value to shadows as well
|
||||
if (nativeView.outerShadowContainerLayer) {
|
||||
nativeView.outerShadowContainerLayer.opacity = value;
|
||||
}
|
||||
|
||||
CATransaction.setDisableActions(false);
|
||||
if (!updateSuspended) {
|
||||
CATransaction.commit();
|
||||
}
|
||||
@ -899,18 +954,22 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
if (!updateSuspended) {
|
||||
CATransaction.begin();
|
||||
}
|
||||
const view = this.nativeViewProtected;
|
||||
if (view) {
|
||||
// Disable CALayer animatable property changes
|
||||
CATransaction.setDisableActions(true);
|
||||
|
||||
const nativeView = this.nativeViewProtected;
|
||||
if (nativeView) {
|
||||
if (value instanceof UIColor) {
|
||||
view.backgroundColor = value;
|
||||
nativeView.backgroundColor = value;
|
||||
} else {
|
||||
iosBackground.createBackgroundUIColor(this, (color: UIColor) => {
|
||||
view.backgroundColor = color;
|
||||
nativeView.backgroundColor = color;
|
||||
});
|
||||
this._setNativeClipToBounds();
|
||||
}
|
||||
}
|
||||
|
||||
CATransaction.setDisableActions(false);
|
||||
if (!updateSuspended) {
|
||||
CATransaction.commit();
|
||||
}
|
||||
@ -922,7 +981,7 @@ export class View extends ViewCommon implements ViewDefinition {
|
||||
const view = this.nativeViewProtected;
|
||||
if (view) {
|
||||
const backgroundInternal = this.style.backgroundInternal;
|
||||
view.clipsToBounds = (view instanceof UIScrollView || backgroundInternal.hasBorderWidth() || backgroundInternal.hasBorderRadius()) && !backgroundInternal.hasBoxShadow();
|
||||
view.clipsToBounds = view instanceof UIScrollView || backgroundInternal.hasBorderWidth() || backgroundInternal.hasBorderRadius();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ import * as am from '../../animation';
|
||||
import { AccessibilityEventOptions, AccessibilityLiveRegion, AccessibilityRole, AccessibilityState, AccessibilityTrait } from '../../../accessibility/accessibility-types';
|
||||
import { accessibilityHintProperty, accessibilityIdentifierProperty, accessibilityLabelProperty, accessibilityValueProperty, accessibilityIgnoresInvertColorsProperty } from '../../../accessibility/accessibility-properties';
|
||||
import { accessibilityBlurEvent, accessibilityFocusChangedEvent, accessibilityFocusEvent, accessibilityPerformEscapeEvent, getCurrentFontScale } from '../../../accessibility';
|
||||
import { CSSShadow } from '../../styling/css-shadow';
|
||||
import { ShadowCSSValues } from '../../styling/css-shadow';
|
||||
import { SharedTransition, SharedTransitionInteractiveOptions } from '../../transition/shared-transition';
|
||||
|
||||
// helpers (these are okay re-exported here)
|
||||
@ -646,10 +646,10 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
|
||||
this.style.backgroundRepeat = value;
|
||||
}
|
||||
|
||||
get boxShadow(): CSSShadow {
|
||||
get boxShadow(): ShadowCSSValues {
|
||||
return this.style.boxShadow;
|
||||
}
|
||||
set boxShadow(value: CSSShadow) {
|
||||
set boxShadow(value: ShadowCSSValues) {
|
||||
this.style.boxShadow = value;
|
||||
}
|
||||
|
||||
|
@ -150,14 +150,17 @@ export class Label extends TextBase implements LabelDefinition {
|
||||
|
||||
_redrawNativeBackground(value: UIColor | Background): void {
|
||||
if (value instanceof Background) {
|
||||
ios.createBackgroundUIColor(
|
||||
this,
|
||||
(color: UIColor) => {
|
||||
const cgColor = color ? color.CGColor : null;
|
||||
this.nativeTextViewProtected.layer.backgroundColor = cgColor;
|
||||
},
|
||||
true
|
||||
);
|
||||
const nativeView = this.nativeTextViewProtected;
|
||||
if (nativeView) {
|
||||
ios.createBackgroundUIColor(
|
||||
this,
|
||||
(color: UIColor) => {
|
||||
const cgColor = color ? color.CGColor : null;
|
||||
nativeView.layer.backgroundColor = cgColor;
|
||||
},
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this._setNativeClipToBounds();
|
||||
|
@ -41,7 +41,10 @@ export class RootLayout extends RootLayoutBase {
|
||||
if (options.color !== this._currentGradient) {
|
||||
this._currentGradient = options.color;
|
||||
const parsedGradient = parseLinearGradient(options.color);
|
||||
this._gradientLayer = iosViewUtils.drawGradient(view.nativeViewProtected, LinearGradient.parse(parsedGradient.value), 0);
|
||||
|
||||
this._gradientLayer = CAGradientLayer.new();
|
||||
iosViewUtils.drawGradient(view.nativeViewProtected, this._gradientLayer, LinearGradient.parse(parsedGradient.value));
|
||||
view.nativeViewProtected.layer.insertSublayerAtIndex(this._gradientLayer, 0);
|
||||
}
|
||||
}
|
||||
UIView.animateWithDurationAnimationsCompletion(
|
||||
@ -92,7 +95,10 @@ export class RootLayout extends RootLayoutBase {
|
||||
|
||||
protected _cleanupPlatformShadeCover(): void {
|
||||
this._currentGradient = null;
|
||||
this._gradientLayer = null;
|
||||
if (this._gradientLayer != null) {
|
||||
this._gradientLayer.removeFromSuperlayer();
|
||||
this._gradientLayer = null;
|
||||
}
|
||||
}
|
||||
|
||||
private _applyAnimationProperties(view: View, shadeCoverAnimation: TransitionAnimation): void {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ListPickerBase, selectedIndexProperty, itemsProperty, ItemsSource } from './list-picker-common';
|
||||
import { Color } from '../../color';
|
||||
import { backgroundColorProperty, colorProperty } from '../styling/style-properties';
|
||||
import { colorProperty } from '../styling/style-properties';
|
||||
import { profile } from '../../profiling';
|
||||
|
||||
export * from './list-picker-common';
|
||||
@ -52,13 +52,6 @@ export class ListPicker extends ListPickerBase {
|
||||
selectedIndexProperty.coerce(this);
|
||||
}
|
||||
|
||||
[backgroundColorProperty.getDefault](): UIColor {
|
||||
return this.ios.backgroundColor;
|
||||
}
|
||||
[backgroundColorProperty.setNative](value: UIColor | Color) {
|
||||
this.ios.backgroundColor = value instanceof Color ? value.ios : value;
|
||||
}
|
||||
|
||||
[colorProperty.getDefault](): UIColor {
|
||||
return this.ios.tintColor;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { CoreTypes } from '../../core-types';
|
||||
import { LinearGradient } from './linear-gradient';
|
||||
// Types.
|
||||
import { Color } from '../../color';
|
||||
import { CSSShadow } from './css-shadow';
|
||||
import { BoxShadow } from './box-shadow';
|
||||
|
||||
/**
|
||||
* Flags used to hint the background handler if it has to clear a specific property
|
||||
@ -40,7 +40,7 @@ export class Background {
|
||||
public borderBottomLeftRadius = 0;
|
||||
public borderBottomRightRadius = 0;
|
||||
public clipPath: string;
|
||||
public boxShadow: CSSShadow;
|
||||
public boxShadow: BoxShadow;
|
||||
public clearFlags: number = BackgroundClearFlags.NONE;
|
||||
|
||||
private clone(): Background {
|
||||
@ -199,7 +199,7 @@ export class Background {
|
||||
return clone;
|
||||
}
|
||||
|
||||
public withBoxShadow(value: CSSShadow): Background {
|
||||
public withBoxShadow(value: BoxShadow): Background {
|
||||
const clone = this.clone();
|
||||
clone.boxShadow = value;
|
||||
if (!value) {
|
||||
@ -266,6 +266,10 @@ export class Background {
|
||||
return this.borderTopLeftRadius > 0 || this.borderTopRightRadius > 0 || this.borderBottomRightRadius > 0 || this.borderBottomLeftRadius > 0;
|
||||
}
|
||||
|
||||
public hasBorder(): boolean {
|
||||
return (this.hasBorderColor() && this.hasBorderWidth()) || this.hasBorderRadius();
|
||||
}
|
||||
|
||||
public hasUniformBorderColor(): boolean {
|
||||
return Color.equals(this.borderTopColor, this.borderRightColor) && Color.equals(this.borderTopColor, this.borderBottomColor) && Color.equals(this.borderTopColor, this.borderLeftColor);
|
||||
}
|
||||
@ -310,7 +314,7 @@ export class Background {
|
||||
return !!this.boxShadow;
|
||||
}
|
||||
|
||||
public getBoxShadow(): CSSShadow {
|
||||
public getBoxShadow(): BoxShadow {
|
||||
return this.boxShadow;
|
||||
}
|
||||
|
||||
|
17
packages/core/ui/styling/background.d.ts
vendored
17
packages/core/ui/styling/background.d.ts
vendored
@ -2,7 +2,7 @@ import { Color } from '../../color';
|
||||
import { View } from '../core/view';
|
||||
import { BackgroundRepeat } from '../../css/parser';
|
||||
import { LinearGradient } from '../styling/linear-gradient';
|
||||
import { CSSShadow } from './css-shadow';
|
||||
import { BoxShadow } from './box-shadow';
|
||||
|
||||
export * from './background-common';
|
||||
|
||||
@ -32,7 +32,7 @@ export enum CacheMode {
|
||||
// public borderBottomRightRadius: number;
|
||||
// public borderBottomLeftRadius: number;
|
||||
// public clipPath: string;
|
||||
// public boxShadow: string | CSSShadow;
|
||||
// public boxShadow: string | BoxShadow;
|
||||
// public clearFlags: number;
|
||||
|
||||
// public withColor(value: Color): Background;
|
||||
@ -53,7 +53,7 @@ export enum CacheMode {
|
||||
// public withBorderBottomRightRadius(value: number): Background;
|
||||
// public withBorderBottomLeftRadius(value: number): Background;
|
||||
// public withClipPath(value: string): Background;
|
||||
// public withBoxShadow(value: CSSShadow): Background;
|
||||
// public withBoxShadow(value: BoxShadow): Background;
|
||||
|
||||
// public isEmpty(): boolean;
|
||||
|
||||
@ -62,6 +62,7 @@ export enum CacheMode {
|
||||
// public hasBorderColor(): boolean;
|
||||
// public hasBorderWidth(): boolean;
|
||||
// public hasBorderRadius(): boolean;
|
||||
// public hasBorder(): boolean;
|
||||
// public hasUniformBorderColor(): boolean;
|
||||
// public hasUniformBorderWidth(): boolean;
|
||||
// public hasUniformBorderRadius(): boolean;
|
||||
@ -70,11 +71,19 @@ export enum CacheMode {
|
||||
// public getUniformBorderWidth(): number;
|
||||
// public getUniformBorderRadius(): number;
|
||||
// public hasBoxShadow(): boolean;
|
||||
// public getBoxShadow(): CSSShadow;
|
||||
// public getBoxShadow(): BoxShadow;
|
||||
// }
|
||||
|
||||
export namespace ios {
|
||||
export function createBackgroundUIColor(view: View, callback: (uiColor: any /* UIColor */) => void, flip?: boolean): void;
|
||||
export function drawBackgroundVisualEffects(view: View): void;
|
||||
export function clearBackgroundVisualEffects(view: View): void;
|
||||
export function generateClipPath(view: View, bounds: CGRect): any;
|
||||
export function generateShadowLayerPaths(view: View, bounds: CGRect): { maskPath: any; shadowPath: any };
|
||||
export function getUniformBorderRadius(view: View, bounds: CGRect): number;
|
||||
export function generateNonUniformBorderInnerClipRoundedPath(view: View, bounds: CGRect): any;
|
||||
export function generateNonUniformBorderOuterClipRoundedPath(view: View, bounds: CGRect): any;
|
||||
export function generateNonUniformMultiColorBorderRoundedPaths(view: View, bounds: CGRect): Array<any>;
|
||||
}
|
||||
|
||||
export namespace ad {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,7 @@
|
||||
import { Color } from '../../color';
|
||||
|
||||
export class BoxShadow {
|
||||
public inset: boolean;
|
||||
public offsetX: number;
|
||||
public offsetY: number;
|
||||
public blurRadius: number;
|
||||
|
@ -2,7 +2,7 @@ import { CoreTypes } from '../../core-types';
|
||||
import { Color } from '../../color';
|
||||
import { Length } from './style-properties';
|
||||
|
||||
export interface CSSShadow {
|
||||
export interface ShadowCSSValues {
|
||||
inset: boolean;
|
||||
offsetX: CoreTypes.LengthType;
|
||||
offsetY: CoreTypes.LengthType;
|
||||
@ -27,14 +27,14 @@ const LENGTH_RE = /^-?[0-9]+[a-zA-Z%]*?$/;
|
||||
const isLength = (v) => v === '0' || LENGTH_RE.test(v);
|
||||
|
||||
/**
|
||||
* Parse a string into a CSSShadow
|
||||
* Parse a string into ShadowCSSValues
|
||||
* Supports any valid css box/text shadow combination.
|
||||
*
|
||||
* inspired by https://github.com/jxnblk/css-box-shadow/blob/master/index.js (MIT License)
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
export function parseCSSShadow(value: string): CSSShadow {
|
||||
export function parseCSSShadow(value: string): ShadowCSSValues {
|
||||
const parts = value.trim().split(PARTS_RE);
|
||||
const inset = parts.includes('inset');
|
||||
const first = parts[0];
|
||||
|
@ -16,7 +16,7 @@ import { CoreTypes } from '../../core-types';
|
||||
|
||||
import { parseBackground } from '../../css/parser';
|
||||
import { LinearGradient } from './linear-gradient';
|
||||
import { CSSShadow, parseCSSShadow } from './css-shadow';
|
||||
import { parseCSSShadow, ShadowCSSValues } from './css-shadow';
|
||||
|
||||
function equalsCommon(a: CoreTypes.LengthType, b: CoreTypes.LengthType): boolean;
|
||||
function equalsCommon(a: CoreTypes.PercentLengthType, b: CoreTypes.PercentLengthType): boolean;
|
||||
@ -1226,11 +1226,22 @@ export const borderBottomLeftRadiusProperty = new CssProperty<Style, CoreTypes.L
|
||||
});
|
||||
borderBottomLeftRadiusProperty.register(Style);
|
||||
|
||||
const boxShadowProperty = new CssProperty<Style, CSSShadow>({
|
||||
const boxShadowProperty = new CssProperty<Style, ShadowCSSValues>({
|
||||
name: 'boxShadow',
|
||||
cssName: 'box-shadow',
|
||||
valueChanged: (target, oldValue, newValue) => {
|
||||
target.backgroundInternal = target.backgroundInternal.withBoxShadow(newValue);
|
||||
target.backgroundInternal = target.backgroundInternal.withBoxShadow(
|
||||
newValue
|
||||
? {
|
||||
inset: newValue.inset,
|
||||
offsetX: Length.toDevicePixels(newValue.offsetX, 0),
|
||||
offsetY: Length.toDevicePixels(newValue.offsetY, 0),
|
||||
blurRadius: Length.toDevicePixels(newValue.blurRadius, 0),
|
||||
spreadRadius: Length.toDevicePixels(newValue.spreadRadius, 0),
|
||||
color: newValue.color,
|
||||
}
|
||||
: null
|
||||
);
|
||||
},
|
||||
valueConverter: (value) => {
|
||||
return parseCSSShadow(value);
|
||||
|
@ -10,7 +10,7 @@ import { FlexDirection, FlexWrap, JustifyContent, AlignItems, AlignContent, Orde
|
||||
import { Trace } from '../../../trace';
|
||||
import { CoreTypes } from '../../../core-types';
|
||||
import { AccessibilityLiveRegion, AccessibilityRole, AccessibilityState } from '../../../accessibility/accessibility-types';
|
||||
import { CSSShadow } from '../css-shadow';
|
||||
import { ShadowCSSValues } from '../css-shadow';
|
||||
|
||||
export interface CommonLayoutParams {
|
||||
width: number;
|
||||
@ -149,7 +149,7 @@ export class Style extends Observable implements StyleDefinition {
|
||||
public borderBottomRightRadius: CoreTypes.LengthType;
|
||||
public borderBottomLeftRadius: CoreTypes.LengthType;
|
||||
|
||||
public boxShadow: CSSShadow;
|
||||
public boxShadow: ShadowCSSValues;
|
||||
|
||||
public fontSize: number;
|
||||
public fontFamily: string;
|
||||
@ -170,7 +170,7 @@ export class Style extends Observable implements StyleDefinition {
|
||||
public textAlignment: CoreTypes.TextAlignmentType;
|
||||
public textDecoration: CoreTypes.TextDecorationType;
|
||||
public textTransform: CoreTypes.TextTransformType;
|
||||
public textShadow: CSSShadow;
|
||||
public textShadow: ShadowCSSValues;
|
||||
public whiteSpace: CoreTypes.WhiteSpaceType;
|
||||
public textOverflow: CoreTypes.TextOverflowType;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Types
|
||||
import { getClosestPropertyValue, maxLinesProperty, textOverflowProperty } from './text-base-common';
|
||||
import { CSSShadow } from '../styling/css-shadow';
|
||||
import { ShadowCSSValues } from '../styling/css-shadow';
|
||||
|
||||
// Requires
|
||||
import { Font } from '../styling/font';
|
||||
@ -419,7 +419,7 @@ export class TextBase extends TextBaseCommon {
|
||||
};
|
||||
}
|
||||
|
||||
[textShadowProperty.setNative](value: CSSShadow) {
|
||||
[textShadowProperty.setNative](value: ShadowCSSValues) {
|
||||
// prettier-ignore
|
||||
this.nativeTextViewProtected.setShadowLayer(
|
||||
Length.toDevicePixels(value.blurRadius, java.lang.Float.MIN_VALUE),
|
||||
|
6
packages/core/ui/text-base/index.d.ts
vendored
6
packages/core/ui/text-base/index.d.ts
vendored
@ -4,7 +4,7 @@ import { Style } from '../styling/style';
|
||||
import { Length } from '../styling/style-properties';
|
||||
import { Property, CssProperty, InheritedCssProperty } from '../core/properties';
|
||||
import { CoreTypes } from '../../core-types';
|
||||
import { CSSShadow } from '../styling/css-shadow';
|
||||
import { ShadowCSSValues } from '../styling/css-shadow';
|
||||
|
||||
export class TextBase extends View implements AddChildFromBuilder {
|
||||
/**
|
||||
@ -56,7 +56,7 @@ export class TextBase extends View implements AddChildFromBuilder {
|
||||
/**
|
||||
* Gets or sets text shadow style property.
|
||||
*/
|
||||
textShadow: CSSShadow;
|
||||
textShadow: ShadowCSSValues;
|
||||
|
||||
/**
|
||||
* Gets or sets white space style property.
|
||||
@ -137,7 +137,7 @@ export const maxLinesProperty: InheritedCssProperty<Style, number>;
|
||||
export const textAlignmentProperty: InheritedCssProperty<Style, CoreTypes.TextAlignmentType>;
|
||||
export const textDecorationProperty: CssProperty<Style, CoreTypes.TextDecorationType>;
|
||||
export const textTransformProperty: CssProperty<Style, CoreTypes.TextTransformType>;
|
||||
export const textShadowProperty: CssProperty<Style, CSSShadow>;
|
||||
export const textShadowProperty: CssProperty<Style, ShadowCSSValues>;
|
||||
export const whiteSpaceProperty: CssProperty<Style, CoreTypes.WhiteSpaceType>;
|
||||
export const textOverflowProperty: CssProperty<Style, CoreTypes.TextOverflowType>;
|
||||
export const letterSpacingProperty: CssProperty<Style, number>;
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Types
|
||||
import { getClosestPropertyValue } from './text-base-common';
|
||||
import { CSSShadow } from '../styling/css-shadow';
|
||||
import { ShadowCSSValues } from '../styling/css-shadow';
|
||||
|
||||
// Requires
|
||||
import { Font } from '../styling/font';
|
||||
@ -11,7 +11,7 @@ import { FormattedString } from './formatted-string';
|
||||
import { Span } from './span';
|
||||
import { colorProperty, fontInternalProperty, fontScaleInternalProperty, Length } from '../styling/style-properties';
|
||||
import { isString, isNullOrUndefined } from '../../utils/types';
|
||||
import { iOSNativeHelper } from '../../utils';
|
||||
import { iOSNativeHelper, layout } from '../../utils';
|
||||
import { Trace } from '../../trace';
|
||||
import { CoreTypes } from '../../core-types';
|
||||
|
||||
@ -255,7 +255,7 @@ export class TextBase extends TextBaseCommon {
|
||||
this._setNativeText();
|
||||
}
|
||||
|
||||
[textShadowProperty.setNative](value: CSSShadow) {
|
||||
[textShadowProperty.setNative](value: ShadowCSSValues) {
|
||||
this._setShadow(value);
|
||||
}
|
||||
|
||||
@ -404,12 +404,8 @@ export class TextBase extends TextBaseCommon {
|
||||
}
|
||||
}
|
||||
|
||||
_setShadow(value: CSSShadow): void {
|
||||
const layer = iOSNativeHelper.getShadowLayer(this.nativeTextViewProtected, 'ns-text-shadow');
|
||||
if (!layer) {
|
||||
Trace.write('text-shadow not applied, no layer.', Trace.categories.Style, Trace.messageType.info);
|
||||
return;
|
||||
}
|
||||
_setShadow(value: ShadowCSSValues): void {
|
||||
const layer: CALayer = this.nativeTextViewProtected.layer;
|
||||
|
||||
if (isNullOrUndefined(value)) {
|
||||
// clear the text shadow
|
||||
@ -421,14 +417,14 @@ export class TextBase extends TextBaseCommon {
|
||||
}
|
||||
|
||||
// shadow opacity is handled on the shadow's color instance
|
||||
layer.shadowOpacity = value.color?.a ? value.color?.a / 255 : 1;
|
||||
layer.shadowOpacity = value.color?.a ? value.color.a / 255 : 1;
|
||||
layer.shadowColor = value.color.ios.CGColor;
|
||||
layer.shadowRadius = Length.toDevicePixels(value.blurRadius, 0.0);
|
||||
layer.shadowRadius = layout.toDeviceIndependentPixels(Length.toDevicePixels(value.blurRadius, 0));
|
||||
|
||||
// prettier-ignore
|
||||
layer.shadowOffset = CGSizeMake(
|
||||
Length.toDevicePixels(value.offsetX, 0.0),
|
||||
Length.toDevicePixels(value.offsetY, 0.0)
|
||||
layout.toDeviceIndependentPixels(Length.toDevicePixels(value.offsetX, 0)),
|
||||
layout.toDeviceIndependentPixels(Length.toDevicePixels(value.offsetY, 0))
|
||||
);
|
||||
|
||||
layer.masksToBounds = false;
|
||||
|
@ -13,7 +13,7 @@ import { Observable } from '../../data/observable';
|
||||
import { CoreTypes } from '../../core-types';
|
||||
import { TextBase as TextBaseDefinition } from '.';
|
||||
import { Color } from '../../color';
|
||||
import { CSSShadow, parseCSSShadow } from '../styling/css-shadow';
|
||||
import { ShadowCSSValues, parseCSSShadow } from '../styling/css-shadow';
|
||||
|
||||
const CHILD_SPAN = 'Span';
|
||||
const CHILD_FORMATTED_TEXT = 'formattedText';
|
||||
@ -118,10 +118,10 @@ export abstract class TextBaseCommon extends View implements TextBaseDefinition
|
||||
this.style.textTransform = value;
|
||||
}
|
||||
|
||||
get textShadow(): CSSShadow {
|
||||
get textShadow(): ShadowCSSValues {
|
||||
return this.style.textShadow;
|
||||
}
|
||||
set textShadow(value: CSSShadow) {
|
||||
set textShadow(value: ShadowCSSValues) {
|
||||
this.style.textShadow = value;
|
||||
}
|
||||
|
||||
@ -278,7 +278,7 @@ export const textTransformProperty = new CssProperty<Style, CoreTypes.TextTransf
|
||||
});
|
||||
textTransformProperty.register(Style);
|
||||
|
||||
export const textShadowProperty = new CssProperty<Style, string | CSSShadow>({
|
||||
export const textShadowProperty = new CssProperty<Style, string | ShadowCSSValues>({
|
||||
name: 'textShadow',
|
||||
cssName: 'text-shadow',
|
||||
affectsLayout: global.isIOS,
|
||||
|
35
packages/core/ui/utils.d.ts
vendored
35
packages/core/ui/utils.d.ts
vendored
@ -1,19 +1,22 @@
|
||||
export interface NativeScriptUIView extends UIView {
|
||||
hasNonUniformBorder: boolean;
|
||||
borderLayer: CALayer;
|
||||
hasNonUniformBorderColor: boolean;
|
||||
borderLayer: CAShapeLayer;
|
||||
|
||||
hasBorderMask: boolean;
|
||||
borderOriginalMask: CALayer;
|
||||
|
||||
topBorderLayer: CALayer;
|
||||
rightBorderLayer: CALayer;
|
||||
bottomBorderLayer: CALayer;
|
||||
leftBorderLayer: CALayer;
|
||||
maskType: ios.LayerMaskType;
|
||||
originalMask: CALayer;
|
||||
|
||||
gradientLayer: CAGradientLayer;
|
||||
boxShadowLayer: CALayer;
|
||||
outerShadowContainerLayer: CALayer;
|
||||
}
|
||||
|
||||
export namespace ios {
|
||||
export type LayerMaskType = 'BORDER' | 'CLIP_PATH';
|
||||
export namespace LayerMask {
|
||||
export const BORDER = 'BORDER';
|
||||
export const CLIP_PATH = 'CLIP_PATH';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets actual height of a [UIView](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/) widget in device pixels.
|
||||
* @param uiView - An instance of UIView.
|
||||
@ -27,17 +30,11 @@ export namespace ios {
|
||||
export function getStatusBarHeight(viewController?: any): number;
|
||||
|
||||
/**
|
||||
* draw gradient using CAGradientLayer and insert into UIView sublayer
|
||||
* @param nativeView UIView
|
||||
* Draw gradient using CAGradientLayer and insert into UIView sublayer
|
||||
* @param nativeView NativeScriptUIView
|
||||
* @param gradientLayer CAGradientLayer
|
||||
* @param gradient Parsed LinearGradient
|
||||
* @param gradientLayerOpacity Initial layer opacity (in case you'd like to use with animation sequence)
|
||||
* @param index sublayer index to insert layer at (defaults to 0)
|
||||
*/
|
||||
export function drawGradient(uiView: any /* UIView */, gradient: LinearGradient, gradientLayerOpacity?: number, index?: number): any; /* CAGradientLayer */
|
||||
|
||||
/**
|
||||
* clear gradientLayer if found on provided UIView
|
||||
* @param nativeView UIView
|
||||
*/
|
||||
export function clearGradient(uiView: any /* UIView */): void;
|
||||
export function drawGradient(uiView: NativeScriptUIView, gradientLayer: CAGradientLayer, gradient: LinearGradient, gradientLayerOpacity?: number): void;
|
||||
}
|
||||
|
@ -1,23 +1,26 @@
|
||||
import * as utils from '../utils';
|
||||
import { Screen } from '../platform';
|
||||
import * as utils from '../utils';
|
||||
import { LinearGradient } from './styling/linear-gradient';
|
||||
|
||||
interface NativeScriptUIView extends UIView {
|
||||
hasNonUniformBorder: boolean;
|
||||
borderLayer: CALayer;
|
||||
hasNonUniformBorderColor: boolean;
|
||||
borderLayer: CAShapeLayer;
|
||||
|
||||
hasBorderMask: boolean;
|
||||
borderOriginalMask: CALayer;
|
||||
|
||||
topBorderLayer: CALayer;
|
||||
rightBorderLayer: CALayer;
|
||||
bottomBorderLayer: CALayer;
|
||||
leftBorderLayer: CALayer;
|
||||
maskType: ios.LayerMaskType;
|
||||
originalMask: CALayer;
|
||||
|
||||
gradientLayer: CAGradientLayer;
|
||||
boxShadowLayer: CALayer;
|
||||
outerShadowContainerLayer: CALayer;
|
||||
}
|
||||
|
||||
export namespace ios {
|
||||
export type LayerMaskType = 'BORDER' | 'CLIP_PATH';
|
||||
export namespace LayerMask {
|
||||
export const BORDER = 'BORDER';
|
||||
export const CLIP_PATH = 'CLIP_PATH';
|
||||
}
|
||||
|
||||
export function getActualHeight(view: UIView): number {
|
||||
if (view.window && !view.hidden) {
|
||||
return utils.layout.toDevicePixels(view.frame.size.height);
|
||||
@ -42,50 +45,46 @@ export namespace ios {
|
||||
return utils.layout.toDevicePixels(min);
|
||||
}
|
||||
|
||||
export function drawGradient(nativeView: NativeScriptUIView, gradient: LinearGradient, gradientLayerOpacity?: number, index?: number): CAGradientLayer {
|
||||
let gradientLayer: CAGradientLayer;
|
||||
if (nativeView && gradient) {
|
||||
gradientLayer = CAGradientLayer.layer();
|
||||
if (typeof gradientLayerOpacity === 'number') {
|
||||
gradientLayer.opacity = gradientLayerOpacity;
|
||||
}
|
||||
gradientLayer.frame = nativeView.bounds;
|
||||
nativeView.gradientLayer = gradientLayer;
|
||||
|
||||
const iosColors = NSMutableArray.alloc().initWithCapacity(gradient.colorStops.length);
|
||||
const iosStops = NSMutableArray.alloc<number>().initWithCapacity(gradient.colorStops.length);
|
||||
let hasStops = false;
|
||||
|
||||
gradient.colorStops.forEach((stop) => {
|
||||
iosColors.addObject(stop.color.ios.CGColor);
|
||||
if (stop.offset) {
|
||||
iosStops.addObject(stop.offset.value);
|
||||
hasStops = true;
|
||||
}
|
||||
});
|
||||
|
||||
gradientLayer.colors = iosColors;
|
||||
|
||||
if (hasStops) {
|
||||
gradientLayer.locations = iosStops;
|
||||
}
|
||||
|
||||
const alpha = gradient.angle / (Math.PI * 2);
|
||||
const startX = Math.pow(Math.sin(Math.PI * (alpha + 0.75)), 2);
|
||||
const startY = Math.pow(Math.sin(Math.PI * (alpha + 0.5)), 2);
|
||||
const endX = Math.pow(Math.sin(Math.PI * (alpha + 0.25)), 2);
|
||||
const endY = Math.pow(Math.sin(Math.PI * alpha), 2);
|
||||
gradientLayer.startPoint = { x: startX, y: startY };
|
||||
gradientLayer.endPoint = { x: endX, y: endY };
|
||||
|
||||
nativeView.layer.insertSublayerAtIndex(gradientLayer, index || 0);
|
||||
export function drawGradient(nativeView: NativeScriptUIView, gradientLayer: CAGradientLayer, gradient: LinearGradient, gradientLayerOpacity?: number): void {
|
||||
if (!nativeView || !gradient) {
|
||||
return;
|
||||
}
|
||||
return gradientLayer;
|
||||
}
|
||||
|
||||
export function clearGradient(nativeView: NativeScriptUIView): void {
|
||||
if (nativeView?.gradientLayer) {
|
||||
nativeView.gradientLayer.removeFromSuperlayer();
|
||||
if (typeof gradientLayerOpacity === 'number') {
|
||||
gradientLayer.opacity = gradientLayerOpacity;
|
||||
}
|
||||
|
||||
// Update these properties instead of layer frame as the latter messes with animations
|
||||
gradientLayer.bounds = nativeView.bounds;
|
||||
gradientLayer.anchorPoint = CGPointMake(0, 0);
|
||||
|
||||
gradientLayer.allowsEdgeAntialiasing = true;
|
||||
gradientLayer.contentsScale = Screen.mainScreen.scale;
|
||||
|
||||
const iosColors = NSMutableArray.alloc().initWithCapacity(gradient.colorStops.length);
|
||||
const iosStops = NSMutableArray.alloc<number>().initWithCapacity(gradient.colorStops.length);
|
||||
let hasStops = false;
|
||||
|
||||
gradient.colorStops.forEach((stop) => {
|
||||
iosColors.addObject(stop.color.ios.CGColor);
|
||||
if (stop.offset) {
|
||||
iosStops.addObject(stop.offset.value);
|
||||
hasStops = true;
|
||||
}
|
||||
});
|
||||
|
||||
gradientLayer.colors = iosColors;
|
||||
|
||||
if (hasStops) {
|
||||
gradientLayer.locations = iosStops;
|
||||
}
|
||||
|
||||
const alpha = gradient.angle / (Math.PI * 2);
|
||||
const startX = Math.pow(Math.sin(Math.PI * (alpha + 0.75)), 2);
|
||||
const startY = Math.pow(Math.sin(Math.PI * (alpha + 0.5)), 2);
|
||||
const endX = Math.pow(Math.sin(Math.PI * (alpha + 0.25)), 2);
|
||||
const endY = Math.pow(Math.sin(Math.PI * alpha), 2);
|
||||
gradientLayer.startPoint = { x: startX, y: startY };
|
||||
gradientLayer.endPoint = { x: endX, y: endY };
|
||||
}
|
||||
}
|
||||
|
@ -149,88 +149,6 @@ export function applyRotateTransform(transform: CATransform3D, x: number, y: num
|
||||
return transform;
|
||||
}
|
||||
|
||||
export function getShadowLayer(nativeView: UIView, name: string = 'ns-shadow-layer', create: boolean = true): CALayer {
|
||||
return nativeView.layer;
|
||||
|
||||
console.log(`--- ${create ? 'CREATE' : 'READ'}`);
|
||||
|
||||
/**
|
||||
* UIView
|
||||
* -> Shadow
|
||||
*
|
||||
*
|
||||
* UIView
|
||||
* -> UIView
|
||||
* -> Shadow
|
||||
*/
|
||||
|
||||
if (!nativeView) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!nativeView.layer) {
|
||||
// should never hit this?
|
||||
console.log('- no layer! -');
|
||||
return null;
|
||||
}
|
||||
|
||||
// if the nativeView's layer is the shadow layer?
|
||||
if (nativeView.layer.name === name) {
|
||||
console.log('- found shadow layer - reusing.');
|
||||
return nativeView.layer;
|
||||
}
|
||||
|
||||
console.log('>> layer :', nativeView.layer);
|
||||
if (nativeView.layer.sublayers?.count) {
|
||||
const count = nativeView.layer.sublayers.count;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const subLayer = nativeView.layer.sublayers.objectAtIndex(i);
|
||||
|
||||
console.log(`>> subLayer ${i + 1}/${count} :`, subLayer);
|
||||
console.log(`>> subLayer ${i + 1}/${count} name :`, subLayer.name);
|
||||
|
||||
if (subLayer.name === name) {
|
||||
console.log('- found shadow sublayer - reusing.');
|
||||
return subLayer;
|
||||
}
|
||||
}
|
||||
// if (nativeView instanceof UITextView) {
|
||||
// return nativeView.layer.sublayers.objectAtIndex(1);
|
||||
// } else {
|
||||
// return nativeView.layer.sublayers.objectAtIndex(nativeView.layer.sublayers.count - 1);
|
||||
// }
|
||||
}
|
||||
// else {
|
||||
// layer = nativeView.layer;
|
||||
// }
|
||||
|
||||
// we're not interested in creating a new layer
|
||||
if (!create) {
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log(`- adding a new layer for - ${name}`);
|
||||
|
||||
const viewLayer = nativeView.layer;
|
||||
const newLayer = CALayer.layer();
|
||||
|
||||
newLayer.name = name;
|
||||
newLayer.zPosition = 0.0;
|
||||
// nativeView.layer.insertSublayerBelow(newLayer, nativeView.layer)
|
||||
// newLayer.insertSublayerAtIndex(nativeView.layer, 0)
|
||||
// nativeView.layer.zPosition = 1.0;
|
||||
// nativeView.layer.addSublayer(newLayer);
|
||||
|
||||
// nativeView.layer = CALayer.layer()
|
||||
|
||||
nativeView.layer.insertSublayerAtIndex(newLayer, 0);
|
||||
// nativeView.layer.insertSublayerAtIndex(viewLayer, 1)
|
||||
|
||||
// nativeView.layer.replaceSublayerWith(newLayer, nativeView.layer);
|
||||
|
||||
return newLayer;
|
||||
}
|
||||
|
||||
export function createUIDocumentInteractionControllerDelegate(): NSObject {
|
||||
@NativeClass
|
||||
class UIDocumentInteractionControllerDelegateImpl extends NSObject implements UIDocumentInteractionControllerDelegate {
|
||||
|
7
packages/core/utils/native-helper.d.ts
vendored
7
packages/core/utils/native-helper.d.ts
vendored
@ -216,13 +216,6 @@ export function dataDeserialize(nativeData?: any): any;
|
||||
// */
|
||||
// export function applyRotateTransform(transform: any /* CATransform3D*/, x: number, y: number, z: number): any; /* CATransform3D*/
|
||||
|
||||
// /**
|
||||
// * @param nativeView UIView to find shadow layer with
|
||||
// * @param name Name of the shadow layer if looking for specifically named layer
|
||||
// * @param create should we create a new layer if not found
|
||||
// */
|
||||
// export function getShadowLayer(nativeView: any /* UIView */, name?: string, create?: boolean): any; /* CALayer */
|
||||
|
||||
// /**
|
||||
// * Create a UIDocumentInteractionControllerDelegate implementation for use with UIDocumentInteractionController
|
||||
// */
|
||||
|
@ -44,3 +44,24 @@ export const degreesToRadians = (a: number) => a * (Math.PI / 180);
|
||||
export function valueMap(val: number, in_min: number, in_max: number, out_min: number, out_max: number) {
|
||||
return ((val - in_min) * (out_max - out_min)) / (in_max - in_min) + out_min;
|
||||
}
|
||||
|
||||
/**
|
||||
* A method that calculates the target X based on the angle of 2 points and target Y.
|
||||
*
|
||||
* @param x1
|
||||
* @param y1
|
||||
* @param x2
|
||||
* @param y2
|
||||
* @param yTarget
|
||||
* @returns
|
||||
*/
|
||||
export function extendPointsToTargetY(x1: number, y1: number, x2: number, y2: number, yTarget: number) {
|
||||
const deltaX: number = x2 - x1;
|
||||
const deltaY: number = y2 - y1;
|
||||
const angleRadians: number = Math.atan2(deltaY, deltaX);
|
||||
|
||||
const targetDeltaY: number = yTarget - y1;
|
||||
const targetDeltaX: number = targetDeltaY / Math.tan(angleRadians);
|
||||
|
||||
return x1 + targetDeltaX;
|
||||
}
|
||||
|
Reference in New Issue
Block a user