What I thought was a bug, turned out to be a feature!
Thanks to Paul Hudson at Hacking With Swift, I recently learned that what I thought was a bug in SwiftUI is actually a feature. Paul says in one of his 100 Days of SwiftUI videos - which I highly highly recommend for anyone wanting to learn Swift and/or SwiftUI - even the most experienced iOS developers are often gobsmacked by this quirk of the code. I knew right away what he was talking about - I had encountered it myself, turned in a feedback report to Apple, and found a work-around. But according to Paul, it's not a bug, it's a feature! He encourages his viewers to spread the word, so that's what I'm doing.
Cheeky Row of Buttons
I encountered the problem, funnily enough, while working on the challenges to one of Paul's earlier 100 Days projects, an app that drills you on your multiplication facts. I was creating controls that would allow the user to choose which numbers to practice. I made a row of buttons, one for each number. To my enormous frustration, I could not select individual numbers. Selecting one number selected them all!
With an appropriately placed print statement I confirmed that yes, what was happening was exactly what it looked like: pressing one button made every button in the row fire.
I searched online for a solution and found nothing helpful. ChatGPT was at a loss. I even showed my code to the good folks at A Flock of Swifts, and they all agreed that it must be a bug in SwiftUI.
My Work-Around
With some sleuthing I figured out that the buttons only behaved badly when they were inside a ForEach
inside an HStack
, inside a Form
. I had designed custom buttons, so I thought it might have to do with the way I built them. I started messing with the pre-formatted button styles and discovered that if I added a .buttonStyle(.plain)
modifier, the problem went away; I could use my own styling and the buttons behaved independently, like good little buttons should.
Here's the code, showing the problematic situation along with my solution:
Form {
HStack {
ForEach(0..<7) {number in
NumberButton(number)
.buttonStyle(.plain)
}
}
}
It's a Feature!?
Imagine my surprise a few weeks later, when I was working through my 100Days lesson for the day, and Paul Hudson himself put a row of buttons into a form. My ears perked up when he said, "Please try it now - Very likely this will not work the way you hoped." Sure enough, all the buttons acted as one.
This is because of the way Form is designed to behave. If you put a single button in a Form row, by default, you don't have to tap the button itself to activate it; you just need to tap anywhere in the row. That means every button responds when you tap anywhere in the row. To turn this "feature" off, you can modify your buttons with a .plain
button style.
Well, now I know the logic behind my "workaround"!
I'd Love To See Your Comment
I know I'm not the only one - have you encountered this? Did you report it to Apple like I did? Have you found any other weirdness in the iOS ecosystem? What's your favorite bug that turned out to be a feature?
Links
If you're interested in exploring further...
Here's my Multiplication Tables app on GitHub.
Here's an app I made to demonstrate the "problem."
Here's Paul's video where he explains what's going on.
Happy buttoning!
Top comments (0)