From 226dc85efb6cb7e8cde39375c740cec501334b7c Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Mon, 18 Aug 2025 22:17:04 -0700 Subject: [PATCH] feat(tabs): iconSource support for sys:// and font:// icons closes https://github.com/NativeScript/NativeScript/issues/10729 --- packages/core/image-source/index.d.ts | 4 ++-- packages/core/ui/tab-view/index.ios.ts | 26 +++++++++++++++++--------- packages/core/utils/common.ts | 13 +++++++++---- packages/core/utils/index.d.ts | 6 ++++++ 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/packages/core/image-source/index.d.ts b/packages/core/image-source/index.d.ts index 5394b6831..95d086d47 100644 --- a/packages/core/image-source/index.d.ts +++ b/packages/core/image-source/index.d.ts @@ -65,13 +65,13 @@ export class ImageSource { * Loads this instance from the specified system image name. * @param name the name of the system image */ - static fromSystemImageSync(name: string, instance: ImageBase): ImageSource; + static fromSystemImageSync(name: string, instance?: ImageBase): ImageSource; /** * Loads this instance from the specified system image name asynchronously. * @param name the name of the system image */ - static fromSystemImage(name: string, instance: ImageBase): Promise; + static fromSystemImage(name: string, instance?: ImageBase): Promise; /** * Loads this instance from the specified file. diff --git a/packages/core/ui/tab-view/index.ios.ts b/packages/core/ui/tab-view/index.ios.ts index 5da4665dd..e23b06cd2 100644 --- a/packages/core/ui/tab-view/index.ios.ts +++ b/packages/core/ui/tab-view/index.ios.ts @@ -12,7 +12,8 @@ import { CoreTypes } from '../../core-types'; import { ImageSource } from '../../image-source'; import { profile } from '../../profiling'; import { Frame } from '../frame'; -import { layout } from '../../utils'; +import { layout } from '../../utils/layout-helper'; +import { isFontIconURI, isSystemURI, SYSTEM_PREFIX } from '../../utils/common'; import { SDK_VERSION } from '../../utils/constants'; import { Device } from '../../platform'; export * from './tab-view-common'; @@ -239,7 +240,7 @@ export class TabViewItem extends TabViewItemBase { const parent = this.parent; const controller = this.__controller; if (parent && controller) { - const icon = parent._getIcon(this.iconSource); + const icon = parent._getIcon(this); const index = parent.items.indexOf(this); const title = getTransformedText(this.title, this.style.textTransform); @@ -456,7 +457,7 @@ export class TabView extends TabViewBase { items.forEach((item, i) => { const controller = this.getViewController(item); - const icon = this._getIcon(item.iconSource); + const icon = this._getIcon(item); const tabBarItem = UITabBarItem.alloc().initWithTitleImageTag(item.title || '', icon, i); updateTitleAndIconPositions(item, tabBarItem, controller); @@ -492,20 +493,27 @@ export class TabView extends TabViewBase { } } - public _getIcon(iconSource: string): UIImage { - if (!iconSource) { + public _getIcon(item: TabViewItem): UIImage { + if (!item || !item.iconSource) { return null; } - let image: UIImage = this._iconsCache[iconSource]; + let image: UIImage = this._iconsCache[item.iconSource]; if (!image) { - const is = ImageSource.fromFileOrResourceSync(iconSource); + let is: ImageSource; + if (isSystemURI(item.iconSource)) { + is = ImageSource.fromSystemImageSync(item.iconSource.slice(SYSTEM_PREFIX.length)); + } else if (isFontIconURI(item.iconSource)) { + is = ImageSource.fromFontIconCodeSync(item.iconSource, item.style.fontInternal, item.style.color); + } else { + is = ImageSource.fromFileOrResourceSync(item.iconSource); + } if (is && is.ios) { const originalRenderedImage = is.ios.imageWithRenderingMode(this._getIconRenderingMode()); - this._iconsCache[iconSource] = originalRenderedImage; + this._iconsCache[item.iconSource] = originalRenderedImage; image = originalRenderedImage; } else { - traceMissingIcon(iconSource); + traceMissingIcon(item.iconSource); } } diff --git a/packages/core/utils/common.ts b/packages/core/utils/common.ts index 2f8f46379..9f2aef35c 100644 --- a/packages/core/utils/common.ts +++ b/packages/core/utils/common.ts @@ -7,9 +7,10 @@ import { GC } from './index'; export * from './mainthread-helper'; export * from './macrotask-scheduler'; +export const FILE_PREFIX = 'file:///'; +export const FONT_PREFIX = 'font://'; export const RESOURCE_PREFIX = 'res://'; export const SYSTEM_PREFIX = 'sys://'; -export const FILE_PREFIX = 'file:///'; export function escapeRegexSymbols(source: string): string { const escapeRegex = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g; @@ -85,10 +86,14 @@ export function isFontIconURI(uri: string): boolean { if (!types.isString(uri)) { return false; } + return uri.trim().startsWith(FONT_PREFIX); +} - const firstSegment = uri.trim().split('//')[0]; - - return firstSegment && firstSegment.indexOf('font:') === 0; +export function isSystemURI(uri: string): boolean { + if (!types.isString(uri)) { + return false; + } + return uri.trim().startsWith(SYSTEM_PREFIX); } export function isDataURI(uri: string): boolean { diff --git a/packages/core/utils/index.d.ts b/packages/core/utils/index.d.ts index 79389d397..114258ac1 100644 --- a/packages/core/utils/index.d.ts +++ b/packages/core/utils/index.d.ts @@ -87,6 +87,12 @@ export function mainThreadify(func: Function): (...args: any[]) => void; */ export function isFontIconURI(uri: string): boolean; +/** + * Returns true if the specified URI is a system URI like "sys://...". + * @param uri The URI. + */ +export function isSystemURI(uri: string): boolean; + /** * Returns true if the specified path points to a resource or local file. * @param path The path.