How can I set the font-family of large titles on iOS? - nativescript

By attaching an event listener to the loaded-event on Page and getting the NativeView-object I've been able to set the prefersLargeTitle to true:
loaded(event){
const page = event.object;
if (isIOS) {
page.frame.ios.controller.navigationBar.prefersLargeTitles = true;
}
}
This works, but I would like to change the font-family of the large title. How can I do this in Nativescript?

Try adding the code below to your app.js
import {
isIOS
} from "tns-core-modules/platform";
import {
ActionBar
} from "tns-core-modules/ui/action-bar";
import {
Font
} from "tns-core-modules/ui/styling/font";
if (isIOS) {
ActionBar.prototype.originalSetColor = ActionBar.prototype.setColor;
ActionBar.prototype.setColor = function (navBar, color) {
ActionBar.prototype.originalSetColor.call(this, navBar, color);
var newDict = {
[NSFontAttributeName]: Font
.default
.withFontFamily(
"yourFontFamily")
.withFontSize(yourFontSize)
.getUIFont(UIFont
.systemFontOfSize(20)),
};
if (navBar.largeTitleTextAttributes) {
newDict[NSForegroundColorAttributeName] = navBar.largeTitleTextAttributes.valueForKey(NSForegroundColorAttributeName);
}
navBar.largeTitleTextAttributes = newDict;
};
}
You may still set the prefersLargeTitles flags as you are doing already.

Related

Set a NativeScript Label line height on iOS

I have a label with a large font size, and the default line-height is quite loose for my taste. I'd like to reduce it to less than the default.
Providing a line-height value larger than the font-size does increase the line spacing, but a smaller value (or negative value) does not reduce it to be smaller than the default on iOS.
From a GitHub issue, I got this snippet that I updated to work with the latest NS;
import { Label } from "#nativescript/core/ui/label";
export function setIOSLineHeight(label: Label, lineHeight: number){
const iosLabel = label.nativeView;
let attributedString;
if (iosLabel.attributedText) {
attributedString = iosLabel.attributedText;
} else {
attributedString = NSMutableAttributedString.alloc().initWithString(iosLabel.text);
}
let range = new NSRange({ location: 0, length: iosLabel.text.length });
const paragraphStyle = NSMutableParagraphStyle.alloc().init();
paragraphStyle.lineSpacing = 0;
paragraphStyle.minimumLineHeight = lineHeight;
paragraphStyle.maximumLineHeight = lineHeight;
attributedString.addAttributeValueRange(NSParagraphStyleAttributeName, paragraphStyle, range);
iosLabel.attributedText = attributedString;
}
However, calling this method in the mounted() lifecycle method does not have any effect for any value of lineHeight - even ones that do have an effect via the CSS property:
<template>
<Page ref="page">
<Label ref="topLine" text="Hello this is a text that flows onto multiple lines" textWrap="true" />
</Page>
</template>
<script>
import { isIOS } from 'tns-core-modules/platform';
import { setIOSLineHeight } from '../label-helper.ts';
export default {
mounted() {
if (isIOS) {
/* Has no effect, regardless of value */
setIOSLineHeight(this.$refs.topLine, 40);
}
}
}
</script>
<style lang="scss" scoped>
Label {
font-size: 60;
/* Does work */
line-height: 100;
/* Does not work */
line-height: 40;
}
</style>
How can I reduce the line height of my Label to a value smaller than the font size?
In NativeScript I have used the following code to handle both IOS and Android for Line Spacing
function labelLineHeight(nsLabel) {
if(isIOS){
var label= nsLabel.ios;
var attributedString;
if(label.atributedText){
attributedString = label.atributedText;
}
else{
attributedString=NSMutableAttributedString.alloc().initWithString(label.text);
}
var paragraphStyle = NSMutableParagraphStyle.alloc().init();
paragraphStyle.lineSpacing = 5;
var range= NSMakeRange(0, label.text.length);
attributedString.addAttributeValueRange(NSParagraphStyleAttributeName, paragraphStyle, range);
label.attributedText = attributedString;
}
if(isAndroid){
var label = nsLabel.android;
//Default spacing is 20% of text size
//setLineSpacing(add,multiplyby);
label.setLineSpacing(12, 1);
}
}
Also follow this thread for more inputs on the line-spacing. You can see the pull request too for reference.

