狛ログ

2019年2月22日金曜日

StackViewの優先度 Content Hugging PriorityとContent Compression Resistance Priority

2月 22, 2019

こんにちは、オフィス狛 モバイル開発担当 Aika-yuy です。
今回の投稿は、 StackViewにつけるAutoLayoutの優先度について書いてみたいと思います。


サイズが曖昧だと怒られる

動的なサイズ変更や、デバイスサイズにも柔軟に対応してくれるStackViewですが、サイズが曖昧だと怒られることがあります。固定サイズを指定するという方法もありますが、iPad対応で文字のサイズを動的に変更したい場合は、あらかじめ優先度を設定しましょう。

まず、StackViewに2つのLabelを配置し、StackViewのサイド20、Y軸の中心になるように制約をつけます。











そうすると、こんなエラーが出ます。







『どっちをLabelサイズを優先すればいいかわからないよーー 』ということです。
こまちゃんのLabelの優先度を低くするようにアドバイスしてくれています。

優先度を指定する

優先度を変更するときはAutoLayoutで設定できます。
優先度は、高くしたい方のpriorityを高くするのではなく、低くしたい方のpriorityを低くするようです。

いろんな解釈の仕方があると思いますが、私が覚えやすい方法使い分けをで書いてみました。

・Content Hugging Priority・・・・・・・・・・  子Viewが親Viewより小さい時 

・Content Compression Resistance Priority・・・  子Viewが親Viewより大きい時 


子Viewが小さい時

2つのLabelどちらともの文章が短い時、どちらのサイズを大きくしてViewいっぱいに表示するかを指定しなければいけません。
その場合は、優先順位が低い方の(固有のサイズを保持しなくてもいい方、大きくなってもいい方)
Content Hugging Priorityを1小さく設定します。

下の画像では、nameは動的にサイズが変更する予定がないので元々のサイズを維持。こまちゃん部分は動的にサイズ変更したいというような場合です。
こまちゃんのLabelのContent Hugging Priorityを下げることにより、長くなってしまった場合省略表示され、nameのラベルを親Viewから押し出すこともなくなります。











子Viewのどちらかが大きい時

2つのLabel合わせたサイズが親Viewよりも大きくなってしまう場合、どちらを優先して表示するか指定します。
省略して表示してもいい方の優先度を1小さくします。










子Viewどちらとも親Viewより大きい時(どちらかを優先すると、どちらかが消えてしまう場合)

この場合は、設定次第ですがどちらも表示したい場合は親のStackViewの比率から固定サイズで指定した方がいいかもしれません。

AutoLayoutマスターになるべく、少しづつブログも更新していけるよう頑張ります。



2019年1月31日木曜日

gitマージツールにWinmergeを使う方法。

1月 31, 2019
オフィス狛 技術部のHammarです。

開発するとき、特に複数人で開発をされる場合は、gitを使われている方が多いと思います。その際に皆さんはマージツールやdiffツールに何を使っていますでしょうか?
私は開発はwindowsを使うことがほとんどなので、WinMergeというマージツールをよく使っています。なので、これをgitのマージツールとして設定していますが、今回はこのgitのマージツールの設定方法について書いてみたいと思います。

ちなみに自分のgitクライアントツールですが、自分はGitExtensionsを使っています。GitExtensionsはデフォルトでkdiffというマージツールが使えるようになっていますが、個人的には使いづらくて、マージツールの設定方法を変えられてうれしかった記憶があります。

設定はプロジェクトにある「.git」というディレクトリ内の「config」ファイルに下記の記述をそのまま追加して保存するだけでOKです。※.gitは隠しファイルとなっています

■config

[diff]
    tool = WinMerge
[difftool "WinMerge"]
    path = C:/Program Files/WinMerge/WinMergeU.exe
    cmd = \"C:/Program Files/WinMerge/WinMergeU.exe\" -f \"*.*\" -e -u -r \"$LOCAL\" \"$REMOTE\"
[merge]
    tool = WinMerge
[mergetool "WinMerge"]
    path = C:/Program Files/WinMerge/WinMergeU.exe
    cmd = \"C:/Program Files/WinMerge/WinMergeU.exe\" \"$MERGED\"

