Memo

メモ > 技術 > IDE: Xcode > SwiftUI+リストの編集

■SwiftUI+リストの編集
■リストの編集(配列版) SwiftUIでリストを編集する - すいすいSwift https://swiswiswift.com/2019-12-17/ 【SwiftUI】Listの行削除 | カピ通信 https://capibara1969.com/1443/ [SwiftUI] List の要素削除 の実装方法 | SmallDeskSoftware https://software.small-desk.com/development/2020/10/08/swiftui-list-ondelete/ 【SwiftUI】Viewの編集モード(editMode)について | カピ通信 https://capibara1969.com/2625/ 行単位の直接編集モードは作れるかも? 【SwiftUI】TextField付きAlertを表示する - .NET ゆる〜りワーク https://www.yururiwork.net/%E3%80%90swiftui%E3%80%91textfield%E4%BB%98%E3%81%8Dalert%E3%82%92%E8%A1%... 入力欄付きのアラートは現状SwiftUIの標準では作れないみたい? もしくは可能なら、カラの状態で一覧に追加して、直接編集モードを有効にしておく…ができるならそれもいいかもしれない ContentView.swift
import SwiftUI struct ContentView: View { @State private var users = ["Paul", "Taylor", "Adele"] @State private var showDialog = false @State private var inputName = "" var userDefaults = UserDefaults.standard var body: some View { NavigationView { List { ForEach(users, id: \.self) { user in Text(user) } .onMove(perform: move) .onDelete(perform: delete) } .navigationBarTitle("ユーザ", displayMode: .inline) .navigationBarItems(trailing: HStack { Button(action: { inputName = "" showDialog = true }) { Text("追加") }.sheet(isPresented: $showDialog, onDismiss: { // 要素を追加すると、エミュレータではリストの編集が正しく動作しなくなる? users.insert(inputName, at: 0) // userDefaultsに値を保存 userDefaults.set(users, forKey: "users") }, content: { Text("これはシートの内容です。") .padding(10) TextField("ユーザ名", text: $inputName) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding(10) Button(action: { // 要素を追加すると、リストの編集が正しく動作しなくなる? //users.insert(inputName, at: 0) //users.append("Test") showDialog = false }) { Text("閉じる") }.padding(10) }) //EditButton() MyEditButton() } ) .onAppear { // userDefaultsから値を取得 users = UserDefaults.standard.stringArray(forKey: "users") ?? [String]() } } } func move(from source: IndexSet, to destination: Int) { users.move(fromOffsets: source, toOffset: destination) // userDefaultsに値を保存 userDefaults.set(users, forKey: "users") } func delete(at offsets: IndexSet) { users.remove(atOffsets: offsets) // userDefaultsに値を保存 userDefaults.set(users, forKey: "users") } } struct MyEditButton: View { @Environment(\.editMode) var editMode var body: some View { Button(action: { withAnimation() { if editMode?.wrappedValue.isEditing == true { editMode?.wrappedValue = .inactive } else { editMode?.wrappedValue = .active } } }) { if editMode?.wrappedValue.isEditing == true { Text("完了") } else { Text("編集") } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
■リストの編集(構造体版) ContentView.swift
import SwiftUI struct Article: Identifiable { var id = UUID() var title: String var text: String } struct ContentView: View { @State private var articles: [Article] = [] @State private var showDialog = false @State private var inputTitle = "" @State private var inputText = "" var body: some View { NavigationView { List { ForEach(articles) { article in Text(article.title) } .onMove(perform: move) .onDelete(perform: delete) } .navigationBarTitle("記事", displayMode: .inline) .navigationBarItems(trailing: HStack { Button(action: { showDialog = true inputTitle = "" inputText = "" }) { Text("追加") }.sheet(isPresented: $showDialog, onDismiss: { articles.insert( Article( title: inputTitle, text: inputText ) , at: 0) }, content: { Text("記事を追加します。") .padding(10) TextField("タイトル", text: $inputTitle) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding(10) TextField("テキスト", text: $inputText) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding(10) Button(action: { showDialog = false }) { Text("閉じる") }.padding(10) }) MyEditButton() } ) } } func move(from source: IndexSet, to destination: Int) { articles.move(fromOffsets: source, toOffset: destination) } func delete(at offsets: IndexSet) { articles.remove(atOffsets: offsets) } } struct MyEditButton: View { @Environment(\.editMode) var editMode var body: some View { Button(action: { withAnimation() { if editMode?.wrappedValue.isEditing == true { editMode?.wrappedValue = .inactive } else { editMode?.wrappedValue = .active } } }) { if editMode?.wrappedValue.isEditing == true { Text("完了") } else { Text("編集") } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
■リストの編集(構造体版 / 高機能版) ContentView.swift
import SwiftUI struct ContentView: View { @State private var articles: [Article] = [] var body: some View { NavigationView { List { ForEach(articles) { article in NavigationLink(destination: EditView(id: article.id).onDisappear(perform: { articles = loadArticles() })) { Text(article.title) } } .onMove(perform: move) .onDelete(perform: delete) } .navigationBarTitle("記事", displayMode: .inline) .navigationBarItems(trailing: HStack { NavigationLink(destination: AddView().onDisappear(perform: { articles = loadArticles() })) { Text("追加") } MyEditButton() } ) } .onAppear { articles = loadArticles() } } func move(from source: IndexSet, to destination: Int) { articles.move(fromOffsets: source, toOffset: destination) saveArticles(data: articles) } func delete(at offsets: IndexSet) { articles.remove(atOffsets: offsets) saveArticles(data: articles) } } struct MyEditButton: View { @Environment(\.editMode) var editMode var body: some View { Button(action: { withAnimation() { if editMode?.wrappedValue.isEditing == true { editMode?.wrappedValue = .inactive } else { editMode?.wrappedValue = .active } } }) { if editMode?.wrappedValue.isEditing == true { Text("完了") } else { Text("編集") } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
AddView.swift
import SwiftUI struct AddView: View { @Environment(\.presentationMode) var presentation @State private var title = "" @State private var text = "" var body: some View { VStack { Text("記事を追加します。") .padding(10) TextField("タイトル", text: $title) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding(10) TextField("テキスト", text: $text) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding(10) Button(action: { let temps = loadArticles() var articles: [Article] = [] articles.append( Article( title: title, text: text ) ) for temp in temps { articles.append(temp) } saveArticles(data: articles) self.presentation.wrappedValue.dismiss() }) { Text("追加") }.padding(10) Spacer() } } } struct AddView_Previews: PreviewProvider { static var previews: some View { AddView() } }
EditView.swift
import SwiftUI struct EditView: View { @Environment(\.presentationMode) var presentation @State var id: UUID @State private var title = "" @State private var text = "" var userDefaults = UserDefaults.standard var body: some View { VStack { Text("記事を編集します。") .padding(10) TextField("タイトル", text: $title) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding(10) TextField("テキスト", text: $text) .textFieldStyle(RoundedBorderTextFieldStyle()) .padding(10) Button(action: { let temps = loadArticles() var articles: [Article] = [] for temp in temps { if temp.id == id { articles.append( Article( id: id, title: title, text: text ) ) } else { articles.append(temp) } } saveArticles(data: articles) self.presentation.wrappedValue.dismiss() }) { Text("編集") }.padding(10) Spacer() } .onAppear { let articles = loadArticles() for article in articles { if article.id == id { title = article.title text = article.text break } } } } } struct EditView_Previews: PreviewProvider { static var previews: some View { EditView(id: UUID()) } }
common.swift
import Foundation struct Article: Identifiable, Codable { var id = UUID() var title: String var text: String } func loadArticles() -> [Article] { var articles: [Article] = [] if let data = UserDefaults.standard.value(forKey: "articles") as? Data { articles = try! PropertyListDecoder().decode(Array<Article>.self, from: data) } else { articles = [] } return articles } func saveArticles(data articles: [Article]) -> Void { UserDefaults.standard.set(try? PropertyListEncoder().encode(articles), forKey: "articles") }

Advertisement