Webview OnProgressChanged and progressbar

In a Xamarin application I have a CustomWebView renderer; I'm injecting in the view and displaying a progressbar in OnProgressChanged event of the WebChromeClient with the following code.
Init:
var progressBar = new Android.Widget.ProgressBar(_context, null, Android.Resource.Attribute.ProgressBarStyleHorizontal);
Control.SetWebViewClient(new CusWebViewClient($"javascript: {JavascriptFunction}"));
Control.SetWebChromeClient(new CusWebChromeClient(progressBar));
Control.AddView(progressBar);
CusWebChromeClient:
public class CusWebChromeClient : WebChromeClient
{
Android.Widget.ProgressBar progressBar;
public CusWebChromeClient(Android.Widget.ProgressBar progressBar)
{
this.progressBar = progressBar;
}
public override void OnProgressChanged(Android.Webkit.WebView view, int newProgress)
{
if (newProgress < 100 && progressBar.Visibility == ViewStates.Gone)
{
progressBar.Visibility = ViewStates.Visible;
}
progressBar.SetProgress(newProgress, true);
if (newProgress == 100)
{
//progressBar.Visibility = ViewStates.Gone;
}
}
}
The issue is that the progress bar is displayed really small like:
I need to display it with full display width and with a more heigth.
You forgot to set the width of progress bar.
You could use the screen width to set the progress bar in custom renderer. After that, it would be okay.
var width = (int)Application.Current.MainPage.Width;
Please note, 'Application' is an ambiguous reference between 'Android.App.Application' and 'Xamarin.Forms.Application'. You could add reference like below to fix it.
using Application = Xamarin.Forms.Application;
Change:
Control.AddView(progressBar);
To:
Control.AddView(progressBar, width, 30);

How do I initialise a NativeScript app fully programmatically (without XML)?

Here's what I have so far. The background goes green (the colour of the Page), but I'd expect a purple ContentView with some text inside to fill the page, too.
Is there anything further I'm missing?
import { on, run, launchEvent } from "tns-core-modules/application";
import { Frame } from "tns-core-modules/ui/frame/frame";
import { ContentView } from "tns-core-modules/ui/content-view/content-view";
import { TextBase } from "tns-core-modules/ui/text-base/text-base";
import { Page } from "tns-core-modules/ui/page/page";
on(launchEvent, (data) => {
const frame = new Frame();
const page = new Page();
page.backgroundColor = "green";
const contentView = new ContentView();
const textBase = new TextBase();
contentView.height = 100;
contentView.width = 100;
contentView.backgroundColor = "purple";
textBase.text = "Hello, world!";
contentView._addView(textBase);
page.bindingContext = contentView;
frame.navigate({ create: () => page });
data.root = page; // Incidentally, should this be the frame or the page?
});
run();
You are almost on track, you just need slight modification on your code.
import { on, run, launchEvent } from 'tns-core-modules/application';
import { Frame } from 'tns-core-modules/ui/frame/frame';
import { ContentView } from 'tns-core-modules/ui/content-view/content-view';
import { TextField } from 'tns-core-modules/ui/text-field';
import { Page } from 'tns-core-modules/ui/page/page';
run({
create: () => {
const frame = new Frame();
frame.navigate({
create: () => {
const page = new Page();
page.backgroundColor = "green";
const contentView = new ContentView();
const textField = new TextField();
contentView.height = 100;
contentView.width = 100;
contentView.backgroundColor = "purple";
textField.text = "Hello, world!";
contentView.content = textField;
page.content = contentView;
return page;
}
});
return frame;
}
});
You don't have to wait for launch event, you could set the root frame in run method itself.
In your code, you were creating the frame but never adding it to root UI element or mark the frame itself as root element
It's recommended to use .content to add child for a ContentView / Page as they are originally designed to hold one child element only.
Use TextField / TextView for input text, TextBase is just a base class.
It seems to me that you try to overcomplicate. You can replace XML with code just by implementing createPage method - Create a page via code.
I just modified default NS + TypeScript Playground template to operate without XML - NS + TypeScript template without XML.
I think you can't leave run as empty as it is expecting an entry to start the app. From {NS} website,
You can use this file to perform app-level initializations, but the
primary purpose of the file is to pass control to the app's root
module. To do this, you need to call the application.run() method and
pass a NavigationEntry with the desired moduleName as the path to the
root module relative to your /app folder.
if you look for run code in "tns-core-modules/application"
function run(entry) {
createRootFrame.value = false;
start(entry);
}
exports.run = run;
and
function start(entry) {
if (started) {
throw new Error("Application is already started.");
}
started = true;
mainEntry = typeof entry === "string" ? { moduleName: entry } : entry;
if (!androidApp.nativeApp) {
var nativeApp = getNativeApplication();
androidApp.init(nativeApp);
}
}

