今日はプロジェクト7を開始します。これはまだ間違いなく小さなアプリです。ただし、このプロセスでは、別の画面を表示する方法、画面間でデータを共有する方法、ユーザーデータを読み込んで保存する方法など、SwiftUIのスキルを次のレベルに引き上げるのに役立つ種類の機能を学習します。
@ObservedObject、sheet()、onDelete()
プログラム名:iExpense
@Stateが構造体でのみ機能する理由
Why @State only works with structs
すべてがひどく理論的に聞こえることは知っていますが、ここでひねりを加えます。これUserは、プロパティ自体が変更されて@Stateいないクラスなので、何も気付かず、ビューを再読み込みできません。はい、クラス内の値は変化していますが、@Stateそれらを監視していません。そのため、実際に起こっているのは、クラス内の値が変更されているのに、その変更を反映するようにビューがリロードされていないということです。
import SwiftUI
struct User {
//class User {
var firstName = "Bilbo"
var lastName = "Baggins"
}
struct ContentView: View {
@State private var user = User()
var body: some View {
VStack {
Text("Your name is \(user.firstName) \(user.lastName).")
TextField("First name", text: $user.firstName)
TextField("Last name", text: $user.lastName)
}
}
}
そのコードは意図したとおりに機能しません。userプロパティを@Stateでマークしました。これは、外部クラスではなくローカル構造体を追跡するように設計されています。その結果、テキストフィールドに入力できますが、上のテキストビューは更新されません。
@ObservedObjectとのSwiftUI状態の共有
Sharing SwiftUI state with @ObservedObject
@ObservedObjectと@EnvironmentObject
クラス内に多数のプロパティが存在する可能性がありますが、このようにしてより広い世界に公開されるのはごく一部です。
import SwiftUI
class User: ObservableObject {
@Published var firstName = "Bilbo"
@Published var lastName = "Baggins"
}
struct ContentView: View {
@ObservedObject var user = User()
var body: some View {
VStack {
Text("Your name is \(user.firstName) \(user.lastName).")
TextField("First name", text: $user.firstName)
TextField("Last name", text: $user.lastName)
}
}
}
ObservableObjectプロトコルに準拠したクラスを作成します。
@Publishedクラスを使用するすべてのビューが変更されたときに更新されるように、いくつかのプロパティをマークします。
@ObservedObjectプロパティラッパーを使用して、クラスのインスタンスを作成します。
ビューの表示と非表示
Showing and hiding views
ビュー自体を閉じる方法です。はい、ユーザーは下にスワイプするだけでよい
import SwiftUI
struct SecondView: View {
var name: String
var body: some View {
Text("Hello, \(name)!")
}
}
struct ContentView: View {
@State private var showingSheet = false
var body: some View {
Button("Show Sheet") {
// show the sheet
self.showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
// contents of the sheet
SecondView(name: "@twostraws")
}
}
}
@Environment
この例では、ビューのプレゼンテーションモードを環境から読み取ります。
ビューの表示モードには2つのデータしか含まれていませんが、どちらも便利です。ビューが現在画面に表示されているかどうかを格納するプロパティと、ビューをすぐに閉じるためのメソッドです
import SwiftUI
struct SecondView: View {
var name: String
@Environment(\.presentationMode) var presentationMode
var body: some View {
//Text("Hello, \(name)!")
Button("Dismiss") {
self.presentationMode.wrappedValue.dismiss()
}
}
}
struct ContentView: View {
@State private var showingSheet = false
var body: some View {
Button("Show Sheet") {
// show the sheet
self.showingSheet.toggle()
}
.sheet(isPresented: $showingSheet) {
// contents of the sheet
SecondView(name: "@twostraws")
}
}
}
onDelete()を使用したアイテムの削除
Deleting items using onDelete()
onDelete()修飾子はForEachにのみ存在する
import SwiftUI
struct ContentView: View {
@State private var numbers = [Int]()
@State private var currentNumber = 1
func removeRows(at offsets: IndexSet) {
numbers.remove(atOffsets: offsets)
}
var body: some View {
NavigationView{
VStack {
// not use for ondelete
// List(numbers, id: \.self) {
// Text("\($0)")
// }
List {
ForEach(numbers, id: \.self) {
Text("\($0)")
}
.onDelete(perform: removeRows)
}
Button("Add Number") {
self.numbers.append(self.currentNumber)
self.currentNumber += 1
}
.navigationBarItems(leading: EditButton())
}
}
}
}
.navigationBarItems(leading: EditButton())
アプリを実行すると、いくつかの数字を追加できることがわかります。次に、[編集]をタップして、それらの行の削除を開始します。
UserDefaultsを使用したユーザー設定の保存
Storing user settings with UserDefaults
struct ContentView: View {
// @State private var tapCount = 0
@State private var tapCount = UserDefaults.standard.integer(forKey: "Tap")
var body: some View {
Button("Tap count: \(tapCount)") {
self.tapCount += 1
UserDefaults.standard.set(self.tapCount, forKey: "Tap")
}
}
}
Codableを使用したSwiftオブジェクトのアーカイブ
Archiving Swift objects with Codable
プロセスのこの部分は、という新しいタイプによって駆動されJSONEncoderます。その仕事はCodable、JavaScript Object Notation(JSON)に準拠したオブジェクトを受け取り、そのオブジェクトを送り返すことです。この名前は、それがJavaScriptに固有であることを示していますが、非常に高速でシンプルなため、実際にはすべて使用します。
userデータをJSONデータに変換するには、でencode()メソッドを呼び出す必要がありますJSONEncoder。これはエラーをスローする可能性があるため、エラーとともに、tryまたはtry?エラーを適切に処理するために呼び出す必要があります。