メモ > 技術 > 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("編集")
}
}
}
}
#Preview {
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("編集")
}
}
}
}
#Preview {
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("編集")
}
}
}
}
#Preview {
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")
}