SwiftUI stati c Список странного поведения при повторном использовании - PullRequest
2 голосов
/ 29 мая 2020

Я столкнулся со странным поведением при использовании списка c в SwiftUI. Я не могу определить, ошибка ли это SwiftUI или что-то я делаю не так. У меня есть очень простой список, который выглядит так:

var body: some View {
    List {
        SettingsPickerView<TrigonometryUnit>(title: "Trigonometry Units", selection: $viewModel.trigonometryUnitIndex, items: TrigonometryUnit.allCases)
        SettingsPickerView<DecimalSeparator>(title: "Decimal Separator", selection: $viewModel.decimalSeparatorIndex, items: DecimalSeparator.allCases)
        SettingsPickerView<GroupingSeparator>(title: "Grouping Separator", selection: $viewModel.groupingSeparatorIndex, items: GroupingSeparator.allCases)
        SettingsPickerView<ExponentSymbol>(title: "Exponent Symbol", selection: $viewModel.exponentSymbolIndex, items: ExponentSymbol.allCases)
    }
}

Каждая ячейка списка выглядит так:

struct SettingsPickerView<T: Segmentable>: View {

    let title: String
    @Binding var selection: Int
    let items: [T]

    var body: some View {
        Section(header: Text(title)) {
            ForEach(items.indices) { index in
                self.cell(for: self.items[index], index: index)
            }
        }
    }

    private func cell(for item: T, index: Int) -> some View {
        print(title, item.title, items.map({ $0.title }))
        return Button(action: {
            self.selection = index
        }, label: {
            HStack {
                Text(item.title)
                Spacer()
                if index == self.selection {
                    Image(systemName: "checkmark")
                        .font(.headline)
                        .foregroundColor(.rpnCalculatorOrange)
                }
            }
        })
    }

}

И, наконец, так выглядит объект Segmentable например:

enum GroupingSeparator: Int, CaseIterable {

    case defaultSeparator
    case space
    case comma

}

extension GroupingSeparator: Segmentable {

    var id: String {
        switch self {
        case .defaultSeparator:
            return "groupingSeparator.default"
        case .space:
            return "groupingSeparator.space"
        case .comma:
            return "groupingSeparator.comma"
        }
    }

    var title: String {
        switch self {
        case .defaultSeparator:
            return "Default"
        case .space:
            return "Space"
        case .comma:
            return "Comma"
        }
    }

}

Когда загружен SettingsView. все выглядит нормально. Но как только я начинаю прокрутку и создаются экземпляры некоторых других ячеек, отображаются некоторые ячейки, но не нужные. Вот несколько скриншотов и логов.

Когда представление загружено, без прокрутки, вот как выглядит экран:

enter image description here

Но то, что я получил на консоли, довольно странно и не соответствует порядку SettingsPickerView, записанного в основном представлении:

Trigonometry Units Radians ["Radians", "Degrees"] <-- Fine
Trigonometry Units Degrees ["Radians", "Degrees"] <-- Fine
Decimal Separator Default ["Default", "Dot", "Comma"] <-- Fine
Decimal Separator Default ["Default", "Dot", "Comma"] <-- Fine
Trigonometry Units Degrees ["Radians", "Degrees"] <-- Not expected. Should be Grouping Separator
Trigonometry Units Radians ["Radians", "Degrees"] <-- Not expected. Should be Grouping Separator

Второй раздел в порядке и правильно отображается:

enter image description here

Но третья секция полностью сломана:

enter image description here

Третья Раздел отображает заголовок правильно, но отображает некоторые данные первого раздела. Я попытался добавить идентификатор к кнопке в ячейке, потому что проблема выглядит так, как будто SwiftUI не может определить правильные данные. Но добавление идентификатора к кнопке разорвало привязку, и флажок больше не меняется.

private func cell(for item: T, index: Int) -> some View {
    print(title, item.title, items.map({ $0.title }))
    return Button(action: {
        self.selection = index
    }, label: {
        HStack {
            Text(item.title)
            Spacer()
            if index == self.selection {
                Image(systemName: "checkmark")
                    .font(.headline)
                    .foregroundColor(.rpnCalculatorOrange)
            }
        }
    })
        .id(UUID().uuidString) // This solve the display issue but broke the binding.
}

У кого-то подобное раньше случалось?

Заранее благодарим за помощь.

1 Ответ

1 голос
/ 29 мая 2020

Вот фиксированный блок кода (только из-за используемых индексов Список запутан и повторно использует строки, поэтому решение состоит в том, чтобы сделать строки идентифицируемыми по элементам).

Протестировано с Xcode 11.4

struct PickerView<T: Segmentable>: View {

    // ... other code here

    var body: some View {
        Section(header: Text(title)) {
            // Corrected section construction !!
            ForEach(Array(items.enumerated()), id: \.element.id) { index, _ in
                self.cell(for: self.items[index], index: index)
            }
        }
    }

    // ... other code here
...