UILabel with pattern image textColor can't be made accessible

Hi there,

I wanted to make a UILabel with a gradient as its text color, and its fairly easy doing it like this:

let gradientImage = ... // Create a Gradient image using CAGradientLayer
label.textColor = UIColor(patternImage: gradientImage)

Problem is that as soon as I set this pattern image color, the UILabel is not accessible anymore. So isAccessibilityElement is set to false and I have no way of changing this back. The issue doesn't lie in the gradientImage, as using any other image as the patternImage for the textColor results in the same issue. Did anyone else experience this before and knows a way around this?

Thanks, Klemens

Replies

Could you show the complete code for the label, so that we can test also ?

Does it occur only with gradient or with any color ?

This may help as well:

https://stackoverflow.com/questions/54187062/how-to-programmatically-set-accessibilitylabel-on-uilabel

I found this in API doc ; did not test but wondered (may be totally out of purpose) if setting the color as a pattern could make label behave as container ?

… a view that merely serves as a container for other items that you want to be accessible. Implement the UIAccessibilityContainer protocol and set this property to false.

isAccessibilityElement

The default value for this property is false unless the element is a standard UIKit control, in which case, the value is true.  Assistive apps can get information only about objects that accessibility elements represent. Therefore, if you implement a custom control or view that you want to be accessible to users with disabilities, set this property to true. The only exception to this practice is a view that merely serves as a container for other items that you want to be accessible. Implement the UIAccessibilityContainer protocol and set this property to false.

Hi, thanks for the answer!

Its just a standard UILabel where I did override the layoutSubviews function to generate the gradient for the views frame.

It does occur with any patternImage UIColor, not only with the gradient. So for testing, you can even use a SFSymbol as the image. Here is everything you need:

class BGStyledLabel : UILabel {
    override func layoutSubviews() {
        super.layoutSubviews()
        self.textColor = UIColor(patternImage: UIImage.init(systemName: "folder.fill")!) // This will break VoiceOver for this label
        // self.textColor = UIColor.white // VoiceOver would work just fine if this is all you do
    }
}

Checked the accessibilityContainerType property, which always stays on none.

Can you reproduce that?

Best, Klemens

Here is what I used which doesn't require images directly:


class GradientLabel: UILabel {
   
  var gradientColors: [CGColor] = []
  var gradientLocations: [CGFloat] = []
   
  override func drawText(in rect: CGRect) {
     
    self.textColor = drawGradientColor(in: rect, colors: gradientColors, locations: gradientLocations)
     
    super.drawText(in: rect)
  }

  private func drawGradientColor(in rect: CGRect, colors: [CGColor], locations: [CGFloat]) -> UIColor? {
    let currentContext = UIGraphicsGetCurrentContext()
    currentContext?.saveGState()
    defer { currentContext?.restoreGState() }

    let size = rect.size
    UIGraphicsBeginImageContextWithOptions(size, false, 0)
    guard let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(),
                    colors: colors as CFArray,
                    locations: locations) else { return nil }
     
    let context = UIGraphicsGetCurrentContext()
    context?.drawLinearGradient(gradient,
                  start: CGPoint(x: 0.0, y: 0.0),
                  end: CGPoint(x: size.width, y: 0.0),
                  options: [])
    let gradientImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    guard let image = gradientImage else { return nil }
    return UIColor(patternImage: image)
  }
}

After implementing you set the gradientColors and gradientLocations properties. You can change the gradient direction by setting "start:" and "end:" in context?.drawLinearGradient