ひとまず上記で最低限の設定となりますが、この記述はいろいろとカスタマイズ可能なので、自分用に見やすくしたり、3画面構成にしたりとかcmdの記述部分でいろいろとオプション設定も可能なので、もっと細かく設定してとことん使いやすくするのもありだと思います。

もちろんクライアントツールで設定を変えることも可能ですが、ツール毎に設定画面も違いますし、この方法はconfigファイルに記述するだけなのでGitExtensions以外のツールでも同じやり方で使えるようになります。

とりあえず細かな設定は置いといてサクッとgitでWinMergeを使いたいときにはぜひご活用ください。


Angular Templateでリストのフィルタを行う。

1月 31, 2019

オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。

今年最初のブログ、何とか1月中に出来ました。(ギリギリ)
今年最初はAngularネタで行きたいと思います。

AngularのTemplate(html)でリストのデータを表示する際、
<ng-container *ngFor="let item of testList>
  <div class="test_class">{{ item.name }}</div>
</ng-container>
こんな感じの書き方をすると思います。
この時、「リスト内のデータをフィルタしてから表示したいんだよなー」と思う時がありませんか?
ありません?ないかな・・・・まあ、私はあります。

というわけで、こんな時は「Pipe」を使います。
以前、『AngularのPipeを使ってhtml(View)の表示を共通化する。』をブログに書きましたが、あの「Pipe」です。

では、いつものようにAngular CLIで作成します。コマンドは、
ng g pipe [path/name]
です。今回は、「list-filter」という名前で作ります。
$ng g pipe shared/list-filter
CREATE src/app/shared/list-filter.pipe.spec.ts (204 bytes)
CREATE src/app/shared/list-filter.pipe.ts (209 bytes)
UPDATE src/app/shared/shared.module.ts (711 bytes)

すると、以下のようなファイルが生成されます。
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'listFilter'
})
export class ListFilterPipe implements PipeTransform {
  transform(value: string, args?: any): any {
    return null;
  }
}

これを以下のように変更します。
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'filter'
})
export class ListFilterPipe implements PipeTransform {
  transform(items: any[], col: string, value: string): any {
    if (col.length === 0 || value.length === 0) {
      return items;
    }
    return items.filter(x => x[col] === value);
  }
}
Pipeの名前は分かりやすく「filter」にしています。

そして、transformの引数を以下のように変更しました。
第1引数:items: any[] → 対象のリスト
第2引数:col: string → フィルタを行う対象の項目名
第3引数:value: string → フィルタする値

実際は色々作り込むのですが、とりあえず、今回は、
フィルタする項目・値が無かったら、リストをそのまま返すようにして、
if (col.length === 0 || value.length === 0) {
  return items;
}
最後にリストのフィルタした結果を返しています。
return items.filter(x => x[col] === value);
では、実際にこのPipeを使ってみましょう。

リストの中身はこんな感じだとします。(分かりやすくする為に、json形式で書いています。)
{"hoge_list":[
  {"id":"1","name":"テスト1"},
  {"id":"2","name":"テスト2"},
  {"id":"3","name":"テスト3"},
  {"id":"4","name":"テスト4"},
  {"id":"7","name":"テスト7"}
]}

実際にTemplateで使用する場合は以下のように記載します。
<ng-container *ngFor="let item of testList | filter:'id':'7">
  <div class="test_class">{{ item.name }}</div>
</ng-container>
@Pipe の name をvertical lineでリストの後ろにつけて、Pipeの後ろは、「:(colon)」を繋げてPipeへの引数を記載します。
つまり、「testList | filter:'id':'7'」の部分で、リスト内の「id」という項目が「7」になっている値を絞り込んでいます。

出力結果は、
 テスト7
となります。

如何でしょうか。Pipe、色々な使い方がありますね。

それでは、良いAngularライフを!


2018年12月27日木曜日

Angular5から7へのバージョンアップ・後編

12月 27, 2018

オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。

前回(Angular5から7へのバージョンアップ・前編)の続きとなります。 前回は、本家の手順通りにポチポチとコマンドを実行していきましたが、実際にどんな風に変更されて、どんな部分はやっぱりエラーになってしまうのか、確認していきます。

自動修正された部分の確認

