Having problems customizing the UIStepper control for my iOS app.
So far I've been able to customize it to the point where the background and divider are translucent, as well as changed the increment and decrement images using the code below...
func decorate(stepper: UIStepper) {
let greyLeft = UIImage(named: Assets.Button.leftGrey)
let greenLeft = UIImage(named: Assets.Button.leftGreen)
let greyRight = UIImage(named: Assets.Button.rightGrey)
let greenRight = UIImage(named: Assets.Button.rightGreen)
let blank = UIImage(named: Assets.Button.translucent)
let color = UIColor(red: 147/255,
green: 148/255,
blue: 81/255,
alpha: 1.0)
stepper.setDecrementImage(greenLeft, for: .normal)
stepper.setDecrementImage(greyLeft, for: .disabled)
stepper.setIncrementImage(greenRight, for: .normal)
stepper.setIncrementImage(greyRight, for: .disabled)
stepper.setBackgroundImage(blank, for: .normal)
stepper.setDividerImage(blank,
forLeftSegmentState: .normal,
rightSegmentState: .normal)
stepper.tintColor = color
}
I have 3 problems though...
Even though I'm using two different images (same shape, different color) for the increment and decrement images their original colors are being overridden by the tint color. I think I can just cycle the tint color when it changes, but is it possible to defer the tint color so it doesn't override the image? Then I can avoid a solid color.
The images that I've got for the increment and decrement images are too small. Can I adjust the size programmatically? So I don't have to edit the images or go back to my graphic artist for larger ones.
Question #1 is my primary concern, because I think I can deal with #2 and #3 if needs be. However, it would be great if I could at least get confirmation that the answers to those are "NO" if that's the case.
Change this line:
let blank = UIImage(named: Assets.Button.translucent)
to:
let blank = UIImage()
that should get rid of the highlighted separator.
If your images are simple arrows, you could easily generate those via code "on-the-fly" with the desired colors, or use them as tint-able images.
If you load the images like this:
let greyLeft = UIImage(named: Assets.Button.leftGrey)?.withRenderingMode(.alwaysOriginal)
let greenLeft = UIImage(named: Assets.Button.leftGreen)?.withRenderingMode(.alwaysOriginal)
let greyRight = UIImage(named: Assets.Button.rightGrey)?.withRenderingMode(.alwaysOriginal)
let greenRight = UIImage(named: Assets.Button.rightGreen)?.withRenderingMode(.alwaysOriginal)
they shouldn't be affected by the tint color.
Edit
Take a look at these changes:
func decorate(stepper: UIStepper) {
let colorNormal = UIColor(red: 147/255,
green: 148/255,
blue: 81/255,
alpha: 1.0)
let colorHighlighted = UIColor(red: 0.9,
green: 0.0,
blue: 0.0,
alpha: 1.0)
let colorDisabled = UIColor(red: 0.25,
green: 0.25,
blue: 0.25,
alpha: 1.0)
// adjust size to your liking
let fnt = UIFont.systemFont(ofSize: 32)
let configuration = UIImage.SymbolConfiguration(font: fnt)
let lArrow = UIImage(systemName: "arrowtriangle.left.fill", withConfiguration: configuration)?.withRenderingMode(.alwaysOriginal)
let leftNormal = lArrow?.withTintColor(colorNormal)
let leftHighlighted = lArrow?.withTintColor(colorHighlighted)
let leftDisabled = lArrow?.withTintColor(colorDisabled)
let rArrow = UIImage(systemName: "arrowtriangle.right.fill", withConfiguration: configuration)?.withRenderingMode(.alwaysOriginal)
let rightNormal = rArrow?.withTintColor(colorNormal)
let rightHighlighted = rArrow?.withTintColor(colorHighlighted)
let rightDisabled = rArrow?.withTintColor(colorDisabled)
let blank = UIImage()
stepper.setDecrementImage(leftNormal, for: .normal)
stepper.setDecrementImage(leftHighlighted, for: .highlighted)
stepper.setDecrementImage(leftDisabled, for: .disabled)
stepper.setIncrementImage(rightNormal, for: .normal)
stepper.setIncrementImage(rightHighlighted, for: .highlighted)
stepper.setIncrementImage(rightDisabled, for: .disabled)
stepper.setBackgroundImage(blank, for: .normal)
stepper.setDividerImage(blank,
forLeftSegmentState: .normal,
rightSegmentState: .normal)
}
This uses left and right filled triangle SF Symbol
images (adjust the size to your liking), and I've added colors for Highlighted
state (not required).
Edit 2
If you want to add some horizontal space between the dec/inc buttons, we can do so by using a clear image for the "divider" images -- but, Apple's docs are not particularly clear on how to go about it.
To avoid the "weird highlight" between the buttons, we have to set the clear image for 3 "state combinations":
normal / normal
normal / highlighted
highlighted / normal
So, if we wanted, for example, 64-points of horizontal spacing:
// create a clear image of desired width
// (height will be auto-stretched)
let blank = UIGraphicsImageRenderer(size: CGSize(width: 64, height: 8)).image(actions: {c in})
stepper.setBackgroundImage(blank, for: .normal)
// set blank image for normal / normal
stepper.setDividerImage(blank,
forLeftSegmentState: .normal,
rightSegmentState: .normal)
// set blank image for normal / highlighted
stepper.setDividerImage(blank,
forLeftSegmentState: .normal,
rightSegmentState: .highlighted)
// set blank image for highlighted / normal
stepper.setDividerImage(blank,
forLeftSegmentState: .highlighted,
rightSegmentState: .normal)
We have to set at least those 3 state combinations.
Curiously, we can also set images to be used if one (or both) of the buttons is .disabled
... but, if we don't it uses the matching .normal
state.
Note: Using a custom-width divider image compresses the width of the buttons, so this may or may not be useful information.