diff --git a/apps/toolbox/src/main-page.xml b/apps/toolbox/src/main-page.xml
index 2905162ec..22e4be777 100644
--- a/apps/toolbox/src/main-page.xml
+++ b/apps/toolbox/src/main-page.xml
@@ -13,6 +13,7 @@
+
diff --git a/apps/toolbox/src/pages/glass-effects.ts b/apps/toolbox/src/pages/glass-effects.ts
new file mode 100644
index 000000000..991a73fa2
--- /dev/null
+++ b/apps/toolbox/src/pages/glass-effects.ts
@@ -0,0 +1,16 @@
+import { Observable, EventData, Page, CoreTypes, GlassEffectConfig } from '@nativescript/core';
+
+let page: Page;
+
+export function navigatingTo(args: EventData) {
+ page = args.object;
+ page.bindingContext = new GlassEffectModel();
+}
+
+export class GlassEffectModel extends Observable {
+ iosGlassEffectInteractive: GlassEffectConfig = {
+ interactive: true,
+ tint: '#faabab',
+ variant: 'clear',
+ };
+}
diff --git a/apps/toolbox/src/pages/glass-effects.xml b/apps/toolbox/src/pages/glass-effects.xml
new file mode 100644
index 000000000..817ad6616
--- /dev/null
+++ b/apps/toolbox/src/pages/glass-effects.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/core/ui/core/view/index.ios.ts b/packages/core/ui/core/view/index.ios.ts
index 44ae47f70..13a29a622 100644
--- a/packages/core/ui/core/view/index.ios.ts
+++ b/packages/core/ui/core/view/index.ios.ts
@@ -1,10 +1,11 @@
import type { Point, Position } from './view-interfaces';
-import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty } from './view-common';
+import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty, iosGlassEffectProperty, GlassEffectType, GlassEffectVariant } from './view-common';
import { isAccessibilityServiceEnabled } from '../../../application';
import { updateA11yPropertiesCallback } from '../../../application/helpers-common';
import { ShowModalOptions, hiddenProperty } from '../view-base';
import { Trace } from '../../../trace';
-import { layout, ios as iosUtils, SDK_VERSION, getWindow } from '../../../utils';
+import { layout, ios as iosUtils, getWindow } from '../../../utils';
+import { SDK_VERSION, supportsGlass } from '../../../utils/constants';
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 } from '../../styling/style-properties';
@@ -15,6 +16,7 @@ import { CoreTypes } from '../../../core-types';
import type { ModalTransition } from '../../transition/modal-transition';
import { SharedTransition } from '../../transition/shared-transition';
import { NativeScriptUIView } from '../../utils';
+import { Color } from '../../../color';
export * from './view-common';
export * from './view-helper';
@@ -51,6 +53,12 @@ export class View extends ViewCommon {
*/
_nativeBackgroundState: 'unset' | 'invalid' | 'drawn';
+ /**
+ * Glass effect configuration
+ */
+ private _glassEffectView: UIVisualEffectView;
+ private _glassEffectMeasure: NodeJS.Timeout;
+
get isLayoutRequired(): boolean {
return (this._privateFlags & PFLAG_LAYOUT_REQUIRED) === PFLAG_LAYOUT_REQUIRED;
}
@@ -887,6 +895,60 @@ export class View extends ViewCommon {
}
}
+ [iosGlassEffectProperty.setNative](value: GlassEffectType) {
+ if (!this.nativeViewProtected || !supportsGlass()) {
+ return;
+ }
+ if (this._glassEffectView) {
+ this._glassEffectView.removeFromSuperview();
+ this._glassEffectView = null;
+ }
+ if (!value) {
+ return;
+ }
+ let effect: UIGlassEffect;
+ if (typeof value === 'string') {
+ effect = UIGlassEffect.effectWithStyle(this.toUIGlassStyle(value));
+ } else {
+ if (value.variant === 'identity') {
+ return;
+ }
+ effect = UIGlassEffect.effectWithStyle(this.toUIGlassStyle(value.variant));
+ if (value.interactive) {
+ effect.interactive = true;
+ }
+ if (value.tint) {
+ effect.tintColor = typeof value.tint === 'string' ? new Color(value.tint).ios : value.tint;
+ }
+ }
+ this._glassEffectView = UIVisualEffectView.alloc().initWithEffect(effect);
+ // let touches pass to content
+ this._glassEffectView.userInteractionEnabled = false;
+ this._glassEffectView.clipsToBounds = true;
+ // size & autoresize
+ if (this._glassEffectMeasure) {
+ clearTimeout(this._glassEffectMeasure);
+ }
+ this._glassEffectMeasure = setTimeout(() => {
+ const size = this.nativeViewProtected.bounds.size;
+ this._glassEffectView.frame = CGRectMake(0, 0, size.width, size.height);
+ this._glassEffectView.autoresizingMask = 2;
+ this.nativeViewProtected.insertSubviewAtIndex(this._glassEffectView, 0);
+ });
+ }
+
+ public toUIGlassStyle(value?: GlassEffectVariant) {
+ if (supportsGlass()) {
+ switch (value) {
+ case 'regular':
+ return UIGlassEffectStyle?.Regular ?? 0;
+ case 'clear':
+ return UIGlassEffectStyle?.Clear ?? 1;
+ }
+ }
+ return 1;
+ }
+
public sendAccessibilityEvent(options: Partial): void {
if (!isAccessibilityServiceEnabled()) {
return;
diff --git a/packages/core/ui/core/view/view-common.ts b/packages/core/ui/core/view/view-common.ts
index b72920810..d7d8d099d 100644
--- a/packages/core/ui/core/view/view-common.ts
+++ b/packages/core/ui/core/view/view-common.ts
@@ -1288,6 +1288,7 @@ export const isUserInteractionEnabledProperty = new Property({
name: 'iosOverflowSafeArea',
defaultValue: false,
@@ -1308,6 +1309,17 @@ export const iosIgnoreSafeAreaProperty = new InheritedProperty({
});
iosIgnoreSafeAreaProperty.register(ViewCommon);
+/**
+ * Glass effects
+ */
+export type GlassEffectVariant = 'regular' | 'clear' | 'identity';
+export type GlassEffectConfig = { variant?: GlassEffectVariant; interactive?: boolean; tint: string | Color };
+export type GlassEffectType = GlassEffectVariant | GlassEffectConfig;
+export const iosGlassEffectProperty = new Property({
+ name: 'iosGlassEffect',
+});
+iosGlassEffectProperty.register(ViewCommon);
+
export const visionHoverStyleProperty = new Property({
name: 'visionHoverStyle',
valueChanged(view, oldValue, newValue) {
@@ -1324,6 +1336,7 @@ const visionIgnoreHoverStyleProperty = new Property({
valueConverter: booleanConverter,
});
visionIgnoreHoverStyleProperty.register(ViewCommon);
+// Apple only end
const touchAnimationProperty = new Property({
name: 'touchAnimation',
diff --git a/packages/core/ui/index.ts b/packages/core/ui/index.ts
index ba9bcb2fd..854667b6d 100644
--- a/packages/core/ui/index.ts
+++ b/packages/core/ui/index.ts
@@ -16,7 +16,7 @@ export { ControlStateChangeListener } from './core/control-state-change';
export { ViewBase, eachDescendant, getAncestor, getViewById, booleanConverter, querySelectorAll } from './core/view-base';
export type { ShowModalOptions } from './core/view-base';
export { View, CSSType, ContainerView, ViewHelper, AndroidHelper, IOSHelper, isUserInteractionEnabledProperty, PseudoClassHandler, CustomLayoutView } from './core/view';
-export type { Template, KeyedTemplate, AddArrayFromBuilder, AddChildFromBuilder } from './core/view';
+export type { Template, KeyedTemplate, AddArrayFromBuilder, AddChildFromBuilder, GlassEffectConfig, GlassEffectType, GlassEffectVariant } from './core/view';
export type { ShownModallyData, Size } from './core/view/view-interfaces';
export { Property, CoercibleProperty, InheritedProperty, CssProperty, InheritedCssProperty, ShorthandProperty, CssAnimationProperty, makeParser, makeValidator } from './core/properties';
export { unsetValue } from './core/properties/property-shared';
diff --git a/packages/core/utils/constants.android.ts b/packages/core/utils/constants.android.ts
index 27e4c1a29..10d5c87f7 100644
--- a/packages/core/utils/constants.android.ts
+++ b/packages/core/utils/constants.android.ts
@@ -1 +1,4 @@
export const SDK_VERSION = android.os.Build.VERSION.SDK_INT;
+export function supportsGlass(): boolean {
+ return false;
+}
diff --git a/packages/core/utils/constants.d.ts b/packages/core/utils/constants.d.ts
index 663c7249e..34db46bc4 100644
--- a/packages/core/utils/constants.d.ts
+++ b/packages/core/utils/constants.d.ts
@@ -1 +1,2 @@
export const SDK_VERSION: number;
+export function supportsGlass(): boolean;
diff --git a/packages/core/utils/constants.ios.ts b/packages/core/utils/constants.ios.ts
index 16a67a4b3..735e20436 100644
--- a/packages/core/utils/constants.ios.ts
+++ b/packages/core/utils/constants.ios.ts
@@ -1 +1,4 @@
export const SDK_VERSION = parseFloat(UIDevice.currentDevice.systemVersion);
+export function supportsGlass(): boolean {
+ return __APPLE__ && SDK_VERSION >= 26;
+}