Skip to content

Commit

Permalink
ListView: refactor vm; fix crash on delete; scroll on addition
Browse files Browse the repository at this point in the history
  • Loading branch information
eagleoflqj committed Dec 15, 2024
1 parent b568289 commit a74139a
Showing 1 changed file with 60 additions and 50 deletions.
110 changes: 60 additions & 50 deletions src/config/ListView.swift
Original file line number Diff line number Diff line change
@@ -1,76 +1,86 @@
import SwiftUI

private func deserialize(_ value: Any) -> [Any] {
private struct ListItem: Identifiable {
let id = UUID()
var value: Any
}

private func deserialize(_ value: Any) -> [ListItem] {
guard let value = value as? [String: Any] else {
return []
}
return (0..<value.count).compactMap { i in value[String(i)] }
return (0..<value.count).compactMap { i in ListItem(value: value[String(i)]!) }
}

private func serialize(_ value: [Any]) -> [String: Any] {
private func serialize(_ value: [ListItem]) -> [String: Any] {
return value.enumerated().reduce(into: [String: Any]()) { result, pair in
result[String(pair.offset)] = pair.element
}
}

private struct ListSectionHeader: View {
let children: Any?
@Binding var value: Any

var body: some View {
HStack {
Spacer()
Button {
var list = deserialize(value)
if let children = children as? [[String: Any]] {
list.append(
children.reduce(into: [:]) { result, child in
result[child["Option"] as! String] = child["DefaultValue"] ?? ""
} as NSDictionary)
} else {
list.append("")
}
value = serialize(list)
} label: {
Image(systemName: "plus")
}
}
result[String(pair.offset)] = pair.element.value
}
}

struct ListSubView: OptionViewProtocol {
let label: String
let data: [String: Any]
@Binding var value: Any
@State private var list = [ListItem]()

var body: some View {
let type = data["Type"] as! String
let optionViewType = toOptionViewType(["Type": String(type.suffix(type.count - "List|".count))])
var list = deserialize(value)
List {
Section(header: ListSectionHeader(children: data["Children"], value: $value)) {
ForEach(list.indices, id: \.self) { i in
AnyView(
optionViewType.init(
label: "", data: data, // List|Entries need this.
value: Binding<Any>(
get: { list[i] },
set: {
list[i] = $0
value = serialize(list)
ScrollViewReader { proxy in
List {
Section {
ForEach(Array(zip(list.indices, list)), id: \.1.id) { i, item in
AnyView(
optionViewType.init(
label: "", data: data, // List|Entries need this.
value: Binding<Any>(
get: { item.value },
set: {
list[i] = ListItem(value: $0)
value = serialize(list)
}
))
).id(item.id) // For scrolling.
}
.onDelete { offsets in
list.remove(atOffsets: offsets)
value = serialize(list)
}
.onMove { indices, newOffset in
list.move(fromOffsets: indices, toOffset: newOffset)
value = serialize(list)
}
} header: {
HStack {
Spacer()
Button {
if let children = data["Children"] as? [[String: Any]] {
list.append(
ListItem(
value:
children.reduce(into: [:]) { result, child in
result[child["Option"] as! String] = child["DefaultValue"] ?? ""
}))
} else {
list.append(ListItem(value: ""))
}
value = serialize(list)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
withAnimation {
proxy.scrollTo(list.last?.id)
}
)))
}
.onDelete { offsets in
list.remove(atOffsets: offsets)
value = serialize(list)
}
.onMove { indices, newOffset in
list.move(fromOffsets: indices, toOffset: newOffset)
value = serialize(list)
}
} label: {
Image(systemName: "plus")
}
}
}
}
}.navigationTitle(label)
.onAppear {
list = deserialize(value)
}
}
}

Expand Down

0 comments on commit a74139a

Please sign in to comment.