SwiftUI View с необязательным привязанным параметром

Я пытаюсь изменить Просмотр рейтинга Стюарта Линча, чтобы он поддерживал статическое представление и «элемент управления» для установки рейтинга. Для этого мне нужны некоторые дополнительные свойства для решения двух разных условий. Так currentRating — текущий рейтинг, позволяющий просмотреть это количество символов. maxRating — максимально возможный рейтинг и mutableRating — это привязанный текущий рейтинг, поэтому я могу отображать значение корнета в виде заполненных символов с остатком символов до maxRating незаполненными, а также незаполненным и зачеркнутым символом, чтобы установить рейтинг равным 0.

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

struct RatingView: View {
    var currentRating: Int?
    
    var maxRating: Int?
    @Binding var mutableRating: Int?
    
    var width:Int
    var color: UIColor
    var sfSymbol: String
    
    public init(
        currentRating: Int? = nil,
        maxRating: Int? = nil,
        mutableRating: Binding<Int?>,
//        mutableRating: Binding<Int>?,
//        mutableRating: (Binding<Int>)?,
        width: Int = 20,
        color: UIColor = .systemYellow,
        sfSymbol: String = "star"
    ) {
        self.currentRating = currentRating
        self.maxRating = maxRating
        self._mutableRating = mutableRating
//        self._mutableRating = mutableRating ?? Binding.constant(nil)
        self.width = width
        self.color = color
        self.sfSymbol = sfSymbol
    }
    
    public var body: some View {
        Text("")
    }
}

#Preview ("mutating") {
    struct PreviewWrapper: View {
        @State var rating: Int? = 3
        
        var body: some View {
            RatingView(
                maxRating: 5,
                mutableRating: $rating,
                width: 30,
                color: .red,
                sfSymbol: "heart"
            )
        }
    }
    return PreviewWrapper()
}

#Preview ("non mutating") {
    struct PreviewWrapper: View {
        
        var body: some View {
            RatingView(
                currentRating: 3,
                width: 20,
                color: .red,
                sfSymbol: "heart"
            )
        }
    }
    return PreviewWrapper()
}

Итак, мой первый вопрос: могу ли я сделать это в iOS 17, и если да, то как? И во-вторых, мне интересно узнать, в чем разница между этими тремя подходами к аргументам Init?

mutableRating: Binding<Int?>
mutableRating: Binding<Int>?
mutableRating: (Binding<Int>)?

Обновлено: Разъяснение ошибок Когда я использую mutableRating: Binding<Int?> & self._mutableRating = mutableRating Я получаю Missing argument for parameter 'mutableRating' in call в предварительном просмотре non mutating. Я предполагаю, что этот параметр на самом деле не является необязательным, что имеет смысл, учитывая упомянутые различия. Если я изменю строку аргумента на mutableRating: Binding<Int>? чтобы сделать сам параметр необязательным, я получаю Cannot assign value of type 'Binding<Int>' to type 'Binding<Int?>' on the initialization line. One of the fix options offered suggests that this might work, self._mutableRating = mutableRating ?? nilbut that producesНевозможно присвоить значение типа «Привязка?» набрать 'Binding<Int?>'`, что возвращает нас к изменению аргумента.

Первый — это привязка к необязательному типу, второй — необязательная привязка к типу, а третий — такой же, как второй. Поэтому, если вы используете их в качестве параметров функции, вы можете передать nil двум последним, но первый не может быть нулевым, хотя его связанное значение может быть нулевым.

Joakim Danielson 01.06.2024 13:41

@joakim-danielson У меня было ощущение, что 3 — это просто более подробная версия 2. Так каковы варианты использования первых двух? И как мне инициализировать второй?

Gordon 01.06.2024 13:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
78
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вот мое упрощенное решение совместного использования необязательной привязки к необязательному типу и необязательному свойству.

struct TestView: View {
    var mutableRating: Binding<Int?>?
    let currentRating: Int?

    init(mutableRating: Binding<Int?>?) {
        self.mutableRating = mutableRating
        currentRating = nil
    }

    init(rating: Int?) {
        self.mutableRating = Optional<Binding<Int?>>.none
        currentRating = rating
    }

    var body: some View {
        if let mutableRating {
            TextField("Rating", value: mutableRating, format: .number.precision(.fractionLength(0)))
                .padding()
        } else if let currentRating {
            Text(currentRating.formatted())
        }
    }
}

#Preview {
    return TestView(mutableRating: .constant(42))
}

#Preview ("non mutating") {
    struct PreviewWrapper: View {

        var body: some View {
            VStack {
                TestView(rating: 3)
            }
            .padding()
        }
    }
    return PreviewWrapper()
}

Протестировано на iOS 17.4 и macOS 14.

Но работает ли это без передачи mutableRating? Кажется, именно здесь я упираюсь в стену. Я могу заставить его работать, когда ДЕЙСТВИТЕЛЬНО передаю этот аргумент, но в варианте без мутации я получаю ошибку, из-за которой кажется, что этот параметр на самом деле не является необязательным, как мне нужно.

Gordon 01.06.2024 14:17

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

Joakim Danielson 01.06.2024 15:13

В том то и дело, а я нет. Поэтому мне нужен один параметр, используемый в первом предварительном просмотре, который принимает привязку, а затем другой параметр во втором предварительном просмотре, который принимает простое целое число. И в результате оба должны быть необязательными.

Gordon 01.06.2024 15:16

Хорошо, я пропустил, что вы использовали другое свойство для постоянного значения.

Joakim Danielson 01.06.2024 15:26
Ответ принят как подходящий

С моей точки зрения, я считаю, что вы ищете:

public init(
    ...
    mutableRating: Binding<Int?> = .constant(nil),
    ...
) { }

В приведенном выше коде действительны оба предварительного просмотра mutating и non mutating.

  • Binding<Int?>: структура привязки, которая содержит необязательный<Int> в качестве обернутого значения.
  • Binding<Int>?: необязательный<Binding<Int>>
  • (Привязка<Int>)? равно Binding<Int>?

Ага! Мне нужно будет немного почитать, чтобы понять, что именно делает этот .constant(nil), но я думаю, что это сработает. Теперь я могу заставить все это работать, а затем опубликовать отдельно, чтобы посмотреть, скажет ли кто-нибудь: «Этот код пахнет всякими видами, даже если он работает» :)

Gordon 01.06.2024 14:31

Собственно, с @Binding это правильно делать, особенно когда вы макетируете данные в режиме предварительного просмотра. Вам стоит взглянуть на туториалы по SwiftUI.

sonle 01.06.2024 14:39

Я думаю, что .optional предназначен только для предварительного просмотра.

Joakim Danielson 01.06.2024 15:32

Раньше я использовал его в качестве заполнителя пользовательского интерфейса. У меня было представление с двумя состояниями, первое состояние предназначено только для демонстрации, в котором .constant используется для представления своего рода демонстрационных данных. Другое состояние для реальных данных, полученных из BE.

sonle 01.06.2024 15:50

Другие вопросы по теме