feat(core): add sys:// support for SF Symbol usage on images with effects (#10555)

This commit is contained in:
Eduardo Speroni
2024-06-26 19:34:24 -03:00
committed by GitHub
parent 84e1a67d6d
commit d678915234
15 changed files with 269 additions and 13 deletions

View File

@ -4,12 +4,13 @@ import { booleanConverter } from '../core/view-base';
import { CoreTypes } from '../../core-types';
import { ImageAsset } from '../../image-asset';
import { ImageSource } from '../../image-source';
import { isDataURI, isFontIconURI, isFileOrResourcePath, RESOURCE_PREFIX } from '../../utils';
import { isDataURI, isFontIconURI, isFileOrResourcePath, RESOURCE_PREFIX, SYSTEM_PREFIX } from '../../utils';
import { Color } from '../../color';
import { Style } from '../styling/style';
import { Length } from '../styling/style-properties';
import { Property, InheritedCssProperty } from '../core/properties';
import { Trace } from '../../trace';
import { ImageSymbolEffect, ImageSymbolEffects } from './symbol-effects';
@CSSType('Image')
export abstract class ImageBase extends View implements ImageDefinition {
@ -75,13 +76,21 @@ export abstract class ImageBase extends View implements ImageDefinition {
}
} else if (isFileOrResourcePath(value)) {
if (value.indexOf(RESOURCE_PREFIX) === 0) {
const resPath = value.substr(RESOURCE_PREFIX.length);
const resPath = value.slice(RESOURCE_PREFIX.length);
if (sync) {
imageLoaded(ImageSource.fromResourceSync(resPath));
} else {
this.imageSource = null;
ImageSource.fromResource(resPath).then(imageLoaded);
}
} else if (value.indexOf(SYSTEM_PREFIX) === 0) {
const sysPath = value.slice(SYSTEM_PREFIX.length);
if (sync) {
imageLoaded(ImageSource.fromSystemImageSync(sysPath));
} else {
this.imageSource = null;
ImageSource.fromSystemImage(sysPath).then(imageLoaded);
}
} else {
if (sync) {
imageLoaded(ImageSource.fromFileSync(value));
@ -178,3 +187,13 @@ export const decodeWidthProperty = new Property<ImageBase, CoreTypes.LengthType>
valueConverter: Length.parse,
});
decodeWidthProperty.register(ImageBase);
/**
* iOS only
*/
export const iosSymbolEffectProperty = new Property<ImageBase, ImageSymbolEffect | ImageSymbolEffects>({
name: 'iosSymbolEffect',
});
iosSymbolEffectProperty.register(ImageBase);
export { ImageSymbolEffect, ImageSymbolEffects };

View File

@ -6,6 +6,7 @@ import { Color } from '../../color';
import { Property, InheritedCssProperty } from '../core/properties';
import { CoreTypes } from '../../core-types';
export { ImageSymbolEffect, ImageSymbolEffects } from './image-common';
/**
* Represents a class that provides functionality for loading and streching image(s).
*/

View File

@ -1,9 +1,10 @@
import { ImageBase, stretchProperty, imageSourceProperty, tintColorProperty, srcProperty } from './image-common';
import { ImageBase, stretchProperty, imageSourceProperty, tintColorProperty, srcProperty, iosSymbolEffectProperty, ImageSymbolEffect, ImageSymbolEffects } from './image-common';
import { ImageSource } from '../../image-source';
import { ImageAsset } from '../../image-asset';
import { Color } from '../../color';
import { Trace } from '../../trace';
import { layout, queueGC } from '../../utils';
import { SDK_VERSION } from '../../utils/constants';
export * from './image-common';
@ -194,4 +195,16 @@ export class Image extends ImageBase {
[srcProperty.setNative](value: string | ImageSource | ImageAsset) {
this._createImageSourceFromSrc(value);
}
[iosSymbolEffectProperty.setNative](value: ImageSymbolEffect | ImageSymbolEffects) {
if (SDK_VERSION < 17) {
return;
}
const symbol = typeof value === 'string' ? ImageSymbolEffect.fromSymbol(value) : value;
if (symbol && symbol.effect) {
this.nativeViewProtected.addSymbolEffectOptionsAnimatedCompletion(symbol.effect, symbol.options || NSSymbolEffectOptions.optionsWithRepeating(), true, symbol.completion || null);
} else {
this.nativeViewProtected.removeAllSymbolEffects();
}
}
}

View File

@ -0,0 +1,37 @@
export enum ImageSymbolEffects {
Appear = 'appear',
AppearUp = 'appearUp',
AppearDown = 'appearDown',
Bounce = 'bounce',
BounceUp = 'bounceUp',
BounceDown = 'bounceDown',
Disappear = 'disappear',
DisappearDown = 'disappearDown',
DisappearUp = 'disappearUp',
Pulse = 'pulse',
Scale = 'scale',
ScaleDown = 'scaleDown',
ScaleUp = 'scaleUp',
VariableColor = 'variableColor',
Breathe = 'breathe',
BreathePlain = 'breathePlain',
BreathePulse = 'breathePulse',
Rotate = 'rotate',
RotateClockwise = 'rotateClockwise',
RotateCounterClockwise = 'rotateCounterClockwise',
Wiggle = 'wiggle',
WiggleBackward = 'wiggleBackward',
WiggleClockwise = 'wiggleClockwise',
WiggleCounterClockwise = 'wiggleCounterClockwise',
WiggleDown = 'wiggleDown',
WiggleForward = 'wiggleForward',
WiggleUp = 'wiggleUp',
WiggleLeft = 'wiggleLeft',
WiggleRight = 'wiggleRight',
}
export class ImageSymbolEffectCommon {
effect?: NSSymbolEffect;
options?: NSSymbolEffectOptions;
completion?: (context: UISymbolEffectCompletionContext) => void;
}

View File

@ -0,0 +1,9 @@
import { ImageSymbolEffectCommon, ImageSymbolEffects } from './symbol-effects-common';
import type { ImageSymbolEffect as ImageSymbolEffectDefinition } from './symbol-effects.d.ts';
export { ImageSymbolEffects };
export const ImageSymbolEffect: typeof ImageSymbolEffectDefinition = class ImageSymbolEffect extends ImageSymbolEffectCommon implements ImageSymbolEffectDefinition {
static fromSymbol(symbol: string): ImageSymbolEffectDefinition {
return new ImageSymbolEffect();
}
};

View File

@ -0,0 +1,13 @@
export { ImageSymbolEffects } from './symbol-effects-common';
/**
* iOS only
* Symbol effects: https://developer.apple.com/documentation/symbols?language=objc
*/
export class ImageSymbolEffect {
effect?: NSSymbolEffect;
options?: NSSymbolEffectOptions;
completion?: (context: UISymbolEffectCompletionContext) => void;
constructor(symbol: NSSymbolEffect);
static fromSymbol(symbol: string): ImageSymbolEffect | null;
}

View File

@ -0,0 +1,95 @@
import { SDK_VERSION } from '../../utils/constants';
import { ImageSymbolEffectCommon, ImageSymbolEffects } from './symbol-effects-common';
import type { ImageSymbolEffect as ImageSymbolEffectDefinition } from './symbol-effects.d.ts';
export const ImageSymbolEffect: typeof ImageSymbolEffectDefinition = class ImageSymbolEffect extends ImageSymbolEffectCommon implements ImageSymbolEffectDefinition {
constructor(symbol: NSSymbolEffect) {
super();
this.effect = symbol;
}
static fromSymbol(symbol: string): ImageSymbolEffectDefinition | null {
if (SDK_VERSION < 17) {
return null;
}
switch (symbol) {
case ImageSymbolEffects.Appear:
return new ImageSymbolEffect(NSSymbolAppearEffect.effect());
case ImageSymbolEffects.AppearUp:
return new ImageSymbolEffect(NSSymbolAppearEffect.appearUpEffect());
case ImageSymbolEffects.AppearDown:
return new ImageSymbolEffect(NSSymbolAppearEffect.appearDownEffect());
case ImageSymbolEffects.Bounce:
return new ImageSymbolEffect(NSSymbolBounceEffect.effect());
case ImageSymbolEffects.BounceUp:
return new ImageSymbolEffect(NSSymbolBounceEffect.bounceUpEffect());
case ImageSymbolEffects.BounceDown:
return new ImageSymbolEffect(NSSymbolBounceEffect.bounceDownEffect());
case ImageSymbolEffects.Disappear:
return new ImageSymbolEffect(NSSymbolDisappearEffect.effect());
case ImageSymbolEffects.DisappearDown:
return new ImageSymbolEffect(NSSymbolDisappearEffect.disappearDownEffect());
case ImageSymbolEffects.DisappearUp:
return new ImageSymbolEffect(NSSymbolDisappearEffect.disappearUpEffect());
case ImageSymbolEffects.Pulse:
return new ImageSymbolEffect(NSSymbolPulseEffect.effect());
case ImageSymbolEffects.Scale:
return new ImageSymbolEffect(NSSymbolScaleEffect.effect());
case ImageSymbolEffects.ScaleDown:
return new ImageSymbolEffect(NSSymbolScaleEffect.scaleDownEffect());
case ImageSymbolEffects.ScaleUp:
return new ImageSymbolEffect(NSSymbolScaleEffect.scaleUpEffect());
case ImageSymbolEffects.VariableColor:
return new ImageSymbolEffect(NSSymbolVariableColorEffect.effect());
}
if (SDK_VERSION < 18) {
return null;
}
// TODO: remove ts-expect-error once we bump the types package
switch (symbol) {
case ImageSymbolEffects.Breathe:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolBreatheEffect.effect());
case ImageSymbolEffects.BreathePlain:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolBreatheEffect.breathePlainEffect());
case ImageSymbolEffects.Rotate:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolRotateEffect.effect());
case ImageSymbolEffects.RotateClockwise:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolRotateEffect.rotateClockwiseEffect());
case ImageSymbolEffects.RotateCounterClockwise:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolRotateEffect.rotateCounterClockwiseEffect());
case ImageSymbolEffects.Wiggle:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolWiggleEffect.effect());
case ImageSymbolEffects.WiggleBackward:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolWiggleEffect.wiggleBackwardEffect());
case ImageSymbolEffects.WiggleClockwise:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolWiggleEffect.wiggleClockwiseEffect());
case ImageSymbolEffects.WiggleCounterClockwise:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolWiggleEffect.wiggleCounterClockwiseEffect());
case ImageSymbolEffects.WiggleDown:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolWiggleEffect.wiggleDownEffect());
case ImageSymbolEffects.WiggleForward:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolWiggleEffect.wiggleForwardEffect());
case ImageSymbolEffects.WiggleUp:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolWiggleEffect.wiggleUpEffect());
case ImageSymbolEffects.WiggleLeft:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolWiggleEffect.wiggleLeftEffect());
case ImageSymbolEffects.WiggleRight:
// @ts-expect-error added on iOS 18
return new ImageSymbolEffect(NSSymbolWiggleEffect.wiggleRightEffect());
}
return null;
}
};

View File

@ -31,7 +31,7 @@ export { GesturesObserver, TouchAction, GestureTypes, GestureStateTypes, SwipeDi
export type { GestureEventData, GestureEventDataWithState, TapGestureEventData, PanGestureEventData, PinchGestureEventData, RotationGestureEventData, SwipeGestureEventData, TouchGestureEventData, TouchAnimationOptions, VisionHoverOptions } from './gestures';
export { HtmlView } from './html-view';
export { Image } from './image';
export { Image, ImageSymbolEffect, ImageSymbolEffects } from './image';
export { Cache as ImageCache } from './image-cache';
export type { DownloadError, DownloadRequest, DownloadedData } from './image-cache';
export { Label } from './label';