まずは、前回の「更新後作業(After the Update)」で行ったマイグレーション、
rxjs-5-to-6-migrate -p src/tsconfig.app.json
について、自動で修正された部分が気になったので、どう修正されたのか確認しました。
例えば、
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/retry';
上記のような記載が、

import { Observable ,  of } from 'rxjs';
に変わっていました。

後は、
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/add/operator/skip';
上記のような記載が、

import { Subscription } from 'rxjs';
に変わっていました。

自動修正されたのは、全てimportの部分でした。
今後は、基本的に「rxjs」を参照すれば、問題なさそうですね。

ビルドエラーになった部分の確認

続いて、前回バージョンアップ作業後に、ビルドエラーになっている部分を確認していきます。
エラーとなった部分の修正方法については、RxJS本家サイトの「RxJS v5.x to v6 Update Guide」を参考にしていきたいと思います。

まずは、下記のエラーメッセージ。
[ts] プロパティ 'skip' は型 'Observable<number>' に存在しません。

該当箇所は、
import { skip } from 'rxjs/operators';
// ・・・(中略)
error$ = this.store.select(fromAuth.getError).skip(1);
です。
本家サイトによると、処理をチェーンさせて行く時は、「pipe」syntaxを使用する、とあります。
よって、
error$ = this.store.select(fromAuth.getError).pipe(skip(1));
のように修正します。

続いてのエラーメッセージ、
[ts] プロパティ 'map' は型 'Observable<HttpResponse<Object>>' に存在しません。

該当箇所は、
  checkEmail(email: string): Observable<boolean> {
    return this.http
      .post('user-test/check-email', { email }, { observe: 'response' })
      .map(data => true);
  }
こちらも先程と同じ感じで修正します。
  checkEmail(email: string): Observable<boolean> {
    return this.http
      .post('user-test/check-email', { email }, { observe: 'response' })
      .pipe(map(data => true));
  }

続いてのエラーメッセージ、
[ts] プロパティ 'do' は型 'Observable<HttpResponse<{ user: HogeModel; }>>' に存在しません。
該当箇所は、
    return this.http
      .post<{ user: HogeModel }>('user-test/authenticate', body, {
        observe: 'response'
      })
      .do(console.log)
      .map(data => {
        const user = data.body.user;
        this.userId = user.id;
        return user;
      });
  }
です。
本家サイトによると、「do、catch、switchは、JavaScriptの予約語になっているので、変更しました」とあります。
よって、
    return this.http
      .post<{ user: HogeModel }>('user-test/authenticate', body, {
        observe: 'response'
      })
      .pipe(
        tap(console.log),
        map(data => {
          const user = data.body.user;
          this.userId = user.id;
          return user;
        })
      );
  }
のように修正します。それぞれの対応は以下の通りです。
  • do -> tap
  • catch -> catchError
  • switch -> switchAll
  • finally -> finalize

上記までがRxJS絡みのエラーでしたが、
他にも、下記のようなエラーが出ました。
ERROR in The Angular Compiler requires TypeScript >=3.1.1 and <3.2.0 but 3.2.1 was found instead.
これは単純にTypeScriptのバージョンを「3.1.6」にする事で解決しました。

以上で、Angular5から7へのバージョンアップが完了しました。
思ったより、やる事が少なくて意外でしたが、気軽にアップデート出来るのは最高ですね。


それでは、良いAngularライフを!


2018年12月11日火曜日

Angular5から7へのバージョンアップ・前編

12月 11, 2018

オフィス狛 技術部のKoma(Twitterアカウントの中の人&CEO)です。

少しAngularから離れていたのですが、過去のプロジェクトをアップデートする事になったので、備忘録で作業内容を残しておこうと思います。(他にもいくつかのプロジェクトでも同じ事する必要があるので・・・・)

基本的な作業としては、こちらの手順通りになります。 バージョンは(Angular Version)は、5.2から7.0でいきます。
アプリの複雑度(App Complexity)は・・・・Mediumぐらいですかね、とりあえず。
その他の依存関係(Other Dependencies)ですが、今回のプロジェクトは、AngularJSも混在していませんし、 Angular Materialも使用していないので、チェックは付けません。
パッケージ管理(Package Manager)はnpmを使っているので、npmを選択します。

