Странное поведение степпера в SwiftUI

Я хочу использовать представление Stepper в ручном (без привязки) режиме, используя onIncrement и onDecrement. Странное поведение, когда я пытаюсь реализовать нижнюю и верхнюю границы, например. возрастное значение не ниже 1 или выше 10.

Если вы попробуете следующий код, вы можете нажать «-» два раза после того, как он уже имеет значение «1». Он не становится ниже 1, как ожидалось, но после двух дополнительных нажатий кнопка «-» внезапно отключается. Если затем нажать «+», ничего не произойдет. Только после 2 дополнительных нажатий на «+» счетчик реагирует ожидаемым образом и переходит на 2.

Это ошибка?

struct StepperTest: View {
    @State private var age = 5

    var body: some View {
        VStack {
            Stepper("Enter your age", onIncrement: {
                if self.age < 10 {
                    self.age += 1
                    print("Adding to age")
                }
            }, onDecrement: {
                guard self.age > 1 else { return }
                self.age -= 1
                print("Subtracting from age")
            })

            Text("Your age is \(age)")
        }
    }
}

person G. Marc    schedule 20.11.2019    source источник
comment
Почему вы не хотите использовать для этого привязку?   -  person Mojtaba Hosseini    schedule 21.11.2019
comment
@MojtabaHosseini: Потому что я хочу иметь полный контроль   -  person G. Marc    schedule 21.11.2019
comment
полный контроль над чем? у вас уже есть доступ к editingChange, и вы можете обнаружить прикосновение к ключу ведьмы. Что еще тебе нужно?   -  person Mojtaba Hosseini    schedule 21.11.2019
comment
Основная причина в том, что я хочу использовать архитектуру типа Redux, в которой состояние изменяется только действиями в отдельном модуле. Используя привязки, состояние изменяется в различных представлениях.   -  person G. Marc    schedule 24.11.2019
comment
Это называется архитектурой однонаправленного потока данных, и это не противоречит этому.   -  person Mojtaba Hosseini    schedule 24.11.2019
comment
@MojtabaHosseini: Я не совсем понимаю: я бы привязал значение шагового двигателя к переменной состояния? Но тогда состояние изменяется непосредственно представлением, а не одним из моих методов действия, верно?   -  person G. Marc    schedule 25.11.2019


Ответы (3)


Проверка и сброс значения после увеличения, похоже, работают:

Stepper("Enter your age", onIncrement: {
                    self.age += 1
                    if self.age > 10 {self.age = 10}
                }
            }, onDecrement: {
                self.age -= 1
                if self.age < 1 {self.age = 1} 
            })
person Skipjakk    schedule 06.01.2020

Думаю, вот объяснение:

/// onIncrement будет инициализирован равным nil, если попытка увеличить
/// value не будет иметь никакого эффекта. Аналогично, onDecrement будет инициализирован
/// значением nil, если попытка уменьшить value не даст никакого эффекта.

Если вы пытаетесь

Stepper("Enter your age", onIncrement: {
    print("Adding to age")
}, onDecrement: {
    print("Subtracting from age")
})

... вы увидите, что степперы перешли в такое же состояние, как вы описали, поэтому Apple отслеживает перестройку представления Stepper, если никакое перестроение не было инициировано действием пользователя на Stepper (т.е. без эффекта), оно отключается.

Другой вопрос, почему не сразу ...

person Asperi    schedule 20.11.2019
comment
Я не совсем понимаю, как вы могли бы это использовать. Я ожидал, что эти два обработчика onIncrement и onDecrement и два свойства будут включать / отключать кнопки плюс и минус ... - person G. Marc; 24.11.2019
comment
Это вопрос к Apple)), но это задокументированное поведение. - person Asperi; 24.11.2019
comment
Думаю, это ошибка. Я получаю такое же причудливое поведение, даже если у меня нет логики if / else внутри моих onIncrement и onDecrement. Он просто останавливается и затемняет + или - серым. Затем, если вы нажмете другой, пойдет в неправильном направлении. Комментарий грамматически плохой и непонятный. - person Rob N; 10.04.2020

Я видел то же самое. Мне определенно кажется ошибкой на стороне Apple. Как упомянул один из комментаторов, вы можете использовать параметр onEditingChanged. Это должно дать вам все необходимые функции. Поскольку связанная переменная изменяется с входами Stepper, вы можете выполнить действие. Это выглядит так:

Stepper(value: $age, in: 1...10, step: 1, onEditingChanged: { didChange in
        // take some action or make a calculation
}) 
{
        Text("Your age is \(age)")
}
person Francojamesfan    schedule 17.08.2020