Material-components-ios: [Buttons] MDCButton does not uppercase titles when loaded from a Xib

Created on 28 Nov 2017  路  5Comments  路  Source: material-components/material-components-ios

Overview

In the MDC Catalog, in the "Buttons (Swift and Storyboard)" example, the storyboard buttons do not have uppercase titles while the "Swift" versions do.

Reproduction steps

Create an MDCButton through Interface Builder.

Version number

40.0.3

Operating system and device

Any/All


Internal data

[Buttons] Bug

Most helpful comment

how do you get rid of the upper casing ?

All 5 comments

This may be working as intended - I believe the buttons might have to enable uppercasing.

how do you get rid of the upper casing ?

You have to set uppercaseTitle to false when the view is loaded in your VC. You could also do it in a the initializer if you use a custom view class in your Xib.

- (void)viewDidLoad {
  [super viewDidLoad];
  button.uppercaseTitle = NO;
}

I don't believe this issue is Working as Intended. Although Material Buttons can use "sentence case" titles, the previous requirement was all uppercase strings. However, the problem is not in our defaulting uppercaseTitles to YES, but in how UIButton has its title values initialized via Interface Builder and how MDCButton stores its own title strings.

When a client creates such a button via Interface Builder and sets the titles with lowercase strings, the created MDCButton does not have the correct default casing. This behavior conflicts with that of programmatically-created MDCButton instances using the same strings. Even if the client "toggles" the uppercasing behavior, it has no effect because the internal storage of the title strings in MDCButton is completely empty. There is currently no way for clients to use Interface Builder's title configuration properties with an MDCButton.

Root cause and possible solution

My testing/debugging leads me to believe that the titleForState property is set "magically" via either the superclass (UIButton) constructor or some KVC awesomeness. There are no calls to setTitleForState: in MDCButton during the Xib's initialization. It appears that the only way around this issue (where title strings are set in Interface Builder) is to extract them manually from the superclass. Something like the code below would work for IB but would require some unit tests around it, including one that loads a test Xib and compares UIButton to MDCButton behavior.

- (void)updateTitleCase {
  NSDictionary<NSNumber *, NSString *> *nontransformedTitles = [_nontransformedTitles copy];

  // If there are no titles set, but the superclass has titles (e.g., using Interface Builder), retrieve the
  // titles of the 4 control states supported by Interface Builder.
  if (nontransformedTitles.count == 0U) {
    NSString *defaultTitle = [super titleForState:UIControlStateNormal];
    _nontransformedTitles[@(UIControlStateNormal)] = defaultTitle;
    NSString *highlightedTitle = [super titleForState:UIControlStateHighlighted];

    // Assume fall-back behavior is being used for states that are not `.normal`
    if (![highlightedTitle isEqualToString:defaultTitle]) {
      _nontransformedTitles[@(UIControlStateHighlighted)] = highlightedTitle;
    }
    NSString *selectedTitle = [super titleForState:UIControlStateSelected];
    if (![highlightedTitle isEqualToString:defaultTitle]) {
      _nontransformedTitles[@(UIControlStateSelected)] = selectedTitle;
    }
    NSString *disabledTitle = [super titleForState:UIControlStateDisabled];
    if (![disabledTitle isEqualToString:defaultTitle]) {
      _nontransformedTitles[@(UIControlStateDisabled)] = disabledTitle;
    }
    nontransformedTitles = [_nontransformedTitles copy];
  }

  // This calls setTitle or setAttributedTitle for every title value we have stored. In each
  // respective setter the title is upcased if _uppercaseTitle is YES.
  for (NSNumber *key in nontransformedTitles.keyEnumerator) {

This is a little bit of a hack. It specifically assumes that Interface Builder only supports the 4 main control states (as it does in Xcode 9). This is probably a reasonable assumption, and a good unit test (comparing all possible control states in a loop) should be sufficient to catch breaking SDK changes in future releases. Changes to the Interface Builder behavior should be easy enough to work around - looping all possible states and configuring them. We could even use that looping behavior from the start to guard against future changes.

Closing this due to inactivity greater than 6 months. Please feel free to reopen if you believe it should be worked on in the near future.

Was this page helpful?
0 / 5 - 0 ratings