mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 19:26:42 +08:00
fix: safeguards against invalid values
This commit is contained in:

committed by
Nathan Walker

parent
1dfa5a6025
commit
f5db58414a
@ -11,13 +11,17 @@ export class BoxShadowModel extends Observable {
|
|||||||
private _selectedBackgroundType: string;
|
private _selectedBackgroundType: string;
|
||||||
private _selectedBorderType: string;
|
private _selectedBorderType: string;
|
||||||
private _selectedAnimation: string;
|
private _selectedAnimation: string;
|
||||||
private _boxShadow: string;
|
private _boxShadow: string = '5 5 1 1 rgba(255, 0, 0, .9)';
|
||||||
|
|
||||||
background: string;
|
background: string;
|
||||||
borderWidth: number;
|
borderWidth: number;
|
||||||
borderRadius: number;
|
borderRadius: number;
|
||||||
appliedBoxShadow: string;
|
appliedBoxShadow: string;
|
||||||
|
|
||||||
|
get boxShadow(): string {
|
||||||
|
return this._boxShadow;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -9,14 +9,14 @@
|
|||||||
<!-- layouts -->
|
<!-- layouts -->
|
||||||
<ScrollView height="100%" visibility="{{ selectedComponentType === 'layouts' ? 'visible' : 'collapsed' }}">
|
<ScrollView height="100%" visibility="{{ selectedComponentType === 'layouts' ? 'visible' : 'collapsed' }}">
|
||||||
<StackLayout>
|
<StackLayout>
|
||||||
<StackLayout
|
<StackLayout
|
||||||
width="300"
|
width="300"
|
||||||
height="100"
|
height="100"
|
||||||
class="demo-component"
|
class="demo-component"
|
||||||
boxShadow="{{ appliedBoxShadow }}"
|
boxShadow="{{ appliedBoxShadow }}"
|
||||||
borderWidth="{{ borderWidth }}"
|
borderWidth="{{ borderWidth }}"
|
||||||
borderRadius="{{ borderRadius }}"
|
borderRadius="{{ borderRadius }}"
|
||||||
backgroundColor="{{ background }}"
|
background="{{ background }}"
|
||||||
tap="{{ toggleAnimation }}"
|
tap="{{ toggleAnimation }}"
|
||||||
>
|
>
|
||||||
<Label text="StackLayout"></Label>
|
<Label text="StackLayout"></Label>
|
||||||
@ -29,7 +29,7 @@
|
|||||||
boxShadow="{{ appliedBoxShadow }}"
|
boxShadow="{{ appliedBoxShadow }}"
|
||||||
borderWidth="{{ borderWidth }}"
|
borderWidth="{{ borderWidth }}"
|
||||||
borderRadius="{{ borderRadius }}"
|
borderRadius="{{ borderRadius }}"
|
||||||
backgroundColor="{{ background }}"
|
background="{{ background }}"
|
||||||
tap="{{ toggleAnimation }}"
|
tap="{{ toggleAnimation }}"
|
||||||
>
|
>
|
||||||
<Label text="GridLayout"></Label>
|
<Label text="GridLayout"></Label>
|
||||||
@ -42,7 +42,7 @@
|
|||||||
boxShadow="{{ appliedBoxShadow }}"
|
boxShadow="{{ appliedBoxShadow }}"
|
||||||
borderWidth="{{ borderWidth }}"
|
borderWidth="{{ borderWidth }}"
|
||||||
borderRadius="{{ borderRadius }}"
|
borderRadius="{{ borderRadius }}"
|
||||||
backgroundColor="{{ background }}"
|
background="{{ background }}"
|
||||||
tap="{{ toggleAnimation }}"
|
tap="{{ toggleAnimation }}"
|
||||||
>
|
>
|
||||||
<Label text="AbsoluteLayout"></Label>
|
<Label text="AbsoluteLayout"></Label>
|
||||||
@ -55,7 +55,7 @@
|
|||||||
boxShadow="{{ appliedBoxShadow }}"
|
boxShadow="{{ appliedBoxShadow }}"
|
||||||
borderWidth="{{ borderWidth }}"
|
borderWidth="{{ borderWidth }}"
|
||||||
borderRadius="{{ borderRadius }}"
|
borderRadius="{{ borderRadius }}"
|
||||||
backgroundColor="{{ background }}"
|
background="{{ background }}"
|
||||||
tap="{{ toggleAnimation }}"
|
tap="{{ toggleAnimation }}"
|
||||||
>
|
>
|
||||||
<Label text="DockLayout"></Label>
|
<Label text="DockLayout"></Label>
|
||||||
@ -68,12 +68,28 @@
|
|||||||
boxShadow="{{ appliedBoxShadow }}"
|
boxShadow="{{ appliedBoxShadow }}"
|
||||||
borderWidth="{{ borderWidth }}"
|
borderWidth="{{ borderWidth }}"
|
||||||
borderRadius="{{ borderRadius }}"
|
borderRadius="{{ borderRadius }}"
|
||||||
backgroundColor="{{ background }}"
|
background="{{ background }}"
|
||||||
tap="{{ toggleAnimation }}"
|
tap="{{ toggleAnimation }}"
|
||||||
>
|
>
|
||||||
<Label text="FlexboxLayout"></Label>
|
<Label text="FlexboxLayout"></Label>
|
||||||
</FlexboxLayout>
|
</FlexboxLayout>
|
||||||
|
|
||||||
|
<GridLayout
|
||||||
|
width="300"
|
||||||
|
height="100"
|
||||||
|
padding="4"
|
||||||
|
boxShadow="{{ appliedBoxShadow }}"
|
||||||
|
tap="{{ toggleAnimation }}"
|
||||||
|
>
|
||||||
|
<StackLayout
|
||||||
|
borderWidth="4"
|
||||||
|
borderRadius="20"
|
||||||
|
backgroundColor="white"
|
||||||
|
>
|
||||||
|
<Label text="BorderRadius + BoxShadow on parent container"></Label>
|
||||||
|
</StackLayout>
|
||||||
|
</GridLayout>
|
||||||
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
@ -90,7 +106,7 @@
|
|||||||
boxShadow="{{ appliedBoxShadow }}"
|
boxShadow="{{ appliedBoxShadow }}"
|
||||||
borderWidth="{{ borderWidth }}"
|
borderWidth="{{ borderWidth }}"
|
||||||
borderRadius="{{ borderRadius }}"
|
borderRadius="{{ borderRadius }}"
|
||||||
backgroundColor="{{ background }}"
|
background="{{ background }}"
|
||||||
tap="{{ toggleAnimation }}"
|
tap="{{ toggleAnimation }}"
|
||||||
text="Label"></Label>
|
text="Label"></Label>
|
||||||
|
|
||||||
@ -109,13 +125,34 @@
|
|||||||
boxShadow="{{ appliedBoxShadow }}"
|
boxShadow="{{ appliedBoxShadow }}"
|
||||||
borderWidth="{{ borderWidth }}"
|
borderWidth="{{ borderWidth }}"
|
||||||
borderRadius="{{ borderRadius }}"
|
borderRadius="{{ borderRadius }}"
|
||||||
backgroundColor="{{ background }}"
|
background="{{ background }}"
|
||||||
tap="{{ toggleAnimation }}"
|
tap="{{ toggleAnimation }}"
|
||||||
text="button"
|
text="button"
|
||||||
></Button>
|
></Button>
|
||||||
|
|
||||||
</GridLayout>
|
</GridLayout>
|
||||||
|
|
||||||
|
<!-- images -->
|
||||||
|
<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>
|
||||||
|
|
||||||
|
|
||||||
|
</GridLayout>
|
||||||
|
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
<GridLayout
|
<GridLayout
|
||||||
@ -130,6 +167,7 @@
|
|||||||
<TextField
|
<TextField
|
||||||
col="1"
|
col="1"
|
||||||
placeholder="box-shadow"
|
placeholder="box-shadow"
|
||||||
|
text="{{ boxShadow }}"
|
||||||
textChange="{{ textChange }}"
|
textChange="{{ textChange }}"
|
||||||
>
|
>
|
||||||
</TextField>
|
</TextField>
|
||||||
@ -146,6 +184,7 @@
|
|||||||
<Button text="Layouts" componentType="layouts" tap="{{ selectComponentType }}"></Button>
|
<Button text="Layouts" componentType="layouts" tap="{{ selectComponentType }}"></Button>
|
||||||
<Button text="Labels" componentType="labels" selectedAttr="{{ selectedComponentType }}" 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="Buttons" componentType="buttons" selectedAttr="{{ selectedComponentType == 'buttons' }}" tap="{{ selectComponentType }}"></Button>
|
||||||
|
<Button text="Images" componentType="images" selectedAttr="{{ selectedComponentType == 'images' }}" tap="{{ selectComponentType }}"></Button>
|
||||||
</FlexboxLayout>
|
</FlexboxLayout>
|
||||||
|
|
||||||
<Label text="Background"></Label>
|
<Label text="Background"></Label>
|
||||||
|
@ -134,6 +134,7 @@ function unsubscribeFromScrollNotifications(view: View) {
|
|||||||
view.off('scroll', onScroll);
|
view.off('scroll', onScroll);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function subscribeForScrollNotifications(view: View) {
|
function subscribeForScrollNotifications(view: View) {
|
||||||
if (view.nativeViewProtected instanceof UIScrollView) {
|
if (view.nativeViewProtected instanceof UIScrollView) {
|
||||||
view.on('scroll', onScroll);
|
view.on('scroll', onScroll);
|
||||||
@ -170,6 +171,7 @@ function clearNonUniformBorders(nativeView: NativeView): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pattern = /url\(('|")(.*?)\1\)/;
|
const pattern = /url\(('|")(.*?)\1\)/;
|
||||||
|
|
||||||
function setUIColorFromImage(view: View, nativeView: UIView, callback: (uiColor: UIColor) => void, flip?: boolean): void {
|
function setUIColorFromImage(view: View, nativeView: UIView, callback: (uiColor: UIColor) => void, flip?: boolean): void {
|
||||||
const frame = nativeView.frame;
|
const frame = nativeView.frame;
|
||||||
const boundsWidth = view.scaleX ? frame.size.width / view.scaleX : frame.size.width;
|
const boundsWidth = view.scaleX ? frame.size.width / view.scaleX : frame.size.width;
|
||||||
@ -733,15 +735,24 @@ function drawBoxShadow(nativeView: NativeView, view: View, boxShadow: CSSShadow,
|
|||||||
}
|
}
|
||||||
// shadow opacity is handled on the shadow's color instance
|
// shadow opacity is handled on the shadow's color instance
|
||||||
layer.shadowOpacity = boxShadow.color?.a ? boxShadow.color?.a / 255 : 1;
|
layer.shadowOpacity = boxShadow.color?.a ? boxShadow.color?.a / 255 : 1;
|
||||||
layer.shadowRadius = Length.toDevicePixels(boxShadow.blurRadius);
|
layer.shadowRadius = Length.toDevicePixels(boxShadow.blurRadius, 0.0);
|
||||||
layer.shadowColor = boxShadow.color.ios.CGColor;
|
layer.shadowColor = boxShadow.color.ios.CGColor;
|
||||||
layer.shadowOffset = CGSizeMake(Length.toDevicePixels(boxShadow.offsetX), Length.toDevicePixels(boxShadow.offsetY));
|
|
||||||
|
// prettier-ignore
|
||||||
|
layer.shadowOffset = CGSizeMake(
|
||||||
|
Length.toDevicePixels(boxShadow.offsetX, 0.0),
|
||||||
|
Length.toDevicePixels(boxShadow.offsetY, 0.0)
|
||||||
|
);
|
||||||
|
|
||||||
// this should match the view's border radius
|
// this should match the view's border radius
|
||||||
const cornerRadius = Length.toDevicePixels(<LengthType>view.style.borderRadius);
|
const cornerRadius = Length.toDevicePixels(<LengthType>view.style.borderRadius, 0.0);
|
||||||
|
|
||||||
// apply spreadRadius by expanding shadow layer bounds
|
// apply spreadRadius by expanding shadow layer bounds
|
||||||
const bounds = boxShadow.spreadRadius ? CGRectInset(nativeView.bounds, -Length.toDevicePixels(boxShadow.spreadRadius), -Length.toDevicePixels(boxShadow.spreadRadius)) : nativeView.bounds;
|
// prettier-ignore
|
||||||
|
const bounds = CGRectInset(nativeView.bounds,
|
||||||
|
-Length.toDevicePixels(boxShadow.spreadRadius, 0.0),
|
||||||
|
-Length.toDevicePixels(boxShadow.spreadRadius, 0.0)
|
||||||
|
);
|
||||||
|
|
||||||
// This has the nice glow with box shadow of 0,0
|
// This has the nice glow with box shadow of 0,0
|
||||||
layer.shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(bounds, cornerRadius).CGPath;
|
layer.shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(bounds, cornerRadius).CGPath;
|
||||||
@ -749,7 +760,10 @@ function drawBoxShadow(nativeView: NativeView, view: View, boxShadow: CSSShadow,
|
|||||||
|
|
||||||
function clearBoxShadow(nativeView: NativeView) {
|
function clearBoxShadow(nativeView: NativeView) {
|
||||||
nativeView.clipsToBounds = true;
|
nativeView.clipsToBounds = true;
|
||||||
const layer: CALayer = iOSNativeHelper.getShadowLayer(nativeView, 'ns-box-shadow');
|
const layer: CALayer = iOSNativeHelper.getShadowLayer(nativeView, 'ns-box-shadow', false);
|
||||||
|
if (!layer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
layer.masksToBounds = true;
|
layer.masksToBounds = true;
|
||||||
layer.shadowOffset = CGSizeMake(0, 0);
|
layer.shadowOffset = CGSizeMake(0, 0);
|
||||||
layer.shadowColor = UIColor.clearColor.CGColor;
|
layer.shadowColor = UIColor.clearColor.CGColor;
|
||||||
|
@ -34,7 +34,7 @@ const isLength = (v) => v === '0' || LENGTH_RE.test(v);
|
|||||||
* @param value
|
* @param value
|
||||||
*/
|
*/
|
||||||
export function parseCSSShadow(value: string): CSSShadow {
|
export function parseCSSShadow(value: string): CSSShadow {
|
||||||
const parts = value.split(PARTS_RE);
|
const parts = value.trim().split(PARTS_RE);
|
||||||
const inset = parts.includes('inset');
|
const inset = parts.includes('inset');
|
||||||
const first = parts[0];
|
const first = parts[0];
|
||||||
const last = parts[parts.length - 1];
|
const last = parts[parts.length - 1];
|
||||||
|
3
packages/core/utils/native-helper.d.ts
vendored
3
packages/core/utils/native-helper.d.ts
vendored
@ -171,8 +171,9 @@ export namespace iOSNativeHelper {
|
|||||||
/**
|
/**
|
||||||
* @param nativeView UIView to find shadow layer with
|
* @param nativeView UIView to find shadow layer with
|
||||||
* @param name Name of the shadow layer if looking for specifically named layer
|
* @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): any; /* CALayer */
|
export function getShadowLayer(nativeView: any /* UIView */, name?: string, create?: boolean): any; /* CALayer */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a UIDocumentInteractionControllerDelegate implementation for use with UIDocumentInteractionController
|
* Create a UIDocumentInteractionControllerDelegate implementation for use with UIDocumentInteractionController
|
||||||
|
@ -120,44 +120,86 @@ export namespace iOSNativeHelper {
|
|||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getShadowLayer(nativeView: UIView, name?: string): CALayer {
|
export function getShadowLayer(nativeView: UIView, name: string = 'ns-shadow-layer', create: boolean = true): CALayer {
|
||||||
let layer: CALayer;
|
return nativeView.layer;
|
||||||
name = name || 'ns-shadow-layer';
|
|
||||||
if (nativeView) {
|
console.log(`--- ${create ? 'CREATE' : 'READ'}`);
|
||||||
if (nativeView.layer) {
|
|
||||||
if (nativeView.layer.name === name) {
|
/**
|
||||||
console.log('- found shadow layer - reusing.');
|
* UIView
|
||||||
return nativeView.layer;
|
* -> Shadow
|
||||||
} else {
|
*
|
||||||
if (nativeView.layer.sublayers && nativeView.layer.sublayers.count) {
|
*
|
||||||
console.log('nativeView.layer.sublayers.count:', nativeView.layer.sublayers.count);
|
* UIView
|
||||||
for (let i = 0; i < nativeView.layer.sublayers.count; i++) {
|
* -> UIView
|
||||||
console.log(`layer ${i}:`, nativeView.layer.sublayers.objectAtIndex(i));
|
* -> Shadow
|
||||||
console.log(`layer ${i} name:`, nativeView.layer.sublayers.objectAtIndex(i).name);
|
*/
|
||||||
if (nativeView.layer.sublayers.objectAtIndex(i).name === name) {
|
|
||||||
return nativeView.layer.sublayers.objectAtIndex(i);
|
if (!nativeView) {
|
||||||
}
|
return null;
|
||||||
}
|
}
|
||||||
if (nativeView instanceof UITextView) {
|
|
||||||
layer = nativeView.layer.sublayers.objectAtIndex(1);
|
if (!nativeView.layer) {
|
||||||
} else {
|
// should never hit this?
|
||||||
layer = nativeView.layer.sublayers.objectAtIndex(nativeView.layer.sublayers.count - 1);
|
console.log('- no layer! -');
|
||||||
}
|
return null;
|
||||||
} else {
|
}
|
||||||
layer = nativeView.layer;
|
|
||||||
}
|
// 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;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// could this occur?
|
|
||||||
console.log('no layer!');
|
|
||||||
}
|
}
|
||||||
|
// if (nativeView instanceof UITextView) {
|
||||||
|
// return nativeView.layer.sublayers.objectAtIndex(1);
|
||||||
|
// } else {
|
||||||
|
// return nativeView.layer.sublayers.objectAtIndex(nativeView.layer.sublayers.count - 1);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
console.log('layer.name:', layer.name);
|
// else {
|
||||||
if (!layer.name) {
|
// layer = nativeView.layer;
|
||||||
// only explicitly name if the developer had not named it themselves and/or some other integration
|
// }
|
||||||
layer.name = name;
|
|
||||||
|
// we're not interested in creating a new layer
|
||||||
|
if (!create) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return layer;
|
|
||||||
|
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 {
|
export function createUIDocumentInteractionControllerDelegate(): NSObject {
|
||||||
|
Reference in New Issue
Block a user