@nativescript/core
Version:
A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.
121 lines • 5.41 kB
JavaScript
import { iosGlassEffectProperty } from '../../core/view';
import { LiquidGlassContainerCommon } from './liquid-glass-container-common';
import { toUIGlassStyle } from '../liquid-glass';
export class LiquidGlassContainer extends LiquidGlassContainerCommon {
constructor() {
super(...arguments);
this._normalizing = false;
}
createNativeView() {
// Keep UIVisualEffectView as the root to preserve interactive container effect
const effect = UIGlassContainerEffect.alloc().init();
effect.spacing = 8;
const effectView = UIVisualEffectView.alloc().initWithEffect(effect);
effectView.overrideUserInterfaceStyle = 2 /* UIUserInterfaceStyle.Dark */;
effectView.clipsToBounds = true;
effectView.autoresizingMask = 2 /* UIViewAutoresizing.FlexibleWidth */ | 16 /* UIViewAutoresizing.FlexibleHeight */;
// Add a host view for children so parent can lay them out normally
const host = UIView.new();
host.frame = effectView.bounds;
host.autoresizingMask = 2 /* UIViewAutoresizing.FlexibleWidth */ | 16 /* UIViewAutoresizing.FlexibleHeight */;
host.userInteractionEnabled = true;
effectView.contentView.addSubview(host);
this._contentHost = host;
return effectView;
}
_addViewToNativeVisualTree(child, atIndex) {
const parentNativeView = this._contentHost;
const childNativeView = child.nativeViewProtected;
if (parentNativeView && childNativeView) {
if (typeof atIndex !== 'number' || atIndex >= parentNativeView.subviews.count) {
parentNativeView.addSubview(childNativeView);
}
else {
parentNativeView.insertSubviewAtIndex(childNativeView, atIndex);
}
// Add outer shadow layer manually as it belongs to parent layer tree (this is needed for reusable views)
if (childNativeView.outerShadowContainerLayer && !childNativeView.outerShadowContainerLayer.superlayer) {
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;
}
// 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.
onLayout(left, top, right, bottom) {
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
stabilizeLayout() {
this._normalizeChildrenTransforms(true);
}
_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;
}
});
}
_normalizeChildrenTransforms(force = false) {
let changed = false;
const count = this.getChildrenCount?.() ?? 0;
for (let i = 0; i < count; i++) {
const child = this.getChildAt(i);
if (!child)
continue;
const tx = child.translateX || 0;
const ty = child.translateY || 0;
if (!tx && !ty)
continue;
const native = child.nativeViewProtected;
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 (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) {
this._applyGlassEffect(value, {
effectType: 'container',
targetView: this.nativeViewProtected,
toGlassStyleFn: toUIGlassStyle,
});
}
}
//# sourceMappingURL=index.ios.js.map