Почему оператор инклюзивного диапазона выдает ошибку, а исключающий - нет? - PullRequest
2 голосов
/ 07 февраля 2020

Я пытаюсь использовать ForEach для построения параметров средства выбора в SwiftUI, но оператор диапазона, кажется, не работает так, как я ожидал.

Вот мой код:

struct ContentView: View {

@State private var inputString = ""
@State private var inputUnits = 0

let units = ["Fahrenheit", "Celsius", "Kelvin"]

var body: some View {
    Form {
        Section(header: Text("Convert:")) {
            TextField("Enter input", text: $inputString)
            Picker("Unit", selection: $inputUnits) {
                ForEach(0 ... 2) {
                    Text("\(self.units[$0])")
                }
            }
        }
    }
}

}

Компилятор имеет проблему с оператором диапазона .... Выдает две ошибки:

  1. Generi c Параметр 'ID' не может быть выведен
  2. Ссылка инициализатора 'init (_: content :)' на 'ForEach' требует, чтобы ' Int 'соответствует' Identifiable '

Любопытно, что здесь работает оператор ..<, поэтому если я вместо этого использую эту строку кода, код скомпилируется:

ForEach(0 ..< 3) {

Мне это кажется ошибкой, но есть ли какая-то разница между этими операторами, о которой я не знаю?

Ответы [ 3 ]

2 голосов
/ 07 февраля 2020

Это потому, что 0 ... 2 - это ClosedRange<Int>, а 0 ..< 3 - это Range<Int>.

Если вы посмотрите на init функции ForEach здесь вы увидите, что есть: init(Range<Int>, content: (Int) -> Content), который соответствует использованию вашего 0 ..< 3.

Во многих других местах в swift вы можете смешивать ... и ..<, потому что функция либо перегружена, либо Collection может использовать дженерики с протоколом RangeExpression. В этом случае, поскольку ForEach не имеет Collection поддержки, кроме вашего ввода, поэтому перегрузка является единственно разумным вариантом.

Это может быть выполнено так:

extension ForEach where Data == ClosedRange<Int>, ID == Int, Content: View {
    public init(_ data: ClosedRange<Int>, @ViewBuilder content: @escaping (Int) -> Content) {
        self.init(data, id: \.self, content: content)
    }
}
2 голосов
/ 07 февраля 2020

Да, есть разница:

Для оператора Range<Int> (..<) существует явное расширение ForEach, которое указывает связанные типы

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension ForEach where Data == Range<Int>, ID == Int, Content : View {

, тогда как для ClosedRange<Int> (...) такого не существует, и это считается коллекцией более обобщенным c

/// A structure that computes views on demand from an underlying collection of
/// of identified data.
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public struct ForEach<Data, ID, Content> where Data : RandomAccessCollection, ID : Hashable {

, поэтому требуется явное предоставление Введите идентификатор, как показано ниже

ForEach(0...2, id: \.self) {

, или вы можете объявить свой собственный добавочный номер

extension ForEach where Data == ClosedRange<Int>, ID == Int, Content : View {
    public init(_ data: ClosedRange<Int>, @ViewBuilder content: @escaping (Int) -> Content) {
        self.init(data, id: \.self, content: content)
    }
}

, а затем просто использовать

ForEach(0...2) {
0 голосов
/ 07 февраля 2020

Решение для реализации ForEach с операторами Range заключается в следующем: -

Для массива счета 3

Первый с закрытым диапазоном

    ForEach(0...2, id: \.self){ index in
            Text("\(self.array[index])")
        }

Второй

    ForEach(0..<3, id: \.self){ index in
            Text("\(self.array[index])")
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...