From a14becdc6af723d6c1d30c35af3074f79ae2da0b Mon Sep 17 00:00:00 2001 From: Dimitris-Rafail Katsampas Date: Mon, 1 May 2023 22:29:36 +0300 Subject: [PATCH] fix(ios): FormattedString and Span a11y font scale (#10281) --- apps/automated/src/test-runner.ts | 3 ++ .../src/ui/styling/style-properties-tests.ts | 20 -------- .../src/ui/text-base/text-base-tests.ts | 23 +++++++++ .../core/ui/text-base/formatted-string.d.ts | 21 +++++++-- .../core/ui/text-base/formatted-string.ts | 32 +++++++++++++ packages/core/ui/text-base/index.ios.ts | 47 +++++++++++-------- packages/core/ui/text-base/span.d.ts | 21 +++++++-- packages/core/ui/text-base/span.ts | 21 +++++++++ 8 files changed, 142 insertions(+), 46 deletions(-) create mode 100644 apps/automated/src/ui/text-base/text-base-tests.ts diff --git a/apps/automated/src/test-runner.ts b/apps/automated/src/test-runner.ts index b7571723d..3e97b5673 100644 --- a/apps/automated/src/test-runner.ts +++ b/apps/automated/src/test-runner.ts @@ -227,6 +227,9 @@ allTests['LISTVIEW'] = listViewTests; import * as activityIndicatorTests from './ui/activity-indicator/activity-indicator-tests'; allTests['ACTIVITY-INDICATOR'] = activityIndicatorTests; +import * as textBaseTests from './ui/text-base/text-base-tests'; +allTests['TEXT-BASE'] = textBaseTests; + import * as textFieldTests from './ui/text-field/text-field-tests'; allTests['TEXT-FIELD'] = textFieldTests; diff --git a/apps/automated/src/ui/styling/style-properties-tests.ts b/apps/automated/src/ui/styling/style-properties-tests.ts index d878bb2a2..2f8632276 100644 --- a/apps/automated/src/ui/styling/style-properties-tests.ts +++ b/apps/automated/src/ui/styling/style-properties-tests.ts @@ -580,26 +580,6 @@ export function test_setting_font_properties_sets_native_font() { test_native_font('italic', 'bold'); } -export function test_native_font_size_with_a11y_font_scale() { - if (isIOS) { - const deviceFontScaleMock = 4.0; - - const page = helper.getCurrentPage(); - const testView = new Label(); - const layout = new StackLayout(); - layout.addChild(testView); - - page.content = layout; - - layout.style.iosAccessibilityAdjustsFontSize = true; - layout.style.fontScaleInternal = deviceFontScaleMock; - - const nativeFontSize = testView.nativeTextViewProtected.font.pointSize; - const expectedNativeFontSize = testView.style.fontInternal.fontSize * deviceFontScaleMock; - TKUnit.assertEqual(nativeFontSize, expectedNativeFontSize, 'View font size does not respect a11y font scaling'); - } -} - function test_native_font(style: 'normal' | 'italic', weight: '100' | '200' | '300' | 'normal' | '400' | '500' | '600' | 'bold' | '700' | '800' | '900') { const testView = new Button(); diff --git a/apps/automated/src/ui/text-base/text-base-tests.ts b/apps/automated/src/ui/text-base/text-base-tests.ts new file mode 100644 index 000000000..ee2ef510e --- /dev/null +++ b/apps/automated/src/ui/text-base/text-base-tests.ts @@ -0,0 +1,23 @@ +import * as TKUnit from '../../tk-unit'; +import * as helper from '../../ui-helper'; +import { FormattedString, isIOS, Label, Span, StackLayout } from '@nativescript/core'; + +export function test_native_font_size_with_a11y_font_scale() { + if (isIOS) { + const deviceFontScaleMock = 4.0; + + const page = helper.getCurrentPage(); + const testView = new Label(); + const layout = new StackLayout(); + layout.addChild(testView); + + page.content = layout; + + layout.style.iosAccessibilityAdjustsFontSize = true; + layout.style.fontScaleInternal = deviceFontScaleMock; + + const nativeFontSize = testView.nativeTextViewProtected.font.pointSize; + const expectedNativeFontSize = testView.style.fontInternal.fontSize * deviceFontScaleMock; + TKUnit.assertEqual(nativeFontSize, expectedNativeFontSize, 'View font size does not respect a11y font scaling'); + } +} diff --git a/packages/core/ui/text-base/formatted-string.d.ts b/packages/core/ui/text-base/formatted-string.d.ts index 6eb1ff90a..cfff33486 100644 --- a/packages/core/ui/text-base/formatted-string.d.ts +++ b/packages/core/ui/text-base/formatted-string.d.ts @@ -6,7 +6,7 @@ import { Span } from './span'; import { ObservableArray } from '../../data/observable-array'; import { ViewBase } from '../core/view-base'; import { Color } from '../../color'; -import { FontStyle, FontWeight } from '../styling/font'; +import { FontStyleType, FontWeightType } from '../styling/font'; import { CoreTypes } from '../../core-types'; /** @@ -36,12 +36,12 @@ export class FormattedString extends ViewBase { /** * Gets or sets the font style which will be used for all spans that doesn't have a specific value. */ - public fontStyle: FontStyle; + public fontStyle: FontStyleType; /** * Gets or sets the font weight which will be used for all spans that doesn't have a specific value. */ - public fontWeight: FontWeight; + public fontWeight: FontWeightType; /** * Gets or sets text decorations which will be used for all spans that doesn't have a specific value. @@ -57,4 +57,19 @@ export class FormattedString extends ViewBase { * Gets or sets the font background color which will be used for all spans that doesn't have a specific value. */ public backgroundColor: Color; + + /** + * Defines whether accessibility font scale should affect font size. + */ + iosAccessibilityAdjustsFontSize: boolean; + + /** + * Gets or sets the minimum accessibility font scale. + */ + iosAccessibilityMinFontScale: number; + + /** + * Gets or sets the maximum accessibility font scale. + */ + iosAccessibilityMaxFontScale: number; } diff --git a/packages/core/ui/text-base/formatted-string.ts b/packages/core/ui/text-base/formatted-string.ts index fdb243b47..f12a38eac 100644 --- a/packages/core/ui/text-base/formatted-string.ts +++ b/packages/core/ui/text-base/formatted-string.ts @@ -66,6 +66,27 @@ export class FormattedString extends ViewBase implements FormattedStringDefiniti this.style.backgroundColor = value; } + get iosAccessibilityAdjustsFontSize(): boolean { + return this.style.iosAccessibilityAdjustsFontSize; + } + set iosAccessibilityAdjustsFontSize(value: boolean) { + this.style.iosAccessibilityAdjustsFontSize = value; + } + + get iosAccessibilityMinFontScale(): number { + return this.style.iosAccessibilityMinFontScale; + } + set iosAccessibilityMinFontScale(value: number) { + this.style.iosAccessibilityMinFontScale = value; + } + + get iosAccessibilityMaxFontScale(): number { + return this.style.iosAccessibilityMaxFontScale; + } + set iosAccessibilityMaxFontScale(value: number) { + this.style.iosAccessibilityMaxFontScale = value; + } + get spans(): ObservableArray { if (!this._spans) { this._spans = new ObservableArray(); @@ -135,6 +156,12 @@ export class FormattedString extends ViewBase implements FormattedStringDefiniti style.on('textDecorationChange', this.onPropertyChange, this); style.on('colorChange', this.onPropertyChange, this); style.on('backgroundColorChange', this.onPropertyChange, this); + + // These handlers will trigger font scale update + style.on('iosAccessibilityAdjustsFontSizeChange', this.onPropertyChange, this); + style.on('iosAccessibilityMinFontScaleChange', this.onPropertyChange, this); + style.on('iosAccessibilityMaxFontScaleChange', this.onPropertyChange, this); + style.on('fontScaleInternalChange', this.onPropertyChange, this); } private removePropertyChangeHandler(span: Span) { @@ -147,6 +174,11 @@ export class FormattedString extends ViewBase implements FormattedStringDefiniti style.off('textDecorationChange', this.onPropertyChange, this); style.off('colorChange', this.onPropertyChange, this); style.off('backgroundColorChange', this.onPropertyChange, this); + + style.off('iosAccessibilityAdjustsFontSizeChange', this.onPropertyChange, this); + style.off('iosAccessibilityMinFontScaleChange', this.onPropertyChange, this); + style.off('iosAccessibilityMaxFontScaleChange', this.onPropertyChange, this); + style.off('fontScaleInternalChange', this.onPropertyChange, this); } private onPropertyChange(data: PropertyChangeData) { diff --git a/packages/core/ui/text-base/index.ios.ts b/packages/core/ui/text-base/index.ios.ts index 50ee3fb1e..ee4907e2c 100644 --- a/packages/core/ui/text-base/index.ios.ts +++ b/packages/core/ui/text-base/index.ios.ts @@ -194,28 +194,17 @@ export class TextBase extends TextBaseCommon { [fontScaleInternalProperty.setNative](value: number) { const nativeView = this.nativeTextViewProtected instanceof UIButton ? this.nativeTextViewProtected.titleLabel : this.nativeTextViewProtected; - const currentFont = this.style.fontInternal || Font.default.withFontSize(nativeView.font.pointSize); - - let finalValue; - if (this.iosAccessibilityAdjustsFontSize) { - finalValue = value; - - if (this.iosAccessibilityMinFontScale && this.iosAccessibilityMinFontScale > value) { - finalValue = this.iosAccessibilityMinFontScale; - } - if (this.iosAccessibilityMaxFontScale && this.iosAccessibilityMaxFontScale < value) { - finalValue = this.iosAccessibilityMaxFontScale; - } - } else { - finalValue = 1.0; - } - - const newFont = currentFont.withFontScale(finalValue); - this.style.fontInternal = newFont; + const font = this.style.fontInternal || Font.default.withFontSize(nativeView.font.pointSize); + const finalValue = adjustMinMaxFontScale(value, this); // Request layout on font scale as it's not done automatically - if (currentFont.fontScale !== finalValue) { + if (font.fontScale !== finalValue) { + this.style.fontInternal = font.withFontScale(finalValue); this.requestLayout(); + } else { + if (!this.style.fontInternal) { + this.style.fontInternal = font; + } } } @@ -360,7 +349,8 @@ export class TextBase extends TextBaseCommon { } createMutableStringDetails(span: Span, text: string, index?: number): any { - const font = new Font(span.style.fontFamily, span.style.fontSize, span.style.fontStyle, span.style.fontWeight); + const fontScale = adjustMinMaxFontScale(span.style.fontScaleInternal, span); + const font = new Font(span.style.fontFamily, span.style.fontSize, span.style.fontStyle, span.style.fontWeight, fontScale); const iosFont = font.getUIFont(this.nativeTextViewProtected.font); const backgroundColor = (span.style.backgroundColor || (span.parent).backgroundColor || (span.parent.parent).backgroundColor); @@ -485,3 +475,20 @@ function isStringTappable(formattedString: FormattedString) { return false; } + +function adjustMinMaxFontScale(value: number, view: TextBase | Span) { + let finalValue; + if (view.iosAccessibilityAdjustsFontSize) { + finalValue = value; + + if (view.iosAccessibilityMinFontScale && view.iosAccessibilityMinFontScale > value) { + finalValue = view.iosAccessibilityMinFontScale; + } + if (view.iosAccessibilityMaxFontScale && view.iosAccessibilityMaxFontScale < value) { + finalValue = view.iosAccessibilityMaxFontScale; + } + } else { + finalValue = 1.0; + } + return finalValue; +} diff --git a/packages/core/ui/text-base/span.d.ts b/packages/core/ui/text-base/span.d.ts index e77b2c79d..9ab2b9919 100644 --- a/packages/core/ui/text-base/span.d.ts +++ b/packages/core/ui/text-base/span.d.ts @@ -1,6 +1,6 @@ import { Color } from '../../color'; import { ViewBase } from '../core/view-base'; -import { FontStyle, FontWeight } from '../styling/font'; +import { FontStyleType, FontWeightType } from '../styling/font'; import { CoreTypes } from '../../core-types'; /** @@ -20,12 +20,12 @@ export class Span extends ViewBase { /** * Gets or sets the font style of the span. */ - public fontStyle: FontStyle; + public fontStyle: FontStyleType; /** * Gets or sets the font weight of the span. */ - public fontWeight: FontWeight; + public fontWeight: FontWeightType; /** * Gets or sets text decorations for the span. @@ -42,6 +42,21 @@ export class Span extends ViewBase { */ public backgroundColor: Color; + /** + * Defines whether accessibility font scale should affect font size. + */ + iosAccessibilityAdjustsFontSize: boolean; + + /** + * Gets or sets the minimum accessibility font scale. + */ + iosAccessibilityMinFontScale: number; + + /** + * Gets or sets the maximum accessibility font scale. + */ + iosAccessibilityMaxFontScale: number; + /** * Gets or sets the text for the span. */ diff --git a/packages/core/ui/text-base/span.ts b/packages/core/ui/text-base/span.ts index da3130b25..83dd56bff 100644 --- a/packages/core/ui/text-base/span.ts +++ b/packages/core/ui/text-base/span.ts @@ -62,6 +62,27 @@ export class Span extends ViewBase implements SpanDefinition { this.style.backgroundColor = value; } + get iosAccessibilityAdjustsFontSize(): boolean { + return this.style.iosAccessibilityAdjustsFontSize; + } + set iosAccessibilityAdjustsFontSize(value: boolean) { + this.style.iosAccessibilityAdjustsFontSize = value; + } + + get iosAccessibilityMinFontScale(): number { + return this.style.iosAccessibilityMinFontScale; + } + set iosAccessibilityMinFontScale(value: number) { + this.style.iosAccessibilityMinFontScale = value; + } + + get iosAccessibilityMaxFontScale(): number { + return this.style.iosAccessibilityMaxFontScale; + } + set iosAccessibilityMaxFontScale(value: number) { + this.style.iosAccessibilityMaxFontScale = value; + } + get text(): string { return this._text; }