【お知らせ】
SwiftUIで作った macOS Todo アプリ
ToDone
を100ダウンロードまで無料にしました。マニュアルページは、ToDone サポートページ です。
【本文】
今までは、ビュー(View、UI部品)をレイアウトするのに、VStack や HStack
使ってきました。このように View をレイアウトするものをコンテナ(container)と言います。この章では Form(フォーム)いうコンテナの説明をします。なおコンテナも View の一つです。
このコーナーでは、任意のテキストエディタでコードを記述し、 ターミナルを使ってビルドする方法で作業を進めています。Xcode をお使いの場合は、Xcodeで作業する場合 をご一読ください。
なお、ターミナルを使う場合も、Swift コンパイラや SwiftUI フレームワークなどを Mac にインストールするために Xcode をインストールして、一度起動させなければなりません。インストールが終われば Xcode は終了しても大丈夫です。
また、作成したアプリケーションを App Storeに提出するためのファイルにするには、 Xcode を使わなければならなかったかもしれません。 どこかで Xcode を使わずに作る方法を見たような気もするのですが...
私の開発環境は次のとおりです。
import SwiftUI
struct ContentView: View {
@State var name = ""
@State var author = ""
@State var review = ""
var body: some View {
VStack {
Form {
Section(header:Text("Books").font(.headline)){
TextField("Title", text: $name)
TextField("Author", text: $author)
}
Section(header:Text("Review").font(.headline)) {
TextEditor(text: $review)
}
}
}
.padding(10)
.frame(minWidth: 300, maxWidth: .infinity, minHeight: 200, maxHeight: .infinity)
}
}
App.swift は、以前は Main.swift というファイル名でした。今後は App.swift というファイル名に統一したいと思います。今回は App.swift に変更はありませんが、一応掲載しておきます。
import SwiftUI
@main
struct FooApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
}
アプリケーションの名前を変えるために、Info.plist の CFBundleName を書き換えます。ここでは「Review」という名前にしました。 Xcode で作業している場合は、ここは無視してください。 機会があれば、 また説明します。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>foo</string>
<key>CFBundleIconFile</key>
<string>icon-macbook.icns</string>
<key>CFBundleName</key>
<string>Review</string>
</dict>
</plist>
swiftc App.swift View.swift -o foo
mv foo Foo.app/Contents/MacOS
import SwiftUI
struct ContentView: View {
@State var name = ""
@State var pass1 = ""
@State var pass2 = ""
@State var match = ""
var body: some View {
VStack {
Form {
Section(header:Text("Account").font(.headline)){
TextField("Name", text: $name)
SecureField("Password", text: $pass1)
}
Section(header:Text("Again").font(.headline)) {
SecureField("Password",text: $pass2, onCommit:{
if name != "" {
match = pass1 == pass2 ? "Matched!" : "Mismatched."
} else {
match = "Enter name."
}
})
}
}
Text(match)
.font(.largeTitle)
.fontWeight(.ultraLight)
.frame(maxWidth:.infinity, maxHeight: .infinity)
}
.padding(10)
.frame(minWidth: 300, maxWidth: .infinity, minHeight: 200, maxHeight: .infinity)
}
}
App.swift に変更はありません。
import SwiftUI
struct ContentView: View {
@State var name = ""
@State var pass = ""
@State var isShow = false
var body: some View {
VStack {
Form {
Section(header: Text("Account")
.font(.headline)) {
TextField("Name", text: $name)
if isShow {
TextField("Password", text:$pass)
} else {
SecureField("Password", text:$pass)
}
}
Section(header:Text("Show").font(.headline)){
Toggle(isOn:$isShow, label:{Text("Show password")})
}
}
Spacer()
}
.padding(10)
.frame(minWidth: 300, maxWidth: .infinity, minHeight: 200, maxHeight: .infinity)
}
}
App.swift に変更はありません。