From 90a50e3d133bbc96746b120f4270cc106bccccfd Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Tue, 21 Oct 2025 17:48:34 -0700 Subject: [PATCH] feat(ios): iosGlassEffect and LiquidGlass containers --- apps/toolbox/src/pages/glass-effects.ts | 39 +++++-- apps/toolbox/src/pages/glass-effects.xml | 5 +- packages/core/ui/core/view/index.ios.ts | 102 +++++++++++++----- packages/core/ui/core/view/view-common.ts | 26 +++++ .../liquid-glass-container/index.ios.ts | 101 +++++++++++++---- .../liquid-glass-container-common.ts | 6 +- .../ui/layouts/liquid-glass/index.android.ts | 6 ++ .../core/ui/layouts/liquid-glass/index.d.ts | 7 ++ .../core/ui/layouts/liquid-glass/index.ios.ts | 60 ++++------- 9 files changed, 250 insertions(+), 102 deletions(-) diff --git a/apps/toolbox/src/pages/glass-effects.ts b/apps/toolbox/src/pages/glass-effects.ts index d2d7075e8..126b8ba49 100644 --- a/apps/toolbox/src/pages/glass-effects.ts +++ b/apps/toolbox/src/pages/glass-effects.ts @@ -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() { diff --git a/apps/toolbox/src/pages/glass-effects.xml b/apps/toolbox/src/pages/glass-effects.xml index 303c29e74..08755017b 100644 --- a/apps/toolbox/src/pages/glass-effects.xml +++ b/apps/toolbox/src/pages/glass-effects.xml @@ -24,7 +24,7 @@ - + @@ -47,6 +47,9 @@