8

I want to use the Stepper view in manual (not binding) mode using onIncrement and onDecrement. There's a strange behavior when I try to implement lower and upper bounds, eg. having an age value not going bellow 1 or above 10.

If you try the bellow code, you can press "-" two times after it already has the value "1". It doesn't go bellow 1 as expected, but after the two additional presses the "-" button suddenly gets disabled. If you then press "+" nothing happens. Only after 2 additional presses to "+" the counter reacts as expected and goes to 2.

Is this a bug?

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)")
        }
    }
}
6
  • Why don't you want to use binding for this? Commented Nov 20, 2019 at 22:04
  • @MojtabaHosseini: Because I want to have full control Commented Nov 21, 2019 at 12:58
  • full control over what? you have the access to the editingChange already and you can detect witch key is touched. What else do you need? Commented Nov 21, 2019 at 14:08
  • The main reason is, I want to use a Redux like architecture where the state is only changed by actions in a separate module. Using bindings, the state is modified within various views. Commented Nov 24, 2019 at 8:24
  • That is called Uni Directional Data Flow architecture and that has no conflict with this. Commented Nov 24, 2019 at 8:45

4 Answers 4

2

Checking and resetting the value after incrementing seems to work:

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} 
            })
Sign up to request clarification or add additional context in comments.

1 Comment

Sorry, but I see no change in behaviour. It is still buggy.
1

I think here is explanation:

/// onIncrement will be initialized to nil if attempting to increment
/// value will have no effect. Likewise, onDecrement will be initialized
/// to nil if attempting to decrement value will have no effect.

If you try

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

... you'll see that steppers got into the same state, as you described, so Apple tracks rebuild of Stepper view, if no rebuild was initiated by user action on Stepper (ie. no effect) it disable itself.

Another question is why it's not at once...

3 Comments

I don't fully understand on how you would use it that way. I'd expect to have those two handlers onIncrement and onDecrement and two properties to enable/disable the plus and minus buttons...
That's a question to Apple )), but it is a documented behavior.
I think it's a bug. I get the same kind of bizarro behavior, even if I don't have any if/else logic inside my onIncrement and onDecrement. It just stops and grays out the "+" or "-". Then if you tap the other one in goes in the wrong direction. The comment is grammatically bad and incomprehensible.
1

I was seeing the same thing. Definitely seems like a bug on Apples side to me. As one of the commenters mentioned, you can use the onEditingChanged parameter. This should give you all the functionality that you would need. As the bound variable changes with the Stepper inputs you are able to take an action. It looks like this:

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

Comments

1

I was seeing similar behavior as described using iOS 14.5 in Xcode 12.5.1. I found this open radar which describes the bug: https://openradar.appspot.com/FB7669491

In my case I was passing functions in my view model directly as the onIncrement and onDecrement parameters when initializing the Stepper:

Stepper("Label", onIncrement: viewModel.increment, onDecrement: viewModel.decrement)

Based on reading the radar, the current issue is caused by passing functions outside the view as the values for these parameters. Changing my code to this fixed the issue for me:

Stepper("Label", onIncrement: { viewModel.increment }, onDecrement: { viewModel.decrement })

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.