[SwiftUI]メモアプリに新規メモ機能を追加する Part.2
■初めに
こんにちわ、中川(Twitter)です。
さて、メモアプリの新規メモ機能追加 Part.2です。
Part.1の記事はこちらから⬇︎
この記事はサンプルアプリのカスタマイズを目的としています。
元となっているアプリ実装の記事はこちらから⬇︎
今回の目標:
使用する主な機能:
新規View完成図:
View名は「NewMemoView」としました。
iPhone標準搭載のメモアプリを参考に、
見た目はできるだけシンプルなデザインとします。
完成後コード⬇︎
import SwiftUI
struct NewMemoView: View {
// フォーカスが当たるTextFieldを判断するためのenum
enum Field { case title }
@FocusState private var foucsedField: Field?
@State var newMemoTitle = ""
@State var newMemoText = ""
// Home画面に戻るためのプロパティ
@Environment(\.presentationMode) private var presentationMode
@EnvironmentObject var vm: MemoData
var body: some View {
VStack {
// メモのタイトルを記入するテキストボックス
TextField("タイトル", text: $newMemoTitle)
.padding(20)
.frame(width: 400, height: 50)
.font(.title)
.focused($foucsedField, equals: .title)
// メモ詳細を記入するテキストボックス
TextEditor(text: $newMemoText)
.padding(.horizontal)
} // VStack
.navigationBarTitle(Text(""),
displayMode: .inline)
.navigationBarItems(trailing: Button("完了") {
self.presentationMode.wrappedValue.dismiss()
}.padding())
// .onApperはView表示時に発生する処理のこと
// NewMemoViewへの遷移から、0.5秒ずらしてenumの値を更新し、キーボード表示の処理を行う
.onAppear() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
foucsedField = .title
}
}
} // body
} // View
[ 🍎 実装開始 🍎 ]
■新規メモ生成用のViewを作成
Part.2では、新規メモ作成用のViewを
新しく作っていきます。
新しいメモタイトルと内容テキストを書く場所ですね。
NavigationViewやNavigationLinkを用いた
画面遷移も入ってきます。
では、実装していきましょう。
よろしくお願いします✊
1.画面デザインの構築
新しくSwiftUIViewファイル「NewMemoView」を作成します。
まずは画面全体の構成を作っていきましょう。⬇︎
struct NewMemoView: View {
// フォーカスが当たるTextFieldを判断するためのenum
enum Field: Hashable { case title }
@State var newMemoTitle = "" // ①
@State var newMemoText = "" // ②
var body: some View {
VStack {
// メモのタイトルを記入するテキストボックス
TextField("タイトル", text: $newMemoTitle) // ✅ ①
.padding(20)
.frame(width: 400, height: 50)
.font(.title)
// メモ詳細を記入するテキストボックス
TextEditor(text: $newMemoText) // ✅ ②
.padding(.horizontal)
} // VStack
} // body
} // View
①TextField
新規メモのタイトルを記入するテキストフィールド。
引数に状態変数(newMemoTitle)を指定して
データバインディングをする必要があります。
②TextEditor
新規メモの本文を記入するテキストエディター。
引数に状態変数(newMemoText)を指定して
データバインディングをする必要があります。
2. View間の 画面遷移を実装
新規メモのViewを用意できましたね。次は、
メイン画面のMemoListViewからNewMemoViewへ
画面遷移ができるようにしていきます。
まず、MemoListViewに記述を足していきましょう。
struct MemoListView: View {
// MemoModelのデータを呼び出し
@EnvironmentObject var vm: MemoData
// 新規メモViewへの遷移フラグを管理
@State private var isActive = false // ✅
Bool型の状態変数を用意します。後の設定で
このプロパティを遷移のフラグとして使用します。
このプロパティがtrueに更新されることでNewMemoViewへと
遷移するという流れですね。
続いてNavigationLinkを設定していきます。
今回NavigationViewの画面遷移を
Buttonからプッシュ遷移で実装します。
NavigationViewの配下に記述していきましょう。
①NavigationLinkを設定
// 新規メモViewへの画面遷移に使用
// 下部のボタンとisActiveプロパティで紐づけている ✅ ①
NavigationLink(destination: NewMemoView(), isActive: $isActive) {
EmptyView()
}
引数destination:
遷移先のViewを指定します。NewMemoView()とします。
引数isActive:
遷移を知らせるフラグBinding<Bool>を指定します。
先ほど作ったBool型の状態変数isActiveとします。
EmptyView()とすることで、NavigationLinkにより
自動配置されるアイテムを隠すことができます。
次に、プッシュ遷移と紐づけるButtonを作成します。
②ボタンによるプッシュ遷移の実装
// 新規メモViewへの画面遷移に使用
// 下部のボタンとisActiveプロパティで紐づけている
NavigationLink(destination: NewMemoView(), isActive: $isActive) {
EmptyView()
}
// 新規メモ生成ボタン
Button(action: {
isActive.toggle() // ✅
}) {
Image(systemName: "square.and.pencil")
.resizable()
.scaledToFit()
.frame(width: 25, height: 25)
.padding(.top)
}
}
NavigationLinkで指定したisActiveを
ボタンアクションによって、.toggle()します。→trueに更新
これで、ボタンを押すことにより
NavigationLinkにフラッグが渡され、画面遷移します⬇︎
③遷移元に戻るためのコードを実装
struct NewMemoView: View {
@State var newMemoTitle = ""
@State var newMemoText = ""
// Home画面に戻るためのプロパティ
@Environment(\.presentationMode) private var presentationMode // ✅ ①
var body: some View {
VStack {
// メモのタイトルを記入するテキストボックス
TextField("タイトル", text: $newMemoTitle)
.padding(20)
.frame(width: 400, height: 50)
.font(.title)
// メモ詳細を記入するテキストボックス
TextEditor(text: $newMemoText)
.padding(.horizontal)
} // VStack
.navigationBarTitle(Text(""),displayMode: .inline)
.navigationBarItems(trailing: Button("完了") {
self.presentationMode.wrappedValue.dismiss() // ✅ ②
}.padding())
} // body
} // View
①
@Environmentを付与してpresentationModeを取得します。
下記の記事がより詳しく書かれているのでどうぞ⬇︎
// Home画面に戻るためのプロパティ
@Environment(\.presentationMode) private var presentationMode // ✅ ①
②
presentationModeにアクセスして、.dissmiss()を指定します。
これにより、現在のViewが閉じるという処理が実行されます。
完了ボタンを押すことで、遷移元のMemoListViewに戻るという流れです。
.navigationBarItems(trailing: Button("完了") {
self.presentationMode.wrappedValue.dismiss() // ✅ ②
}
3. 画面遷移と同時に入力キーボードを出す
iPhone標準搭載のメモアプリだと、
新規メモ画面へ遷移すると入力キーボードが
自動で出現するようになっています。
これもコードで実装されることで実現する挙動です。
今回試しに実装してみましょう✊
NewMemoViewに以下の4つのコードを記述していきます。
少し多いですがそこまで複雑ではないのでやってみましょう✊
struct NewMemoView: View {
// フォーカスのフラグに使用するenum
enum Field { case title } // ✅ ①
@FocusState private var foucsedField: Field? // ✅ ②
@State var newMemoTitle = ""
@State var newMemoText = ""
// Home画面に戻るためのプロパティ
@Environment(\.presentationMode) private var presentationMode
var body: some View {
VStack {
// メモのタイトルを記入するテキストボックス
TextField("タイトル", text: $newMemoTitle)
.padding(20)
.frame(width: 400, height: 50)
.font(.title)
.focused($foucsedField, equals: .title) // ✅ ③
// メモ詳細を記入するテキストボックス
TextEditor(text: $newMemoText)
.padding(.horizontal)
} // VStack
.navigationBarTitle(Text(""),displayMode: .inline)
.navigationBarItems(trailing: Button("完了") {
self.presentationMode.wrappedValue.dismiss()
}.padding())
// .onApperはView表示時に発生する処理のこと
// NewMemoViewへの遷移から、0.5秒ずらしてenumの値を更新し、キーボード表示の処理を行う
.onAppear() { // ✅ ④
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
foucsedField = .title
}
}
} // body
} // View
①
フォーカスのフラグを管理するための列挙型enumを宣言します⬇︎
// フォーカスのフラグに使用するenum
enum Field { case title } // ✅ ①
②
@FocusedStateを付与したプロパティを宣言します。
型はenumで定義した名前を?で
オプショナルラップして宣言しましょう⬇︎
@FocusState private var foucsedField: Field? // ✅ ②
③
.focusedをフォーカス制御したい対象に付与します。
②のfoucsedFieldをバインディングで指定し、
引数equals: にenumで作成したケースを指定します。
これにより、enumがtitle.になったときに
対象がフォーカスされるようになります。
.focused($foucsedField, equals: .title) // ✅ ③
④
.onApperのクロージャ内に処理を記述することで、
対象のViewが表示されたタイミングで処理を実行させることができます。
つまり、NewMemoViewに画面遷移したた時、処理が実行されます。
クロージャ内の処理を見ていきます、これが大事です⬇︎
.onAppear() { // ✅ ④
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
foucsedField = .title
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { 処理 }
これは何かというと、クロージャ内の処理を0.6秒遅らせます。
これが.onAppear内にあることで、
「画面表示から0.6秒後にクロージャ内の処理を実行する」
という指令をすることができます。
0.6秒後に、FoucsedFieldの値を.titleに更新しています。
:なぜ処理を遅らせることが必要?
入力キーボードはテキストフィールドに
フォーカスがされた時に出現します。
今回はそれを自動で出現させようとしているわけですが、
DispachQueueによる遅延が無いと、画面遷移と
フォーカス判定のタイミングが噛み合わないようで、
フォーカスがうまく実行されません。
なので、画面遷移が無事終わるまでフォーカス判定の実行を
少し待ってあげているということですね。
遅延時間設定は色々試してみましたが、
0.5秒まではうまく発動しない時がありました。
0.6秒だと現在安定しています。
これで、画面遷移と同時に入力キーボードが
出現してくれるようになりました。お疲れ様です✊
■まとめ
なかなか盛りだくさんとなってしまいましたね🙇♂️
次は最後のパート、リスト内に新規メモの内容が追加されるよう
実装していきます。
ここまで読んでいただきありがとうございました!
ではでは🙌
[一緒に読んでほしい記事]
◆メモサンプルアプリ作成
◆本記事のPart1
以上