Allow/prevent accessibility font resizing of a Label - nativescript

I am implementing font size accessibility in a NativeScript-Vue app.
I want to allow or prevent Label resizing through an XML attribute for both Android and iOS, but behavior and implementation on the platforms are different.
Android
All labels are scaled by default. If I want a label not to resize, I need to call the function setTextSize in the loaded event, following this solution.
<Label text="Not resizable" #loaded="$androidFixedLabelSize($event, 70)" />
Vue.prototype.$androidFixedLabelSize = function({ object }, fontSize) {
if (object.android)
object.nativeView.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, utils.layout.toDevicePixels(fontSize));
}
iOS
Labels are not scaled by default. To resize a label, I need to use nativescript-accessibility-ext plugin and add the attribute accessibilityAdjustsFontSize.
<Label text="Resizable" accessibilityAdjustsFontSize="true" />
Having to add one attribute for fixed Android and one for resizable iOS is a bit cumbersome.
I was thinking of having all labels resizable by default, and specify if I want one not to resize through a directive or an attribute.
Can I achieve this through a custom directive? Or something else?
Update
I was able to prevent resizing on Android through a directive without hardcoding font size, but there is a problem: update is triggered only for few labels. el.nativeView.android in bind and inserted hooks is undefined.
Vue.directive("noresize", {
update: el => {
if (el.nativeView.android) {
el.nativeView.android.setTextSize(android.util.TypedValue.COMPLEX_UNIT_DIP, el.nativeView.fontSize);
} else {
// iOS code
}
}
});
On iOS, I would like to simply set accessibilityAdjustsFontSize="false", but this implies that it is true by default.
So the next question is: how do I set accessibilityAdjustsFontSize="true" on all Label components on iOS?

Thanks to Morten Sjøgren, developer of #nota/nativescript-accessibility-ext, I was able to add a global event. Accessibility resizing is now applied on all Label components, unless the attribute noResize is true.
app.js
import '#nota/nativescript-accessibility-ext';
import { Label } from 'tns-core-modules/ui/label';
// code
Label.on(Label.loadedEvent, ({ object }) => {
if (object.noResize) {
if (object.android) {
object.nativeView.setTextSize(android.util.TypedValue.COMPLEX_UNIT_DIP, object.fontSize);
}
} else {
object.accessibilityAdjustsFontSize = "true";
}
});
<Label text="Don't resize" noResize="true" />

Related

How to get specific RadCalendar Day Cell on a cellTap Event and add custom style to it

I'm using RadCalendar in my NativeScript Project, the problem is I want to add custom styling on specific day cell from a cellTap Event.
So I started with listening to the Event
<RadCalendar (cellTap)="onCellTap($event)"></RadCalendar>
in my component.ts file:
onCellTap(args: CalendarCellTapEventData) {
// here, it return the whole RadCalendar Object
console.log(args.object);
// and in the line below it returns the native cell Element
console.log(args.cell)
}
I tried to change directly the CSS properties like this:
args.object.style.backgroundColor = new Color('#ff0000')
but it doesn't work.
Is there a way to perform the required behaviour ?
I don't think adjusting cell style upon tap is supported but it should be possible. To adjust background color of cell,
import { CalendarCellTapEventData } from "nativescript-ui-calendar";
import { Color } from "tns-core-modules/color";
onCellTap(event: CalendarCellTapEventData) {
const color = new Color('#ff0000');
if (color.android) {
// https://docs.telerik.com/devtools/android/AndroidControlsDoc/com/telerik/widget/calendar/CalendarDayCell.html
event.cell.setBackgroundColor(color.android);
} else {
// https://docs.telerik.com/devtools/ios/api/Classes/TKCalendarDayCell.html
event.cell.style().backgroundColor = color.ios;
}
}

How to change height of progress component in NativeScript

I'm trying to change the height of the progress component in nativescript like this:
<Progress value="{{ progress }}" maxValue="{{totalTime}}" height="100">
</Progress>
But the height property has no effect. How do I change the height of this component?
You can change the height of ProgressBar using the height property (due to some spcific in the native controls).
However one of the gret advantages of NativeScript is that you have direct access to the native APIs for both iOS and Android. So after some small research here is how I managed to change the default scale (there might be even better approaches - this is one of the shortest I came upon).
TypeScript example:
Use the laoded event to get the reference to your progress bar in the code behind.
<Progress minValue="0" maxValue="100" value="25" loaded="onProgressLoaded"/>
Then in the code behind access the native controls
import { Progress } from 'ui/progress';
import { isAndroid, isIOS } from "platform"
declare let CGAffineTransformMakeScale: any; // or use tns-platform-declarations instead of casting to any
export function onProgressLoaded(args: EventData) {
let progress = <Progress>args.object;
if (isAndroid) {
progress.android.setScaleY(5); // progress.android === android.widget.ProgressBar
} else if (isIOS) {
let transform = CGAffineTransformMakeScale(1.0, 5.0);
progress.ios.transform = transform; // progress.ios === UIProgressView
}
}
Full demo app can be found here
You can directly set scaleY attribute of the progress element.
Example:
<Progress scaleY="2" value="{{ progress }}" maxValue="{{totalTime}}" height="100"></Progress>
This will make it 2 times wider in the vertical (Y) axis.

