SwiftUIの文法 その1 View
SwiftUIでは簡潔なコードでユーザーインターフェースを記述できます。
従来のコードよりも大幅に少ないコードで構築できるヒミツがあるはずです。
この記事ではまず、最もシンプルなビュー配置コードを調べ、SwiftUIが従来のSwift文法で書かれていることを確認します。
【2020年12月29日に内容やリンクなどを確認し一部追記しました。
(最初の公開2019年11月)】
毎月札幌でiOSアプリ作りをアシストするセミナーをやっています。1時間にわたるセミナーの全内容を、物理的に参加できない方のためにnote上で公開します。
お知らせ
電子書籍『Swift5初級ガイド』をAppleのブックストアから出しています。
文字サイズを好みに変更でき、本文を検索可能な電子書籍です。
無料サンプルでご確認ください。
MacでもiPadでもiPhoneでも読めます。
Xcode 12に対応した第7版がダウンロード可能です。(ご購入済みの場合は無料アップデートです)
(2020年10月12日に第7版にアップデートしました)
Swift 5.3は第6版で対応済みです。
ブックストアから一度購入すると今後のアップデートは無料で読めます。
iOSアプリ作りをアシストするセミナーは今後も月一回のペースで続ける予定です。(2020年3月以降COVID-19感染拡大防止のため休止しています)
詳細は connpass.com の 札幌Swiftでご確認ください。そして機会があればぜひ参加してください。
アプリ作りやプログラミング教育に関連する話題は 札幌Swift のfacebookページで発信しています。
・・・
・画像クリックで拡大表示できます
・画像を拡大表示中は画像の左右をクリックで画像だけを順に表示できます
・ソースコード部分は左右にスクロールできます
画像はXcode 11.2.1です。
【2020年12月29日追記:Xcode 12.3との違いがあれば追記しました。】
有料記事です。購入でサンプルコードもダウンロードできます。
1 playgroundで確認できる
コードの確認は Xcode 11.2.1(11.2) のplayground で可能です。
playground を使った SwiftUIの実行は macOS 10.14.x でも macOS 10.15.x でも可能です。
playground でライブビューに表示するにはPlaygroundPage.current.liveView にViewControllerインスタンスを渡します。
【2020年12月29日追記:Xcode 12.3 でも実行できますがライブビューの表示結果が異なります。Xcode 12ではデバイスの画面に相当する部分がなくビューを小さく表示します。Swift PlaygroundsアプリはXcode11と同じ表示です。】
SwiftUIのViewの場合は UIHostingController クラスのイニシャライザ UIHostingController(rootView:) を使い playground で表示できます。(iPad の Swift Playgrounds 3.1アプリでも実行できます)
1-1 SwiftUIと従来のコード例
SwiftUIのシンプルなコードを例に、従来のコードと比べましょう。
// 1行の文字列 フォントと色の指定のみ
import SwiftUI
import PlaygroundSupport
struct SampleView: View {
var body: some View {
Text("SwiftUIもSwiftだ!")
.font(.title)
.foregroundColor(.green)
}
}
PlaygroundPage.current.liveView = UIHostingController(rootView: SampleView())
このSwiftUIコードは、ダイナミックタイプに対応したテキストスタイルと文字色を指定した1行の文字列を表示する playground 用のコードです。
(そのままiPadのSwift Playgrounds 3.1アプリでも実行できます)
Xcode 11.2.1 のplaygroundで実行した結果はこのようになります。
【2020年12月29日追記:Xcode 12.3 では表示結果が異なります。最初の表示まで数分かかる場合がありました。(Xcode 12.3の画面はダークモードで表示します)】
次はこれまでのコード、UIKitを使ったコード例を次に示します。
// UIKitの例
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
let margin = 50
let width1 = 300
let height1 = 60
override func viewDidLoad() {
let box = CGRect(x: margin, y: margin, width: width1, height: height1)
let topLabel = UILabel(frame:box)
topLabel.text = "UIKitのラベル"
topLabel.font = UIFont.preferredFont(forTextStyle:.title1)
topLabel.textColor = .systemGreen
view.addSubview(topLabel)
view.backgroundColor = .white
}
}
PlaygroundPage.current.liveView = ViewController()
(そのままplaygroundで使えるコードなので冗長な部分があります)
SwiftUIはコードの見た目がかなり違います。
冗長であっても指定が必要であることそのものが、SwiftUI開発のきっかけの一つであることを示していますね。
実行結果です。
表示位置が違いますが、座標の与え方で同じにできます。
文字スタイル(フォント)と文字色を指定した文字列を表示するコードとしては同じです。
2 宣言型シンタックス
AppleはSwiftUIのコードを「宣言型シンタックス」(Declarative Syntax)と呼んでいます。
確かにSwiftUIのコードには代入文がありません。が表示結果はほぼ同じです。
● 宣言型シンタックスであるSwiftUIはコードの組み方が変わる
● そのために新しく作られたフレームワークを使う
● 命令型ではない このためaddSubviewなどは使わない
SwiftUIではデフォルトが注意深く設定されています。
最小限のコードでそれなりの表示になり、調整が必要な場合のカスタマイズもシンプルです。
SwiftUIではレイアウトの自動化が最初から考慮されていて、何も指定しない場合は画面中央に自動で表示します。
【2020年12月29日追記:Xcode 12.x のSwiftUIでは画面がありません、Viewだけを表示します。】
3 ビュー宣言のコード
さらにシンプルに6行のコードを細かく見ていきましょう。(文字色の指定をはずしました)(サンプル1)
// 1行の文字列 フォント指定のみ
struct SampleView: View {
var body: some View {
Text("SwiftUIもSwiftだ!")
.font(.title)
}
}
最初の struct SampleView: View { はごく普通の型の定義です。
struct なのでコロンの次は準拠するプロトコルです。
SwiftUIのビューはViewプロトコルを継承したstructです。
var body: some View はstructのプロパティ です。
{...} が続いているので計算型プロパティ です。
bodyはViewプロトコルでは必須のプロパティ です。
型指定 some View の some はSwift5.1で利用可能になった新しいキーワードです。
some の付く場合はOpaque Type(不透明型)と呼ばれます。
型に厳格なSwift言語で型を抽象化しています。
3-1 body プロパティ
計算型プロパティ body では実は return が省略されています。
計算型プロパティはゲッターとセッターを設定し利用します。
ゲッターの通常の書式は get { return 計算値 }
セッターを書かない場合は get キーワードも省略可能になります。
上記の body では get キーワードも省略されています。
Swift5.1から関数や計算型プロパティで、処理が1行の場合は return キーワードが省略可能になりました。(Implicit Return)
サンプル2のように return を書いても問題なく、表示は同じです。
// return を省略しないコード
struct SampleView: View {
var body: some View {
return Text("SwiftUIもSwiftだ!")
.font(.title)
}
}
return が省略できていたことから、次行の .font(.title) はもともと一つの行をあえて次の行に書いていたことがわかります。
3-2 Text("〜")はイニシャライザ
Text("SwiftUIもSwiftだ!") はText型のイニシャライザです。
init<S>(_ content: S) where S : StringProtocol
Text型は Equatableプロトコルと Viewプロトコルに準拠する struct です。
StringProtocol は Swift Standard Libraryのプロトコルです。
StringProtocol には String だけでなくSubstringも準拠しています。
【2020年12月29日追記:SwiftUIのドキュメントで各Viewのイニシャライザを確認するクセを付けるとSwiftUIが身近になってきます。】
そして .font(.title) はText型のインスタンスメソッドです。
サンプル3のように書き直してもコードの構造はまったく同じです。
// イニシャライザとメソッドを二つに分離
struct SampleView: View {
var body: some View {
let t0 = Text("SwiftUIもSwiftだ!")
let t1 = t0.font(.title)
return t1
}
}
イニシャライザで生成したインスタンスを t0 に代入し、t0 に対してフォント指定したインスタンスを t1 に代入します。
bodyのインスタンスとして t1 を返しても表示はまったく同じことを確認してください。
3-3 .font(.title) は Text型を返すメソッド
Text型のfont(_:)インスタンスメソッドは Text型インスタンスを返します。
// Text型 font インスタンスメソッドの宣言
func font(_ font: Font?) -> Text
font(_:) メソッドは指定したフォントで表示する新しい Text 型インスタンスを返します。
元のインスタンスには影響しません。
ViewプロトコルとText型はSwiftUIフレームワークに実装されたものですが、コードの書き方はSwift5.1の機能を使って省略されているだけで、通常のSwiftのコードそのままなのです。
引数に渡すFont型についての補足:
t0.font(.title) は t0.font(Font.title) のFontを省略した書き方です。
titleのほかにもタイププロパティを持っています。
引数の型が決まっているので「Font」を省略できます。
ただしドットは省略できません、「.title」と書かないとエラーになりmす。
ここで return t0 とすると、デフォルトのフォント(WWDCセッションでデフォルトのスタイルは.bodyと言っていました)で(小さな文字で)表示されます。
t0 は影響を受けていないことが確認できます。
今後も記事を増やすつもりです。 サポートしていただけると大変はげみになります。