I created a List for a settings menu with two sections where you can reorder/add/remove a set of pages. It should work similar to Apple's Today Widget editing menu where you can add and reorder items.
Connector class code snippet:
class SettingsConnector: ObservableObject {
enum DetailsPage {
case page1
case page2
case page3
func printed() -> String {
switch self {
case .page1:
return "Page 1"
case .page2:
return "Page 2"
case .page3:
return "Page 3"
}
}
}
@Published var detailsPages: [DetailsPage] = [.page1, .page2]
@Published var hiddenDetailsPages: [DetailsPage] = [.page3]
}
SwiftUI code snippet:
List {
Section {
ForEach(self.connector.detailsPages, id: \.self) { page in
HStack(spacing: 0) {
Text("\(page.printed())")
Spacer()
}
}
.onMove { (source, destination) in
self.connector.detailsPages.move(fromOffsets: source, toOffset: destination)
}
.onDelete { (rows) in
rows.forEach { (i) in
let page: SettingsConnector.DetailsPage = self.connector.detailsPages[i]
self.connector.detailsPages.remove(at: i)
self.connector.hiddenDetailsPages.insert(page, at: 0
}
}
}
Section {
ForEach(self.connector.hiddenDetailsPages, id: \.self) { page in
HStack(spacing: 0) {
Image(systemName: "plus.circle.fill")
.foregroundColor(.green)
.padding(.horizontal, 5)
Text("Add \(page.printed())")
.padding(.horizontal, 5)
Spacer()
}
}
}
}
When I delete a page on the first section, it does appear in the hiddenDetailsPages sections.
However, even though there is no direct relation between the sections, the cell view is just moved to the second section but using the drawing code from the first section. The drawing instructions from the second section are simply not used even though the cell appears in that section.
Am I getting something completely wrong or is this a bug in SwiftUI?
There still misses definition of connector in ContentView.
Is it
@State private var connector = SettingsConnector()
If that's what you did, replace with ObservedObject
@ObservedObject private var connector = SettingsConnector()
Please, give the complete and exact code.
Here is what I tested (delete works better)
import SwiftUI
class SettingsConnector: ObservableObject {
enum DetailsPage {
case page1
case page2
case page3
func printed() -> String {
switch self {
case .page1:
return "Page 1"
case .page2:
return "Page 2"
case .page3:
return "Page 3"
}
}
}
@Published var detailsPages: [DetailsPage] = [.page1, .page2]
@Published var hiddenDetailsPages: [DetailsPage] = [.page3]
}
struct ContentView: View {
@ObservedObject private var connector = SettingsConnector()
var body: some View {
List {
Section {
ForEach(self.connector.detailsPages, id: \.self) { page in
HStack(spacing: 0) {
Text("\(page.printed())")
Spacer()
}
}
.onMove { (source, destination) in
self.connector.detailsPages.move(fromOffsets: source, toOffset: destination)
}
.onDelete { rows in
rows.forEach { i in
let page: SettingsConnector.DetailsPage = self.connector.detailsPages[i]
self.connector.detailsPages.remove(at: i)
self.connector.hiddenDetailsPages.insert(page, at: 0)
}
}
}
Section {
VStack{
Text("Hidden \(self.connector.hiddenDetailsPages.count)")
ForEach(self.connector.hiddenDetailsPages, id: \.self) { page in
HStack(spacing: 0) {
Image(systemName: "plus.circle.fill")
.foregroundColor(.green)
.padding(.horizontal, 5)
Text("Add \(page.printed())")
.padding(.horizontal, 5)
Spacer()
}
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Notes:
- Added print line 51 for check
- VStack line 50 is needed, otherwise, section is not redrawn !
Fear that's a problem in List.
I will file a bug report, unless someone has an explantion for this.
To be able to move items, I had to add a NavigationView
(see this: h ttps://medium.com/programming-with-swift/move-list-item-with-swiftui-b21fdc3cba3d):
struct ContentView: View {
@ObservedObject private var connector = SettingsConnector()
var body: some View {
NavigationView { // NEEDED TO MOVE ITEMS
List {
Section {
ForEach(self.connector.detailsPages, id: \.self) { page in
HStack(spacing: 0) {
Text("\(page.printed())")
Spacer()
}
}
.onMove { (source, destination) in
self.connector.detailsPages.move(fromOffsets: source, toOffset: destination)
}
.onDelete { rows in
rows.forEach { i in
let page: SettingsConnector.DetailsPage = self.connector.detailsPages[i]
self.connector.detailsPages.remove(at: i)
self.connector.hiddenDetailsPages.insert(page, at: 0)
}
}
}
// Spacer()
//
Section {
VStack{
Text("Hidden \(self.connector.hiddenDetailsPages.count)")
ForEach(self.connector.hiddenDetailsPages, id: \.self) { page in
HStack(spacing: 0) {
Image(systemName: "plus.circle.fill")
.foregroundColor(.green)
.padding(.horizontal, 5)
Text("Add \(page.printed())")
.padding(.horizontal, 5)
Spacer()
}
}
}
}
}
.navigationBarTitle(Text("Nav Title"))
.navigationBarItems(trailing: EditButton())
}
}
}
With the addition of Navigation (line 05), if we suppress VSTack lines 29-31, then onDelete, rows moves to second section, but the + is not drawn ?!
definetely, there is some mysterious interaction between List and VStack…