Question

iOS 7.1 includes a new Accessibility setting calls Button Shapes that causes some button text to be automatically underlined. Is there a way to detect this mode, or customize it for individual UIButtons?

(This to allow changing button labels such as a dash or underscore so that when underlined, they don't look like an equals sign, etc.)

Was it helpful?

Solution 3

It looks like you can request the button label's attributes and test to see if it contains an NSUnderlineStyleAttributeName attribute. If you remove the NSUnderlineStyleAttributeName attribute the system will put it right back so it seems the trick is to explicitly set the label's underline attribute to 0. I've added the following to my custom button:

- (void) adjustLabelProperties  // override underline attribute
{
    NSMutableAttributedString   *attributedText = [self.titleLabel.attributedText mutableCopy];

    [attributedText addAttribute: NSUnderlineStyleAttributeName value: @(0) range: NSMakeRange(0, [attributedText length])];
    self.titleLabel.attributedText = attributedText;
}

OTHER TIPS

As of iOS 14, you can use UIAccessibility.buttonShapesEnabled or UIAccessibilityButtonShapesEnabled(), which will be true when the setting is enabled.

Old question, but hopefully this helps someone. There's still no built-in method for checking if Button Shapes is enabled on iOS, so we added this:

#pragma mark - Accessibility

/**
 * There's currently no built-in way to ascertain whether button shapes is enabled.
 * But we want to manually add shapes to certain buttons when it is.
 */

static BOOL _accessibilityButtonShapesEnabled = NO;

+ (BOOL)accessibilityButtonShapesEnabled {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self checkIfButtonShapesEnabled];
    });

    return _accessibilityButtonShapesEnabled;
}

+ (void)checkIfButtonShapesEnabled {
    UIButton *testButton = [[UIButton alloc] init];
    [testButton setTitle:@"Button Shapes" forState:UIControlStateNormal];

    _accessibilityButtonShapesEnabled = (BOOL)[(NSDictionary *)[testButton.titleLabel.attributedText attributesAtIndex:0 effectiveRange:nil] valueForKey:NSUnderlineStyleAttributeName];
}

Because there's also no notification if Button Shapes is disabled/enabled whilst the app is running, we run checkIfButtonShapesEnabled in applicationDidBecomeActive:, and push our own notification if the value has changed. This should work in all cases, because it is not currently possible to add the Button Shapes toggle to the "Accessibility Shortcut".

I know it's an old question but this code works. Tested in iOS 9.3

NSMutableAttributedString *attrStr = [btn.titleLabel.attributedText mutableCopy];
[attrStr enumerateAttributesInRange:NSMakeRange(0, [attrStr length])
                            options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
                         usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop) {
                             NSMutableDictionary *mutableAttributes = [NSMutableDictionary dictionaryWithDictionary:attributes];
                             if([mutableAttributes objectForKey:NSUnderlineStyleAttributeName] != nil) {
                                 //It's enabled for this button
                             }
                         }];

To disable button shapes for a specific button

[btn.titleLabel.attributedText addAttribute: NSUnderlineStyleAttributeName value: @(0) range: NSMakeRange(0, [attributedText length])];

I converted the code from this post to Swift (4.2):

import UIKit

public extension UIAccessibility {

    public static var isButtonShapesEnabled: Bool {
        let button = UIButton()
        button.setTitle("Button Shapes", for: .normal)
        return button.titleLabel?.attributedText?.attribute(NSAttributedString.Key.underlineStyle, at: 0, effectiveRange: nil) != nil
    }

}

Usage:

if UIAccessibility.isButtonShapesEnabled {
    // Apply button shapes style to custom button...
}

Tested and working in iOS 12.

Originally posted at my own question: Click

I had the same problem and I found no official solution. So the only workaround that I found until Apple releases a solution is to render a UIToolbar into an image and check if the button is underlined:

+ (BOOL)isUsesButtonShapes {
    BOOL result = FALSE;

    CGRect rect = CGRectMake(0, 0, 320, 44);
    CGPoint point = CGPointMake(26, 33);

    UIToolbar *toolbar = [[[UIToolbar alloc] initWithFrame:rect] autorelease];
    toolbar.backgroundColor = [UIColor whiteColor];
    toolbar.tintColor = [UIColor darkGrayColor];
    toolbar.barTintColor = [UIColor whiteColor];
    [toolbar setItems:@[[[[UIBarButtonItem alloc] initWithTitle:@"Test" style:UIBarButtonItemStyleBordered target:nil action:nil] autorelease]]];
    toolbar.barStyle = UIBarStyleDefault;
    toolbar.translucent = FALSE;

    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    [toolbar.layer renderInContext:context];

    int bpr = CGBitmapContextGetBytesPerRow(context);
    unsigned char *data = CGBitmapContextGetData(context);
    if (data != NULL) {
        int offset = (int) (bpr * point.y + 4 * point.x);
        int blue = data[offset + 0];

        result = blue < 250;
    }

    UIGraphicsEndImageContext();

    return result;
}

It basically just renders the UIToolbar into an image:

Rendered UIToolbar

Then it checks if there is an underline in the pixel under the "T". I know that this can easily break if Apple changes the way how the UIToolbar is rendered. But maybe this method can be improved and is at least better than nothing? Sorry, it isn't a good solution but I didn't find anything better yet.

This is only semi related, but I kind of "roll my own" for Button shapes and make the option available to the user via a settings menu.

I apologize for not being "spot on" regarding the question, but this is what I ended up with by thinking about the same question.

(The example is set up to always use a semi-circle for rounded corners regardless the size - please modify as you wish).

-(void)setBorderForButton:(UIButton*)theButton withSetting:(BOOL)theSetting{
    if (theSetting == YES){
        theButton.layer.cornerRadius = theButton.frame.size.height/2;
        theButton.layer.borderWidth = 1;
        theButton.layer.borderColor = [UIColor yourDesiredColor].CGColor;
        theButton.clipsToBounds = YES;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top