4

I have a view (Form) that, basically, has a picker and a list of items that can be re-arranged by the user. I'd like that the list is always editable, so the user doesn't have to tap en Edit button (it seems unnatural for me to have an Edit button that only affects a small part of a form).

enter image description here

I succeeded on the aforementioned always-on-edit by setting the environmental variable editMode: .environment(\.editMode, .constant(.active)).

The problem is that while the form is editable the picker stops working (i.e., it doesn't switch to the list of options when you tap on it).

This is a MCVE that reproduces my problem:

import SwiftUI

struct SampleView: View {
  enum SortBy: String, CaseIterable {
    case FirstName
    case LastName

    var name: String {
      switch (self) {
      case .FirstName:
        return "First name"
      case .LastName:
        return "Last name"
      }
    }
  }
  @State var sortBy = SortBy.FirstName

  var body: some View {
    Form {
      Section(header: Text("General")) {
        Picker("Sort by", selection: $sortBy) {
          ForEach(SortBy.allCases, id: \.self) { sortBy in
            Text(sortBy.name).tag(sortBy)
          }
        }
      }

      Section(header: Text("Phone order")) {
        ForEach(1..<10) { number in
          Text("\(number)")
        }
        .onMove(perform: onMove)
        .padding(.leading, -39) // remove space dedicated to delete button
      }
    }
    .environment(\.editMode, .constant(.active)) // <--
    .navigationBarTitle(Text("Sample View"))
  }

  private func onMove(source: IndexSet, destination: Int) {
    // ...
  }
}

struct SampleView_Previews: PreviewProvider {
  static var previews: some View {
    NavigationView {
      SampleView()
    }
  }
}

Obviously I could eat my words and bring back the edit button, but I'd like to know if there is any way to keep the list editable and have the picker working appropriately.

I've already taken a look at this other question but it doesn't solve the problem.

I understand that the problem is related to the fact that when editing the navigation links are disabled.

As the editMode is a property of the form, one option I tried was to create a VStack with two separate forms, so only the one with the list is editable. The problem is that I cannot manage to get the correct layout, not to mention that I ended with two scrollable regions.

Another idea is to manually navigate to the picker's list using the onTapGesture, but I haven't found how to do it using SwiftUI.

The only solution I have so far is to change the picker's style to SegmentedPickerStyle, but it won't be very useful when the sorting options (the enum) grows.

cbuchart
  • 10,847
  • 9
  • 53
  • 93

1 Answers1

2

As it looks such combination is impossible for now... Find below proposed variant of alternate solution - the idea is to use in-section-only button to activate reordering

demo2

    @State private var reorderMode: EditMode = .inactive
    var body: some View {
        Form {
            Section(header: Text("General")) {
                Picker("Sort by", selection: $sortBy) {
                    ForEach(SortBy.allCases, id: \.self) { sortBy in
                        Text(sortBy.name).tag(sortBy)
                    }
                }
            }

            Section(header:
                HStack {
                    Text("Phone order")
                    Spacer()
                    Button(self.reorderMode == .inactive ? "Reorder" : "Done") {
                        self.reorderMode = self.reorderMode == .active ? .inactive : .active
                    }
                }
            ) {
                ForEach(1..<10) { number in
                    Text("\(number)")
                }
                .onMove(perform: onMove)
                    .padding(.leading, self.reorderMode == .active ? -39 : 0) // remove space dedicated to delete button
            }
        }
        .environment(\.editMode, $reorderMode)
        .navigationBarTitle(Text("Sample View"))
    }
Asperi
  • 228,894
  • 20
  • 464
  • 690