足し算ゲームのiPhoneアプリを作ってみた話 2. SwiftUIで画面を作ってみたらすごく大変だった
従来型の画面デザイン開発とは異なり、SwiftUIでは宣言的っぽく画面デザインを定義していきます。JavaのLayoutManagerを覚えている方には、その時の記憶を思い出して頂きながら、SwiftUIで画面デザインをしていく私の七転八倒ぶりをご笑覧いただければと思います。
bodyは一つのオブジェクトしか格納できません
struct ContentView: View {
var body: some View {
get{
var てきすと = Text("Hello, world!")
var 返却するGUI部品 = てきすと.padding()
return 返却するGUI部品
}
}
}
前回は、SwiftUIのbodyの宣言的っぽい記述を、手続きの形に直して書き出してみました。そしてbodyは一つのView要素(画面上の部品)しか持つことができないので、複数の画面要素を設定するときには、HStackやVStackと言った、部品の入れ物を作ってあげる必要があります。
HStackは、ホリゾンタル(水平方向)のH、VStackはヴァーティカル(垂直方向)のVを接頭辞にもつ、画面GUI部品を乗せるためのお皿です。HStackにGUI部品を乗せると、それらは水平方向に配置され、VStackにGUI部品を乗せるとそれらは縦方向に配置されます。
GUI部品の上で ⌘ クリック
GUI部品をお皿(HStack, VStack, ZStack)に乗せるための簡単な方法は、GUI部品に対して⌘(コマンドキー)を押しながらクリックすることです。
これはソースコードエディタ上でも、プレビュー画面上でも、どちらでも有効です。
Appleの公式チュートリアルだと、ソースコードエディタ上での⌘クリックが紹介されていた記憶がありますが、プレビュー画面でも有効でした。(プレビュー画面の方は、SwiftUI Inspectorを開くときに使っていたような?)
ここで、Embed in HStack(HStack構造に埋め込む)などを選択することでソースコードの形が変わります。
HStackの中では、複数のGUI部品を並べることが可能で、書いたとおりの順番に並んでくれます。
画面に入り切らないくらいに並べてあげると、自動的に折り返してくれたりもします。
もちろん、xStack(お皿)は、入れ子にできます。(プログラミングでは、ネストできますと表現します)
お料理の乗った小鉢をお皿にのせて、さらに大きなお皿にのせて・・・というイメージでしょうか。これを繰り返して画面をデザインしていきます。
正直しんどい
ぶっちゃけ、しんどいです。たとえば上の例だと、英語で書いている行と、日本語で書いている行で、ズレができちゃってます。
わかりやすくするために、それぞれの背景色を変えてみました。modifier(あとで説明します)を使わずに、英語と日本語の位置をそろえたいと思ったら、むしろ、VStackを2つ作って、これをHStackで並べることになります。
モディファイアを使ってGUI部品の細部を制御
すでにここまでのコード例でも.padding()とか、.background()とか書いているものがmodifiersと言うものです。GUI部品の表示詳細を調整するものになります。SwiftUI以前は、そもそもGUI部品にたくさんのプロパティがあって、それらを変更することで画面の表示状態を設定することができたのですが、SwiftUIでは変更に必要な要素について、個別にモディファイアを設定するということになっています。そして、このモディファイアがわかりにくいというか、探しにくいというか、、、自分が行いたい画面調整をするのに、どんなモディファイアを使えばよいのかが分かってないのが今の私です。いろんなモディファイアを試しては、
ちがうー、これじゃなかったあぁぁぁ
と、悲嘆にくれながらのトライアンドエラーを重ねました。そんな中で私が筆算ゲームを作るのに使ったモディファイアたちが以下のものです。
・frame() でGUI部品のサイズを調整ができました
・foregroundColor()でテキストの色を指定できました
・font()でフォント指定ができました
・hidden()で、レイアウト上のスペースを取るが表示はしない、という状況が作れました
・action()で、ボタンがタップされたときのアクションを支持することができました
がっちがちにframeで絶対サイズ指定をしちゃってるので、Apple的にはNGなコードだよなぁと思います。ですが、、、週末の2日くらいの時間では、どうしても繰り上がりの、ちっちゃい1の位置指定を絶対サイズ指定せずに行うことができませんでした。
画面上段、解答欄の上にある、ちっちゃい1、です。たぶん格好良い解決の仕方があるのだと思うのですが、(Java/SwingのGridbagLayoutみたいなのがあるはず?!)見つけられませんでした・・・orz
SwiftUIはスクリーンリフレッシュ時の挙動をプログラムさせるものである
実際にプログラミングをしてみて、SwiftUIのプリンシプルの一つに触れた気がします。それは、SwiftUIでのプログラミングというのはスクリーンリフレッシュのコードを書かせるものであり、かつ、込み入ったイベント処理によるバグを避けるための「コーディング上の制限」を課しているものだ、という気付きでした。
GUIプログラミングでは、ウィンドウシステムからの指示に従って、画面描画のロジックを実装する箇所があります。最初期のJavaでは、画面の描画が必要になったとき、paint()というメソッドが呼び出されるという約束になっていました。画面描画が必要になったときというのは、たとえばアプリケーションが起動して初めてのウィンドウを表示するときや、他のアプリケーションの影に隠されていたウィンドウが前面に出てきたときなど、多岐に渡ります。この画面描画を担当する箇所というのは、思いもよらなかったようなバグが入り込みがちなのです。SwiftUIのやり方は、ある種オーバヘッドが多いようにも思えますが、これはこのような「思いもよらないようなバグ」を避けるために支払うべきコスト(具体的には処理が要求するCPU時間)だとAppleは判断しているのだと思います。
ですから、SwiftUIを使ったアプリ作成においては、これまで以上に注意深くMVCを意識したプログラミングが必要になるのだと思います。
というのも、ここで記述しているプログラムロジックは、画面描画のたびにメモリに展開され、処理を行い、その後廃棄されるからです。画面を形作るプログラムコードの中にデータを置いておいても、それは画面描画のたびにメモリにアロケートされ、しかる後に削除されてしまうのです。もちろん、これでは画面がデータにアクセスすることができなくなってしまいますから、そのための仕組みが用意されています。次回、その仕組みについて調べてみたことを書いていこうと思います。
以下、投げ銭です。本文はありません。
ここから先は
¥ 100
この記事が気に入ったらサポートをしてみませんか?