title: Changing list element font size
body: <p>I'm building a NativeScript application that displays rows of text in a <code>ListView</code>. Each row is (for now) a couple of <code>Label</code> fields, one to display an optional row label and one to display text. I want the font size to scale automatically with the actual width of the row. I'm having a hard time getting it to work correctly. I have three major problems, described below. Here's what I have so far:</p>
  
  <p>The row layout:</p>
  
  <pre><code>&lt;GridLayout columns="*,25"&gt;
      &lt;Label #text [text]="row.full.join(' ')" col="0" class="pointed tikkuntext"&gt;&lt;/Label&gt;
      &lt;Label #label col="1" class="tikkunlabel" *ngIf="row.label"&gt;
          &lt;FormattedString&gt;
              &lt;Span #chapter *ngIf="row.label.chapter" [text]="row.label.chapter + ' '" fontSize="11" fontAtttributes="Bold"&gt;&lt;/Span&gt;
              &lt;Span #verse [text]="row.label.verse.join(',')"&gt;&lt;/Span&gt;
          &lt;/FormattedString&gt;
      &lt;/Label&gt;
  &lt;/GridLayout&gt;
  </code></pre>
  
  <p>This is encapsulated in a custom component for each row:</p>
  
  <pre><code>import { Component, ElementRef, Input, AfterViewInit, ViewChild } from '@angular/core';
  import { Page } from "ui/page";
  import { Label } from "ui/label";
  import { Span } from "text/span";
  
  @Component({
      moduleId: module.id,
      selector: "tikkun-row",
      styleUrls: ["tikkun-row.css"],
      templateUrl: "tikkun-row.component.html"
  })
  export class TikkunRow implements AfterViewInit {
      @Input() row: any;
      @ViewChild("text") textField: ElementRef;
      @ViewChild("label") labelField: ElementRef;
      @ViewChild("chapter") chapterSpan: ElementRef;
  
      constructor(private page: Page) {}
  
      ngAfterViewInit() {
          let width = this.page.frame.getActualSize().width;
          width /= 375; // magic number!
          if (width &gt; 0) {
              let textSize = 14 * width; // more magic numbers!
              let verseSize = 9 * width;
              let chapterSize = 11 * width;
  
              let textField = &lt;Label&gt;this.textField.nativeElement;
              textField.style.fontSize = textSize;
              if (this.labelField) {
                  let labelField = &lt;Label&gt;this.labelField.nativeElement;
                  labelField.style.fontSize = verseSize;
                  if (this.chapterSpan) {
                      let span = &lt;Span&gt;this.chapterSpan.nativeElement
                      span.fontSize = chapterSize;
                  }
              }
          }
      }
  }
  </code></pre>
  
  <p>It's the <code>ngAfterViewInit()</code> method where I need some help.</p>
  
  <p><strong>Problem 1</strong></p>
  
  <p>My first problem is that the text scaling doesn't always work for the first line of the list in Android:</p>
  
  <p><a href="https://i.stack.imgur.com/5DIqF.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/5DIqF.png" alt="image from Android emulator"></a></p>
  
  <p>This seems to be intermittent; every once in a while the first line displays correctly, but most of the time it's like shown above. I can't figure out a pattern to it. Oddly, if I log the width each time <code>ngAfterViewInit()</code> is called, I get a single console output line in iOS and eight output lines in Android (of which the first is 0 and the others are the same positive value). This despite the list having seven rows!</p>
  
  <p><strong>EDIT</strong> I've managed to almost (but not quite) eliminate this problem by modifying how I probe for the width. Instead of a one-shot call to <code>getActualSize()</code>, I do it in a timed loop. Here's the relevant code change:</p>
  
  <pre><code>ngAfterViewInit() {
      let width = this.page.frame.getActualSize().width;
      let wait = () =&gt; {
          if (width &gt; 0) {
              // the same stuff as before
          } else {
              width = this.page.frame.getActualSize().width;
              setTimeout(wait, 10);
          }
      };
      wait();
  }
  </code></pre>
  
  <p>With this change, the intermittent lack of font scaling has almost entirely disappeared. However, once in a rare while it still shows up. Perhaps significantly, when it does show up it's not always on the first row of the list. And it's not always the entire row; sometimes it's just the right-side label. Perhaps it's more of a rendering issue?</p>
  
  <p><strong>Problem 2</strong></p>
  
  <p>On iOS, the two <code>&lt;Label&gt;</code> elements of each row are nicely aligned vertically on their text baselines, like this:</p>
  
  <p><a href="https://i.stack.imgur.com/NKkas.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/NKkas.png" alt="image from iOS emulator"></a></p>
  
  <p>On Android, I can't get that to happen. What's in the first picture above is the effects of using <code>vertical-align: center</code>, which is the closest I can get. Unfortunately, NativeScript only supports <code>top</code>, <code>center</code>, <code>bottom</code>, and <code>stretch</code>, none of which look right. Is there a way (perhaps by dropping down into native) to force baseline alignment across a <code>GridLayout</code> row in Android?</p>
  
  <p><strong>Problem 3</strong></p>
  
  <p>It seems that <code>ngAfterViewInit()</code> isn't the right lifecycle method to override. It gets called at the beginning (once for iOS, once for each row [+1 extra!] for Android), but doesn't get called if I change the device orientation between landscape and portrait, in either iOS or Android. I've tried all the <a href="https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#hooks-purpose-timing" rel="nofollow noreferrer">Angular lifecycle hooks</a> and nothing seems to work right. Is there a way to detect when the row's actual width changes so I can update the text (and label) size? Is there some useful event somewhere that I can subscribe to, to do this?</p>