Search code examples
swiftuisegmentedcontrol

Why is using UIControlState.normal on a segmented control causing this crash?


I have a simple UISegmentedControl with two segments and I'm trying to customize its appearance.

I'm getting this runtime error:

*** Terminating app due to uncaught exception >'NSInvalidArgumentException', reason: '-[NSNull renderingMode]: unrecognized selector sent to instance 0x112156f08'

@IBOutlet weak var sortController: UISegmentedControl!

override func viewDidLoad() {
    super.viewDidLoad()

    styleSortController()

    // other setup code

}

func styleSortController() {
    let titleAttributes: [String: Any] = [NSForegroundColorAttributeName: UIColor.white, NSFontAttributeName: UIFont(name: "AvenirNext-Demi", size: CGFloat(14.0)) as Any]
    let selectedTitleAttributes: [String: Any] = [NSForegroundColorAttributeName: UIColor.white,
                                                  NSFontAttributeName: UIFont(name: "AvenirNext-DemiBold", size: CGFloat(14.0)) as Any]

    // this line causes it to crash
    sortController.setTitleTextAttributes(titleAttributes, for: UIControlState.normal)

    // but this line works, no problem
    sortController.setTitleTextAttributes(selectedTitleAttributes, for: UIControlState.selected)
}

caused by the line seen above where I first try to set a title text attribute (note that the second line setting an attribute works just fine).

According to Apple's documentation, I should be able to use any valid UIControlState and UIControlState.normal sure seems valid so I'm really confused about why this is happening.

What could this line be causing me trouble?


Solution

  • let titleAttributes: [String: Any] = [NSForegroundColorAttributeName: UIColor.white, 
                                          NSFontAttributeName: UIFont(name: "AvenirNext-Demi", size: CGFloat(14.0)) as Any]
    let selectedTitleAttributes: [String: Any] = [NSForegroundColorAttributeName: UIColor.white,
                                                  NSFontAttributeName: UIFont(name: "AvenirNext-DemiBold", size: CGFloat(14.0)) as Any]
    

    Before saying it's the UIControlState issue, you need to do:

    sortController.setTitleTextAttributes(selectedTitleAttributes, for: UIControlState.normal)
    

    In other words, use the same working sample (the one for .selected)

    It should create the same error crash.

    Then you'll find that the culprit is UIFont(name: "AvenirNext-Demi", size: CGFloat(14.0)), it returns nil.

    So the font name is not a valid one.

    You can iterate through your Font to list it (see this related question) and find the correct one. It's a "usual one", so you can open Font Book.app, find it, and check the Post script name of it. Not all font in macOS are in iOS, but that's the case for that one.

    enter image description here

    You'll get: AvenirNext-Regular, AvenirNext-Medium, AvenirNext-UltraLight, AvenirNext-Italic, AvenirNext-MediumItalic, AvenirNext-UltraLightItalic, AvenirNext-DemiBold, AvenirNext-Bold, AvenirNext-Heavy, AvenirNext-DemiBoldItalic, AvenirNext-BoldItalic, AvenirNext-HeavyItalic.

    There is no AvenirNext-Demi.

    So use the one you think fit more your need.

    I recommend if you use custom font to install them in your Mac. It's easy to checkout the postscript name of the font that way.