Transparent view

I would like to create a View (stage, window) with partially transparent background. I have an image containing alpha channel
I used this kind of scenes in JavaFx, where I had to set the scene fill to null and the root node background color to transparent. I tried the same with TornadoFX:
class NextRoundView : View("Következő kör") {
override val root = vbox {
style {
backgroundColor = multi(Color.TRANSPARENT)
backgroundImage = multi(URI.create("/common/rope-bg-500x300.png"))
backgroundRepeat = multi(BackgroundRepeat.NO_REPEAT
to BackgroundRepeat.NO_REPEAT)
}
prefWidth = 500.0
prefHeight = 300.0
spacing = 20.0
padding = insets(50, 20)
text("A text") {
font = Font.font(40.0)
alignment = Pos.CENTER
}
button("OK")
{
font = Font.font(20.0)
action {
close()
}
}
sceneProperty().addListener{ _,_,n ->
n.fill = null
}
}
}
I'm calling the view like this:
NextRoundView().apply {
openModal(stageStyle = StageStyle.TRANSPARENT, block = true)
}
However, the stage is still has background:
What have I missed?
You've made a couple of mistakes that causes this. First of all, you must never manually instantiate UICompoenents (View, Fragment). Doing so will make them miss important life cycle callbacks. One important callback is onDock, which is the perfect place to manipulate the assigned scene. Changing these two issues and also cleaning up some syntax leads to this code, which successfully makes the background transparent:
class MyApp : App(MyView::class)
class MyView : View() {
override val root = stackpane {
button("open").action {
find<NextRoundView>().openModal(stageStyle = StageStyle.TRANSPARENT, block = true)
}
}
}
class NextRoundView : View("Következő kör") {
override val root = vbox {
style {
backgroundColor += Color.TRANSPARENT
backgroundImage += URI.create("/common/rope-bg-500x300.png")
backgroundRepeat += BackgroundRepeat.NO_REPEAT to BackgroundRepeat.NO_REPEAT
}
prefWidth = 500.0
prefHeight = 300.0
spacing = 20.0
padding = insets(50, 20)
text("A text") {
font = Font.font(40.0)
alignment = Pos.CENTER
}
button("OK") {
font = Font.font(20.0)
action {
close()
}
}
}
override fun onDock() {
currentStage?.scene?.fill = null
}
}
Here is a screenshot of the app with the changes implemented:

firefox add-on sdk ToggleButton badgeColor displayed default red color

when I resized the firefox window to just not display my add-on's toggleButton, then resized the firefox window to display my add-on's toggleButton. The badgeColor was displayed with default color(red).
How should I do and solve this problem? And is this the firefox's bug?
var {ToggleButton} = require("sdk/ui/button/toggle");
var button = ToggleButton({
id:"aaaa",
label:"aaaa",
icon:{
"16":"./aaaa-16.png",
"32":"./aaaa-32.png",
"64":"./aaaa-64.png",
},
onChange:changed,
badge:"0",
badgeColor:"#A9A9A9",
});
function changed(state) {
if (button.badge == "1") {
// code
button.badge = "0";
} else {
button.badge = "1";
}
if (state.checked) {
// code
button.badgeColor = "#20B2AA";
}
else {
button.badgeColor = "#A9A9A9";
}
}

Resources