・・・・・ん???
Warning: We do not recommend moving across multiple major versions.
複数のメジャーバージョンを跨いだアップデートはオススメしない??
・・・・まあ、モノは試し、オススメしない方法でとりあえずやってみよう。

「Show me how to update!」を押すと、下記のように手順が表示されるので、順番にやっていこうと思います。

更新する前に(Before Updating)

・・・これ、もしかして、アプリの複雑度(App Complexity)によって出てくる内容違うのでは?と思ったら、案の定違うので、念の為「Advanced」の方の記載も見ておいた方が良いかもしれません。
今回のプロジェクトはこのまま「Medium」で行きます。

まずは、
If you import any animations services or tools from @angular/core, you should import them from @angular/animations
です。
以前(Angular2ぐらい?)のバージョンでは、animations系の処理はcoreに含まれてたので、それは使えないよ、と言う事ですね。
まあ、今回は元のバージョンがAngular5なので、気にする必要はなさそうです。

続いての
Switch from HttpModule and the Http service to HttpClientModule and the HttpClient service. HttpClient simplifies the default ergonomics (You don't need to map to json anymore) and now supports typed return values and interceptors. Read more on angular.io
です。
これも似たような事ですが、HttpModuleは使えないからHttpClientModuleに変更してね、と言う事ですね。
HttpClientModuleは、確かAngular 4.3 ぐらいから使えるようになった機能です。

最後の
Choose a value of off for preserveWhitespaces in your tsconfig.json to gain the benefits of this setting, which was set to off by default in v6.
です。
Angular5から追加になったpreserveWhitespacesは、tsconfig.jsonに記載するのですが、これをoff(false)にする事で、コンパイル時に余計な空白を削除することができます。 Angular6から、これはデフォルトoff(false)だよ、と注意を促してくれています。

今回は全て問題ないので、そのまま次の手順に進んで行きます。

更新作業(During the Update)

Make sure you are using Node 8 or later

Node のバージョンは8以降である必要があります。
コマンドラインやターミナル等で「node -v」を実行して確認しましょう。

Update your Angular CLI globally and locally, and migrate the configuration to the new angular.json format by running the following:

続いて、Angular CLIの最新をインストールします。
以降は、バージョンアップを行いたいプロジェクトディレクトリで実行して下さい。
npm install -g @angular/cli
npm install @angular/cli
ng update @angular/cli
グローバルのAngular CLIを更新した後、ローカルの更新を行い、最後にローカルのパッケージの更新を行なっています。
ただ、上記「npm install @angular/cli」だとローカル側のAngular CLIのバージョンが変わらなかったので、「npm install @angular/cli@latest」として最新をインストールしました。

正しく動いていると、
DELETE .angular-cli.json
CREATE angular.json (4321 bytes)
こんな感じで、旧バージョン形式である「angular-cli.json」が削除され、新バージョンの「angular.json」が作成されているはずです。

その後、もう一度「ng update @angular/cli」を行なっています。

ちなみに、プロジェクトでngrxを使っていると、モジュールに対してのワーニングが出ます。
ここでは無視して構いません。

Update any scripts you may have in your package.json to use the latest Angular CLI commands. All CLI commands now use two dashes for flags (eg ng build --prod --source-map) to be POSIX compliant.

package.jsonに記載されているscriptsは、最新のコマンドで更新してね、と言っていますね。
よくあるのが、「"build": "ng build --prod",」とかで、コマンドを簡略化するパターンですが、
最終的なコマンドはAngular CLIなので、そこは最新のコマンドに書き換えする必要があります。

Update all of your Angular framework packages to v6, and the correct version of RxJS and TypeScript.

続いて、Angular自体のアップデートを行います。(下記のコマンドを実行)
ng update @angular/core
注意書きとして、
After the update, TypeScript and RxJS will more accurately flow types across your application, which may expose existing errors in your application's typings
とあります。
AngularをUpdatesする事で、型チェック等が厳密になる為、既存のプログラムでエラーが出る可能性があるよ、と言う事ですね。まあ、その辺は想定内というか、多分別のところでエラーになるでしょう・・・。

ngModelChange is now emitted after the value/validity is updated on its control instead of before to better match expectations. If you rely on the order of these events, you will need to begin tracking the old value in your component.

Angular6以前でngModelChangeを使用している場合、実装方法によっては、変更前の値が取れてしまう問題がありましたが、
それがAngular6で修正されているので、ngModelChangeを使用しているところは注意してね、という事です。

Use ng update or your normal package manager tools to identify and update other dependencies.

先程は、Angular自体のアップデートを行ったので、個々のパッケージの依存関係は「ng update」などで更新してね、という事です。
という訳で、
ng update --all
をやっておきましょう。

ただ、ngrxを使っている場合はエラーでupdate出来ないと思います。
Package "@ngrx/store" has an incompatible peer dependency to "@angular/core" (requires "^6.0.0", would install "7.1.1")

ここは一旦強制的にupdateしてしまいます。
ng update --all --force
ng updateの度に、
@ngrx/store@6.1.2 requires a peer of @angular/core@^6.0.0 but none is installed. You must install peer dependencies yourself.
のようなワーニングは出てしまうのですが、
6.1.2はangular7に対応はしているので、一旦このままで行きます。
関連: https://github.com/ngrx/platform/issues/1397

If you have TypeScript configured to be strict (if you have set strict to true in your tsconfig.json file), update your tsconfig.json to disable strictPropertyInitialization or move property initialization from ngOnInit to your constructor. You can learn more about this flag on the TypeScript 2.7 release notes.

TypeScript 2.7から追加されたstrictPropertyInitializationについての記載です。より厳密な初期化チェックを行うかどうかのフラウですが、元々から存在しているstrictが有効になっている場合、サブセットであるstrictPropertyInitializationも有効になってしまうので、tsconfig.jsonで無効にするか、厳密なチェックに対応するようプログラムを変更してね、という事です。
(プロパティの初期化は、ngOnInitではなく、コンストラクタでやらないといけない)

更新後作業(After the Update)

Remove deprecated RxJS 6 features using rxjs-tslint auto update rules For most applications this will mean running the following two commands:

deprecatedされた機能を削除する、という事で、
これは、指示通り、以下のコマンドを実行します。
npm install -g rxjs-tslint
rxjs-5-to-6-migrate -p src/tsconfig.app.json
そうすると、変更されたファイルが表示されます。
Fixed 5 error(s) in /Users/officekoma/hoge-web-prj/src/app/auth/services/hoge1.service.ts
Fixed 1 error(s) in /Users/officekoma/hoge-web-prj/src/app/auth/services/hoge2.service.ts
// (中略)
Fixed 5 error(s) in /Users/officekoma/hoge-web-prj/src/app/home/store/effects/my-page.effect.ts
Fixed 5 error(s) in /Users/officekoma/hoge-web-prj/src/app/home/store/effects/my-page-edit.effect.ts

WARNING: /Users/officekoma/hoge-web-prj/src/app/auth/services/hoge1.service.ts[3, 1]: duplicate RxJS import

Once you and all of your dependencies have updated to RxJS 6, remove rxjs-compat.

続いて、rxjs-compatを削除します。
npm uninstall rxjs-compat

If you use the Angular Service worker, migrate any versionedFiles to the files array. The behavior is the same.

Angular Service workerを使っている場合もマイグレーションを行う必要があるようですが、
今回のプロジェクトでは使用していないので、特に何もしません。

という事で、5.2から7.0へのアップデートを行いました。
後は、個々のプログラムの中を見て、エラーとなっている部分を修正していく事になります。
長くなったので、その辺は次回ブログに記載しようと思います。

それでは、良いAngularライフを!

2018年12月7日金曜日

Photoshopでスライスツールを使わずにパーツを一気に書き出す方法。

12月 07, 2018
こんにちは、オフィス狛 デザイン部のSatoです。

今回はPhotoshopで作ったモック画像からスライスツールを使わないで細かい画像を書き出す方法をご紹介しようと思います。

モック画像からスライスを設定して書き出しするのは地味に大変な作業だと自分は思っているので、できるだけ楽に作業したいと思っていましたが実はスライスツールを使うよりも、もっと楽に細かいウェブデザインのパーツを書き出す方法があったのでご紹介させていただきます。

方法1 画像アセット機能で書き出す。


1.書き出したいレイヤーのレイヤー名またはグループのグループ名を「(書き出したい画像のタイトル).(書き出したい画像拡張子)」にします。

2.Photoshopのメニューバーの「ファイル」の「生成」にマウスカーソルを合わせると「画像アセット」という項目が出てくるのでクリックします。

psdファイルを保存している場所に「(書き出したpsdファイル名)-assets」というフォルダーができているので、その中に書き出されています。


書き出せる拡張子はJPEGや透過PNG透過GIFはもちろんの事、SVGも書き出せるそうです。
個人的にはSVGが書き出せるのがうれしいですね。

詳しい設定などはAdobeのブログで解説されていますので是非参考にしてみてください。


方法2 選択したレイヤーを「書き出し形式」で書き出す。


1.書き出したいアイコンなどのパーツを移動ツール+shiftキーで全て選択します。

2.レイヤーパネルの上で右クリックして出てきたメニューから「書き出し形式」を選択します。

3.画像のサイズや拡張子を選択する画面になるのでお好みで設定を変えてください。

書き出し設定を全て終えたら、右下の全て書き出しボタンを押します。
保存するフォルダーを選択して開くボタンを押すと指定したフォルダー中に書き出されています。

この方法なら色々なサイズのアイコンを一気に書き出せるのでとても便利です。

ただ、この方法で自分が少し躓いたところが一つあります。
画像のサイズや拡張子を選択する画面で画像の解像度を変えたはずなのに、1ファイルのみその拡張子で書き出され、他の画像はもともとの拡張子のままで書き出されていました。
実は左の書き出す画像の一覧の部分で、選択した画像しか設定が反映されないようです。
書き出す画像全ての拡張子を変えたい場合はCtrlキーを押しながら全てクリックして選択してから拡張子を変えてから書き出すと全部同じ拡張子になってくれます。


どちらの機能も背景を消してスライスツールでスライスを引いて……という作業を省略できる素晴らしい機能なのですごい時短になります!
みなさんもぜひ使ってみてください。

2018年12月5日水曜日

C# foreachでListに要素を追加する方法。

12月 05, 2018

オフィス狛 技術部のHammarです。

最近業務でC#を使うことが多いのですが、個人的にはまだまだやり始めでわからないことが多く、ちょっとしたことで詰まる今日この頃なんですが、基本的なところで「こういう書き方もあるんだなー」とわかったことを今回は書きたいと思います。

■foreachでListに要素追加方法

C#のCollectionであるListクラスは配列っぽいイメージですが、動的に長さ(サイズ)を自由に変えられることと、データを途中に挿入できる点が配列との大きな違いとなっています。

で、例えばListや配列を回しながら、ある条件のとき等にListに要素を追加したいとき等がよくあると思いますが、最初自分は下記のように記述していました。
ちなみに下記は配列を回しながら、indexの値と配列の中身を新たなListに作っているだけの記述です。

public class animalList
{
    public int Seq { get; set; }
    public string AnimalName { get; set; }
}

public void TestMethod()
{
    string[] animals = new string[] { "lion", "zebra", "elephant" };
    var index = 0;

    List<animalList> result = new List<animalList>();
    animalList animalListdata = null;

    foreach (var name in animals)
    {
        animalListdata = new animalList
        {
            Seq = index,
            AnimalName = name
        };
        index++;
        result.Add(animalListdata);
    }
}

上記でも作りたい形になるのですが、ほんの少しだけ簡単に記述できる方法がありました。
※animalListクラスは同じものを使います。

public void TestMethod()
{
    string[] animals = new string[] { "lion", "zebra", "elephant" };
    var index = 0;

    var result = new List<animalList>();
    foreach (var name in animals)
    {
        result.Add(new animalList
        {
            Seq = index,
            AnimalName = name
        });
        index++;
    }
}

たった2~3行だけですが、回すと同時にListにAddしているので個人的にはこっちのほうがスッキリしているように見えます。

というわけで、C#をよくご存知の方にはそんなことは基本中の基本と怒られそうですが、まだ触れてから日の浅い自分にとってはちょっとしたことですが勉強になりました。