Develop Apps for iOSをやってみる 〜その6〜
はじめに
DX推進室の野上です。
前回 の続きです
どこから?
今回はここから続けます。
全体のやっと半分です。
最初に
今回も用意されたプロジェクトファイルがありますが、今までと同じように無視します。
データバインディングでデータを渡す
Edit Viewにバインディングデータを渡す
DetailViewからEditViewにバインディングされてデータを渡します。
編集が完了したら、DetailView側でもデータが変更されるようにしましょう。
EditViewのscrumDataを@Stateから@Bindingに変更します。
@BindingをつけるとView間でデータを共有することができます。
import SwiftUI
struct EditView: View {
@Binding var scrumData: DailyScrum.Data
@State private var newAttendee = ""
var body: some View {
List {
Section(header: Text("Meeting Info")) {
TextField("Title", text: $scrumData.title)
HStack {
Slider(value: $scrumData.lengthInMinutes, in: 5...30, step: 1.0) {
Text("Length")
}
.accessibilityValue(Text("\(Int(scrumData.lengthInMinutes)) minutes"))
Spacer()
Text("\(Int(scrumData.lengthInMinutes)) minutes")
.accessibilityHidden(true)
}
ColorPicker("Color", selection: $scrumData.color)
.accessibilityLabel(Text("color picker"))
}
Section(header: Text("Attendees")) {
ForEach(scrumData.attendees, id: \.self) { attendee in
Text(attendee)
}
.onDelete { indices in
scrumData.attendees.remove(atOffsets: indices)
}
HStack {
TextField("New Attendee", text: $newAttendee)
Button(action: {
withAnimation {
scrumData.attendees.append(newAttendee)
}
newAttendee = ""
}) {
Image(systemName: "plus.circle.fill")
.accessibilityLabel(Text("add attendee"))
}
.disabled(newAttendee.isEmpty)
}
}
}
.listStyle(InsetGroupedListStyle())
}
}
struct EditView_Previews: PreviewProvider {
static var previews: some View {
EditView(scrumData: .constant(DailyScrum.data[0].data))
}
}
これでEditView側で受け取る準備はできました。
次はDetailView側でデータの送り側の変更をしましょう。
@Stateプロパティをつけてdataという名前のDailyScrum.Data型の変数を作ります。
そのdataをEditボタンが押された際に、EditViewに渡されるようにしましょう。
またDoneボタンが押されて戻ってきた場合にscrumを更新する処理を呼び出します。
送り側のscrumもletから@Bindingに変更しておきましょう。
import SwiftUI
struct DetailView: View {
@Binding var scrum: DailyScrum
@State private var data: DailyScrum.Data = DailyScrum.Data()
@State private var isPresented = false
var body: some View {
List {
Section(header: Text("Meeting Info")) {
NavigationLink(
destination: MeetingView()) {
Label("Start Meeting", systemImage: "timer")
.font(.headline)
.foregroundColor(.accentColor)
.accessibilityLabel(Text("start meeting"))
}
HStack {
Label("Length", systemImage: "clock")
.accessibilityLabel(Text("meeting length"))
Spacer()
Text("\(scrum.lengthInMinutes) minutes")
}
HStack {
Label("Color", systemImage: "paintpalette")
Spacer()
Image(systemName: "checkmark.circle.fill")
.foregroundColor(scrum.color)
}
.accessibilityElement(children: .ignore)
}
Section(header: Text("Attendees")) {
ForEach(scrum.attendees, id: \.self) { attendee in
Label(attendee, systemImage: "person")
.accessibilityLabel(Text("person"))
.accessibilityValue(Text(attendee))
}
}
}
.listStyle(InsetGroupedListStyle())
.navigationBarItems(trailing: Button("Edit") {
isPresented = true
data = scrum.data
})
.navigationTitle(scrum.title)
.fullScreenCover(isPresented: $isPresented) {
NavigationView {
EditView(scrumData: $data)
.navigationTitle(scrum.title)
.navigationBarItems(leading: Button("Cancel") {
isPresented = false
}, trailing: Button("Done") {
isPresented = false
scrum.update(from: data)
})
}
}
}
}
struct DetailView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
DetailView(scrum: .constant(DailyScrum.data[0]))
}
}
}
ここまででDetailViewとEditViewの間でデータの共有はできましたが、まだbuildエラーが出ます。
他のファイルも修正が必要なためです。
次はScrumsViewとDetailView間のデータバインディングを設定します。
ScrumsViewのscrumsを@Bindingに変更します。
Previewの方もconstantで渡すようにします。
また、scrumsから一つずつのbindingを取り出すメソッドbindingを追加します。
DetailViewにはbindingされたscrumを渡すようにします。
import SwiftUI
struct ScrumsView: View {
@Binding var scrums: [DailyScrum]
var body: some View {
List {
ForEach(scrums) { scrum in
NavigationLink(destination: DetailView(scrum: binding(for: scrum))) {
CardView(scrum: scrum)
}
.listRowBackground(scrum.color)
}
}
.navigationTitle("Daily Scrums")
.navigationBarItems(trailing: Button(action: {}) {
Image(systemName: "plus")
})
}
private func binding(for scrum: DailyScrum) -> Binding<DailyScrum> {
guard let scrumIndex = scrums.firstIndex(where: { $0.id == scrum.id }) else {
fatalError("Can't find scrum in array")
}
return $scrums[scrumIndex]
}
}
struct ScrumsView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ScrumsView(scrums: .constant(DailyScrum.data))
}
}
}
これで、ScrumsViewとDetailView間のデータ共有ができました。
同じようにScrumdingerAppとScrumsView間のデータバインディングを設定します。
@Stateをつけたscrumsを定義して、このデータにバインディングされるようにします。
ScrumsViewにはscrumsのバインディングを渡します。
import SwiftUI
@main
struct ScrumdingerApp: App {
@State private var scrums = DailyScrum.data
var body: some Scene {
WindowGroup {
NavigationView {
ScrumsView(scrums: $scrums)
}
}
}
}
アプリケーションを実行してDetailViewからEditしてみてください。
タイトルや色、時間などが変更できるようになっていて、DetailViewに戻ってきても反映されているはずです。
ここまでのまとめ
- @Bindingを用いてデータバインディングする
- 大元となるデータから引き継いでいく
次回は状態とライフサイクルの管理です。