How to ignore accessibility fonts resize in NativeScript?

I'm using NativeScript with Angular.
Is there an attribute to specify on Labels/buttons to ignore the font increase due to accessibility settings?
I need a solution for both android and ios.
Thank you!
If anybody interested, I found a workaround of using FormattedString for Labels, this object does not scale.
I just had a look a NativeScript own code and noticed this:
https://github.com/NativeScript/NativeScript/blob/master/tns-core-modules/ui/text-base/text-base.android.ts#L216
You could try this:
HTML:
<Label text="Fixed size text" (loaded)="fixedFontSize($event)"></Label>
TypeScript
import { Label } from 'ui/label';
...
fixedFontSize({object}, fontSize = 20) {
const label = <Label>object;
if (label.android) {
label.nativeView.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, utils.layout.toDevicePixels(fontSize));
}
}
This should lock the fontSize of the label, until the CSS is changed.

Replace one component with another using animation

I'm looking to animate a text field into view and a button out of view at the same time, so that it looks like the text field is replacing the button. (They are both equal size and take up the same area of the screen).
What's the best way to do this using React Native animation?
At this point, I am rendering the button if one of my state values is false, and the text field if it is true.
You can animate any style property in react-native using the Animated API.
If you are able to represent the changes in a sequence of style changes, the Animated API can do it. For instance animating the opacity from 1 to 0 and back to 1 will give a nice fade in fade out effect. The docs explain the Animations much more clearly
Also you can you selective rendering to mount or hide the component
<View style={{/*style props that need to be animated*/}}
{ boolShowText? <Text/> : <View/> }
</View>
The fading example as found in react-native docs
class FadeInView extends React.Component {
constructor(props) {
super(props);
this.state = {
fadeAnim: new Animated.Value(0), // init opacity 0
};
}
componentDidMount() {
Animated.timing( // Uses easing functions
this.state.fadeAnim, // The value to drive
{toValue: 1}, // Configuration
).start(); // Don't forget start!
}
render() {
return (
<Animated.View // Special animatable View
style={{opacity: this.state.fadeAnim}}> // Binds
{this.props.children}
</Animated.View>
);
}
}

Optimizing AngularJS directive that updates ng-model and ng-style on focus

I'm new to AngularJS and I'm making made a couple of custom Angular directives to do what I used to do with Jquery, however there is one case where I'm not sure if I'm doing it the "the angular way". It works but I think there might be a better way.
I want to do is this for a search box:
When focus is on the search box the app must change the color of the text in the box from grey to black. The app must also then check the current text in the box, if it is the default text then the app must clear the text.
When the box loses focus (blur) the app must change the box's text back to grey. It must then put back the default text only if the text box is empty upon losing focus.
Here is a jsfiddle that has a directive set up to do all of this perfectly.
http://jsfiddle.net/Rick_KLN/5N73M/2/
However I suspect there is an even better way to do it.
It seems like all three those variables in the controller should be unnecessary.
I also seems like having 4 if, else statements is too much and that binding to all the events is overkill seeing as only focus and blur are used and they are specified in the if statements.
Any ideas on optimizing this directive?
The "default text" behavior you are looking for is automatically handled by the HTML5 placeholder attribute. It is supported in just about any modern browser, and can be styled using CSS, as follows:
Markup:
<input id="searchBar" type="text" placeholder="Search" />
CSS:
#searchBar { color: #858585; }
#searchBar:focus { color: #2c2c2c; }
#searchBar:focus::-webkit-input-placeholder { color: transparent; }
#searchBar:focus::-moz-placeholder { color: transparent; }
#searchBar:focus:-moz-placeholder { color: transparent; }
#searchBar:focus:-ms-input-placeholder { color: transparent; }
It's that simple.
Notes:
The pseudo-elements & pseudo-classes (-webkit-input-placeholder, etc) are what hide the placeholder text on focus. Normally it stays until you start typing.
I forked your original jsFiddle. It's not really an AngularJS app anymore:
http://jsfiddle.net/Smudge/RR9me/
For older browsers: You can still use the same code as above, but you could use Modernizr (or something similar) to fall-back to a javascript-only approach if the attribute is not supported.
You can create a custom directive that requires the ng-model directive and then within your directive's link function supply a fourth parameter that is a reference to the model. This will allow you to watch the model for changes and react accordingly. Here is the fiddle:
http://jsfiddle.net/brettlaforge/6t39j/3/
var app = angular.module('app', []);
app.directive('searchbar', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, model) {
var options = scope.$eval(attrs.searchbar);
scope.$watch(attrs.ngModel, function(value) {
// If the input element is currently focused.
if (!elm.is(":focus")) {
// If the input is empty.
if (value === undefined || value === "") {
// Set the input to the default. This will not update the controller's model.
elm.val(options.default);
}
}
});
elm.on('focus', function(event) {
// If the input is equal to the default, replace it with an empty string.
if (elm.val() === options.default) {
elm.val('');
}
});
elm.on('focusout', function(event) {
// If the input is empty, replace it with the default.
if (elm.val() === '') {
elm.val(options.default);
}
});
}
};
});
function FormCtrl($scope) {
$scope.search = "";
}​

Resources