mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
feat(ios): iosGlassEffect and LiquidGlass containers
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Observable, EventData, Page, CoreTypes, GlassEffectConfig, View, GlassEffectType, TouchAnimationOptions, Label, Image } from '@nativescript/core';
|
||||
import { Observable, EventData, Page, CoreTypes, GlassEffectConfig, View, Label, Animation, LiquidGlassContainer } from '@nativescript/core';
|
||||
|
||||
let page: Page;
|
||||
|
||||
@@ -42,10 +42,10 @@ export class GlassEffectModel extends Observable {
|
||||
const glass = args.object as View;
|
||||
switch (glass.id) {
|
||||
case 'glass1':
|
||||
glass.translateX = -40;
|
||||
glass.translateX = 10;
|
||||
break;
|
||||
case 'glass2':
|
||||
glass.translateX = 40;
|
||||
glass.translateX = 70;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -58,22 +58,43 @@ export class GlassEffectModel extends Observable {
|
||||
this.glassTargetLabels[label.id] = label;
|
||||
}
|
||||
|
||||
toggleMergeGlass() {
|
||||
async toggleMergeGlass(args) {
|
||||
if (!this.glassTargets['glass1'] || !this.glassTargets['glass2']) {
|
||||
return;
|
||||
}
|
||||
const container = args?.object as LiquidGlassContainer | undefined;
|
||||
this.glassMerged = !this.glassMerged;
|
||||
const glass1 = this.glassTargets['glass1'];
|
||||
const glass2 = this.glassTargets['glass2'];
|
||||
glass1.animate({ translate: { x: this.glassMerged ? -40 : 0, y: 0 }, duration: 300, curve: CoreTypes.AnimationCurve.easeInOut }).catch(() => {});
|
||||
glass2.animate({ translate: { x: this.glassMerged ? 40 : 0, y: 0 }, duration: 300, curve: CoreTypes.AnimationCurve.easeInOut }).catch(() => {});
|
||||
|
||||
this.glassTargetLabels['share'].animate({ opacity: this.glassMerged ? 1 : 0, duration: 300, curve: CoreTypes.AnimationCurve.easeInOut }).catch(() => {});
|
||||
// Use relative deltas for translate; the container will bake them into frames post-animation
|
||||
const d1 = this.glassMerged ? 25 : -25; // left bubble moves inward/outward
|
||||
const d2 = this.glassMerged ? -25 : 25; // right bubble moves inward/outward
|
||||
|
||||
this.glassTargetLabels['like'].text = this.glassMerged ? 'Done' : 'Like';
|
||||
if (!this.glassMerged) {
|
||||
this.glassTargetLabels['like'].text = 'Like';
|
||||
}
|
||||
|
||||
const animateAll = new Animation([
|
||||
{ target: glass1, translate: { x: d1, y: 0 }, duration: 300, curve: CoreTypes.AnimationCurve.easeOut },
|
||||
{ target: glass2, translate: { x: d2, y: 0 }, duration: 300, curve: CoreTypes.AnimationCurve.easeOut },
|
||||
{
|
||||
target: this.glassTargetLabels['share'],
|
||||
opacity: this.glassMerged ? 0 : 1,
|
||||
duration: 300,
|
||||
},
|
||||
]);
|
||||
animateAll.play().then(() => {
|
||||
if (this.glassMerged) {
|
||||
this.glassTargetLabels['like'].text = 'Done';
|
||||
}
|
||||
|
||||
// Ask container to stabilize frames so UIGlassContainerEffect samples correct positions
|
||||
setTimeout(() => container?.stabilizeLayout?.(), 0);
|
||||
});
|
||||
|
||||
// for testing, on tap, can see glass effect changes animating differences
|
||||
this.testGlassBindingChanges();
|
||||
// this.testGlassBindingChanges();
|
||||
}
|
||||
|
||||
testGlassBindingChanges() {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</LiquidGlass>
|
||||
|
||||
|
||||
<LiquidGlassContainer row="4" tap="{{toggleMergeGlass}}" horizontalAlignment="center" verticalAlignment="middle" class="m-t-20" width="300" height="100">
|
||||
<LiquidGlassContainer row="4" tap="{{toggleMergeGlass}}" horizontalAlignment="left" verticalAlignment="middle" class="m-t-20" width="300" height="100" columns="*">
|
||||
<LiquidGlass id="glass1" loaded="{{loadedGlass}}" borderRadius="50" width="100" height="100">
|
||||
<Label id="share" text="Share" fontSize="22" class="font-weight-bold text-center" width="100" height="100" loaded="{{loadedGlassLabels}}" />
|
||||
</LiquidGlass>
|
||||
@@ -47,6 +47,9 @@
|
||||
<Label class="text-center c-white" fontWeight="bold" fontSize="18" text="Glass Effects Interactive" />
|
||||
</GridLayout>
|
||||
</GridLayout>
|
||||
|
||||
<!-- make scrollable to view glass on scroll -->
|
||||
<ContentView height="500"/>
|
||||
</StackLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
@@ -895,54 +895,98 @@ export class View extends ViewCommon {
|
||||
}
|
||||
}
|
||||
|
||||
[iosGlassEffectProperty.setNative](value: GlassEffectType) {
|
||||
if (!this.nativeViewProtected || !supportsGlass()) {
|
||||
return;
|
||||
}
|
||||
let effect: UIGlassEffect | UIVisualEffect;
|
||||
protected _applyGlassEffect(
|
||||
value: GlassEffectType,
|
||||
options: {
|
||||
effectType: 'glass' | 'container';
|
||||
targetView?: UIVisualEffectView;
|
||||
toGlassStyleFn?: (variant?: GlassEffectVariant) => number;
|
||||
onCreate?: (effectView: UIVisualEffectView, effect: UIVisualEffect) => void;
|
||||
onUpdate?: (effectView: UIVisualEffectView, effect: UIVisualEffect, duration: number) => void;
|
||||
},
|
||||
): UIVisualEffectView | undefined {
|
||||
const config: GlassEffectConfig | null = typeof value !== 'string' ? value : null;
|
||||
const variant = config ? config.variant : (value as GlassEffectVariant);
|
||||
const defaultDuration = 0.3;
|
||||
const duration = config ? (config.animateChangeDuration ?? defaultDuration) : defaultDuration;
|
||||
|
||||
let effect: UIGlassEffect | UIGlassContainerEffect | UIVisualEffect;
|
||||
|
||||
// Create the appropriate effect based on type and variant
|
||||
if (!value || ['identity', 'none'].includes(variant)) {
|
||||
// empty effect
|
||||
effect = UIVisualEffect.new();
|
||||
} else {
|
||||
effect = UIGlassEffect.effectWithStyle(this.toUIGlassStyle(variant));
|
||||
if (config) {
|
||||
(effect as UIGlassEffect).interactive = !!config.interactive;
|
||||
if (config.tint) {
|
||||
(effect as UIGlassEffect).tintColor = typeof config.tint === 'string' ? new Color(config.tint).ios : config.tint;
|
||||
if (options.effectType === 'glass') {
|
||||
const styleFn = options.toGlassStyleFn || this.toUIGlassStyle.bind(this);
|
||||
effect = UIGlassEffect.effectWithStyle(styleFn(variant));
|
||||
if (config) {
|
||||
(effect as UIGlassEffect).interactive = !!config.interactive;
|
||||
if (config.tint) {
|
||||
(effect as UIGlassEffect).tintColor = typeof config.tint === 'string' ? new Color(config.tint).ios : config.tint;
|
||||
}
|
||||
}
|
||||
} else if (options.effectType === 'container') {
|
||||
effect = UIGlassContainerEffect.alloc().init();
|
||||
(effect as UIGlassContainerEffect).spacing = config?.spacing ?? 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this._glassEffectView) {
|
||||
this._glassEffectView = UIVisualEffectView.alloc().initWithEffect(effect);
|
||||
// this._glassEffectView.overrideUserInterfaceStyle = UIUserInterfaceStyle.Light;
|
||||
// let touches pass to content
|
||||
this._glassEffectView.userInteractionEnabled = false;
|
||||
this._glassEffectView.clipsToBounds = true;
|
||||
// size & autoresize
|
||||
if (this._glassEffectMeasure) {
|
||||
clearTimeout(this._glassEffectMeasure);
|
||||
// Handle creating new effect view or updating existing one
|
||||
if (options.targetView) {
|
||||
// Update existing effect view
|
||||
if (options.onUpdate) {
|
||||
options.onUpdate(options.targetView, effect, duration);
|
||||
} else {
|
||||
// Default update behavior: animate effect changes
|
||||
UIView.animateWithDurationAnimations(duration, () => {
|
||||
options.targetView.effect = effect;
|
||||
});
|
||||
}
|
||||
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);
|
||||
return undefined;
|
||||
} else if (options.onCreate) {
|
||||
// Create new effect view and let caller handle setup
|
||||
const effectView = UIVisualEffectView.alloc().initWithEffect(effect);
|
||||
options.onCreate(effectView, effect);
|
||||
return effectView;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
[iosGlassEffectProperty.setNative](value: GlassEffectType) {
|
||||
if (!this.nativeViewProtected || !supportsGlass()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._glassEffectView) {
|
||||
// Create new glass effect view
|
||||
this._glassEffectView = this._applyGlassEffect(value, {
|
||||
effectType: 'glass',
|
||||
onCreate: (effectView, effect) => {
|
||||
// let touches pass to content
|
||||
effectView.userInteractionEnabled = false;
|
||||
effectView.clipsToBounds = true;
|
||||
// size & autoresize
|
||||
if (this._glassEffectMeasure) {
|
||||
clearTimeout(this._glassEffectMeasure);
|
||||
}
|
||||
this._glassEffectMeasure = setTimeout(() => {
|
||||
const size = this.nativeViewProtected.bounds.size;
|
||||
effectView.frame = CGRectMake(0, 0, size.width, size.height);
|
||||
effectView.autoresizingMask = 2;
|
||||
this.nativeViewProtected.insertSubviewAtIndex(effectView, 0);
|
||||
});
|
||||
},
|
||||
});
|
||||
} else {
|
||||
// animate effect changes
|
||||
UIView.animateWithDurationAnimations(duration, () => {
|
||||
this._glassEffectView.effect = effect;
|
||||
// Update existing glass effect view
|
||||
this._applyGlassEffect(value, {
|
||||
effectType: 'glass',
|
||||
targetView: this._glassEffectView,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public toUIGlassStyle(value?: GlassEffectVariant) {
|
||||
toUIGlassStyle(value?: GlassEffectVariant) {
|
||||
if (supportsGlass()) {
|
||||
switch (value) {
|
||||
case 'regular':
|
||||
|
||||
@@ -1239,6 +1239,32 @@ export abstract class ViewCommon extends ViewBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared helper method for applying glass effects to views.
|
||||
* This method can be used by View and its subclasses (LiquidGlass, LiquidGlassContainer, etc.)
|
||||
* iOS only at the moment but could be applied to others once supported in other platforms.
|
||||
*
|
||||
* @param value - The glass effect configuration
|
||||
* @param options - Configuration options for different glass effect behaviors
|
||||
* @param options.effectType - Type of effect to create: 'glass' | 'container'
|
||||
* @param options.targetView - The UIVisualEffectView to apply the effect to (if updating existing view)
|
||||
* @param options.toGlassStyleFn - Custom function to convert variant to UIGlassEffectStyle
|
||||
* @param options.onCreate - Callback when a new effect view is created (for initial setup)
|
||||
* @param options.onUpdate - Callback when an existing effect view is updated
|
||||
*/
|
||||
protected _applyGlassEffect(
|
||||
value: GlassEffectType,
|
||||
options: {
|
||||
effectType: 'glass' | 'container';
|
||||
targetView?: UIVisualEffectView;
|
||||
toGlassStyleFn?: (variant?: GlassEffectVariant) => number;
|
||||
onCreate?: (effectView: UIVisualEffectView, effect: UIVisualEffect) => void;
|
||||
onUpdate?: (effectView: UIVisualEffectView, effect: UIVisualEffect, duration: number) => void;
|
||||
},
|
||||
): UIVisualEffectView | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public sendAccessibilityEvent(options: Partial<AccessibilityEventOptions>): void {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import type { NativeScriptUIView } from '../../utils';
|
||||
import { GlassEffectConfig, GlassEffectType, GlassEffectVariant, iosGlassEffectProperty, View } from '../../core/view';
|
||||
import { GlassEffectType, iosGlassEffectProperty, View } from '../../core/view';
|
||||
import { LiquidGlassContainerCommon } from './liquid-glass-container-common';
|
||||
import { toUIGlassStyle } from '../liquid-glass';
|
||||
|
||||
export class LiquidGlassContainer extends LiquidGlassContainerCommon {
|
||||
public nativeViewProtected: UIVisualEffectView;
|
||||
private _contentHost: UIView;
|
||||
private _normalizing = false;
|
||||
|
||||
createNativeView() {
|
||||
// Keep UIVisualEffectView as the root to preserve interactive container effect
|
||||
@@ -15,7 +17,7 @@ export class LiquidGlassContainer extends LiquidGlassContainerCommon {
|
||||
effectView.clipsToBounds = true;
|
||||
effectView.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
|
||||
|
||||
// Add a host view for children so GridLayout can lay them out normally
|
||||
// Add a host view for children so parent can lay them out normally
|
||||
const host = UIView.new();
|
||||
host.frame = effectView.bounds;
|
||||
host.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
|
||||
@@ -42,32 +44,91 @@ export class LiquidGlassContainer extends LiquidGlassContainerCommon {
|
||||
this.nativeViewProtected.layer.insertSublayerBelow(childNativeView.outerShadowContainerLayer, childNativeView.layer);
|
||||
}
|
||||
|
||||
// Normalize in case the child comes in with a residual translate from a previous state
|
||||
this._scheduleNormalize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[iosGlassEffectProperty.setNative](value: GlassEffectType) {
|
||||
console.log('iosGlassEffectProperty:', value);
|
||||
let effect: UIGlassContainerEffect | UIVisualEffect;
|
||||
const config: GlassEffectConfig | null = typeof value !== 'string' ? value : null;
|
||||
const variant = config ? config.variant : (value as GlassEffectVariant);
|
||||
const defaultDuration = 0.3;
|
||||
const duration = config ? (config.animateChangeDuration ?? defaultDuration) : defaultDuration;
|
||||
if (!value || ['identity', 'none'].includes(variant)) {
|
||||
// empty effect
|
||||
effect = UIVisualEffect.new();
|
||||
} else {
|
||||
effect = UIGlassContainerEffect.alloc().init();
|
||||
(effect as UIGlassContainerEffect).spacing = config?.spacing ?? 8;
|
||||
// When children animate with translate (layer transform), UIVisualEffectView-based
|
||||
// container effects may recompute based on the underlying frames (not transforms),
|
||||
// which can cause jumps. Normalize any residual translation into the
|
||||
// child's frame so the effect uses the final visual positions.
|
||||
public onLayout(left: number, top: number, right: number, bottom: number): void {
|
||||
super.onLayout(left, top, right, bottom);
|
||||
|
||||
// Try to fold any pending translates into frames on each layout pass
|
||||
this._normalizeChildrenTransforms();
|
||||
}
|
||||
|
||||
// Allow callers to stabilize layout after custom animations
|
||||
public stabilizeLayout() {
|
||||
this._normalizeChildrenTransforms(true);
|
||||
}
|
||||
|
||||
private _scheduleNormalize() {
|
||||
if (this._normalizing) return;
|
||||
this._normalizing = true;
|
||||
// Next tick to allow any pending frame/transform updates to settle
|
||||
setTimeout(() => {
|
||||
try {
|
||||
this._normalizeChildrenTransforms();
|
||||
} finally {
|
||||
this._normalizing = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _normalizeChildrenTransforms(force = false) {
|
||||
let changed = false;
|
||||
const count = this.getChildrenCount?.() ?? 0;
|
||||
for (let i = 0; i < count; i++) {
|
||||
const child = this.getChildAt(i) as View | undefined;
|
||||
if (!child) continue;
|
||||
const tx = child.translateX || 0;
|
||||
const ty = child.translateY || 0;
|
||||
if (!tx && !ty) continue;
|
||||
|
||||
const native = child.nativeViewProtected as UIView;
|
||||
if (!native) continue;
|
||||
|
||||
// Skip if the child is still animating (unless forced)
|
||||
if (!force) {
|
||||
const keys = native.layer.animationKeys ? native.layer.animationKeys() : null;
|
||||
const hasAnimations = !!(keys && keys.count > 0);
|
||||
if (hasAnimations) continue;
|
||||
}
|
||||
|
||||
const frame = native.frame;
|
||||
native.transform = CGAffineTransformIdentity;
|
||||
native.frame = CGRectMake(frame.origin.x + tx, frame.origin.y + ty, frame.size.width, frame.size.height);
|
||||
|
||||
child.translateX = 0;
|
||||
child.translateY = 0;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (effect) {
|
||||
// animate effect changes
|
||||
UIView.animateWithDurationAnimations(duration, () => {
|
||||
this.nativeViewProtected.effect = effect;
|
||||
});
|
||||
if (changed) {
|
||||
// Ask the effect view to re-evaluate its internal state using updated frames
|
||||
const nv = this.nativeViewProtected;
|
||||
if (nv) {
|
||||
nv.setNeedsLayout();
|
||||
nv.layoutIfNeeded();
|
||||
// Also request layout on contentView in case the effect inspects it directly
|
||||
nv.contentView?.setNeedsLayout?.();
|
||||
nv.contentView?.layoutIfNeeded?.();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[iosGlassEffectProperty.setNative](value: GlassEffectType) {
|
||||
this._applyGlassEffect(value, {
|
||||
effectType: 'container',
|
||||
targetView: this.nativeViewProtected,
|
||||
toGlassStyleFn: toUIGlassStyle,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { GridLayout } from '../grid-layout';
|
||||
import { AbsoluteLayout } from '../absolute-layout';
|
||||
|
||||
export class LiquidGlassContainerCommon extends GridLayout {}
|
||||
export class LiquidGlassContainerCommon extends AbsoluteLayout {
|
||||
stabilizeLayout() {}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import { LiquidGlassCommon } from './liquid-glass-common';
|
||||
import type { GlassEffectVariant } from '../../core/view';
|
||||
|
||||
export class LiquidGlass extends LiquidGlassCommon {}
|
||||
|
||||
export function toUIGlassStyle(value?: GlassEffectVariant) {
|
||||
// Can support when Android equivalent is available
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
import { LiquidGlassCommon } from './liquid-glass-common';
|
||||
|
||||
export class LiquidGlass extends LiquidGlassCommon {}
|
||||
|
||||
/**
|
||||
* Convert GlassEffectVariant to corresponding platform glass style.
|
||||
* @param value GlassEffectVariant | undefined
|
||||
* @returns 0 | 1 | UIGlassEffectStyle
|
||||
*/
|
||||
export function toUIGlassStyle(value?: GlassEffectVariant): 0 | 1 | UIGlassEffectStyle;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { NativeScriptUIView } from '../../utils';
|
||||
import { supportsGlass } from '../../../utils/constants';
|
||||
import { GlassEffectConfig, GlassEffectType, GlassEffectVariant, iosGlassEffectProperty, View } from '../../core/view';
|
||||
import { Color } from '../../../color';
|
||||
import { type GlassEffectType, type GlassEffectVariant, iosGlassEffectProperty, View } from '../../core/view';
|
||||
import { LiquidGlassCommon } from './liquid-glass-common';
|
||||
|
||||
export class LiquidGlass extends LiquidGlassCommon {
|
||||
@@ -9,7 +8,6 @@ export class LiquidGlass extends LiquidGlassCommon {
|
||||
private _contentHost: UIView;
|
||||
|
||||
createNativeView() {
|
||||
console.log('createNativeView');
|
||||
// Use UIVisualEffectView as the root so interactive effects can track touches
|
||||
const effect = UIGlassEffect.effectWithStyle(UIGlassEffectStyle.Clear);
|
||||
effect.interactive = true;
|
||||
@@ -18,7 +16,7 @@ export class LiquidGlass extends LiquidGlassCommon {
|
||||
effectView.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
|
||||
effectView.clipsToBounds = true;
|
||||
|
||||
// Host for all children so GridLayout (derived) layout works as usual
|
||||
// Host for all children so parent layout works as usual
|
||||
const host = UIView.new();
|
||||
host.frame = effectView.bounds;
|
||||
host.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
|
||||
@@ -52,42 +50,22 @@ export class LiquidGlass extends LiquidGlassCommon {
|
||||
}
|
||||
|
||||
[iosGlassEffectProperty.setNative](value: GlassEffectType) {
|
||||
console.log('iosGlassEffectProperty:', value);
|
||||
let effect: UIGlassEffect | UIVisualEffect;
|
||||
const config: GlassEffectConfig | null = typeof value !== 'string' ? value : null;
|
||||
const variant = config ? config.variant : (value as GlassEffectVariant);
|
||||
const defaultDuration = 0.3;
|
||||
const duration = config ? (config.animateChangeDuration ?? defaultDuration) : defaultDuration;
|
||||
if (!value || ['identity', 'none'].includes(variant)) {
|
||||
// empty effect
|
||||
effect = UIVisualEffect.new();
|
||||
} else {
|
||||
effect = UIGlassEffect.effectWithStyle(this.toUIGlassStyle(variant));
|
||||
if (config) {
|
||||
(effect as UIGlassEffect).interactive = !!config.interactive;
|
||||
if (config.tint) {
|
||||
(effect as UIGlassEffect).tintColor = typeof config.tint === 'string' ? new Color(config.tint).ios : config.tint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (effect) {
|
||||
// animate effect changes
|
||||
UIView.animateWithDurationAnimations(duration, () => {
|
||||
this.nativeViewProtected.effect = effect;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public toUIGlassStyle(value?: GlassEffectVariant) {
|
||||
if (supportsGlass()) {
|
||||
switch (value) {
|
||||
case 'regular':
|
||||
return UIGlassEffectStyle?.Regular ?? 0;
|
||||
case 'clear':
|
||||
return UIGlassEffectStyle?.Clear ?? 1;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
this._applyGlassEffect(value, {
|
||||
effectType: 'glass',
|
||||
targetView: this.nativeViewProtected,
|
||||
toGlassStyleFn: toUIGlassStyle,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function toUIGlassStyle(value?: GlassEffectVariant) {
|
||||
if (supportsGlass()) {
|
||||
switch (value) {
|
||||
case 'regular':
|
||||
return UIGlassEffectStyle?.Regular ?? 0;
|
||||
case 'clear':
|
||||
return UIGlassEffectStyle?.Clear ?? 1;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user