iosswiftaccessibilityuisearchbardynamic-type-feature

Add dynamic type to a UISearchBar


My question is pretty much stated in the title. I'm trying to add dynamic type (as defined here) to a UISearchBar with no luck. I know this is possible as the system apps seem to be able to handle it just fine as shown here: enter image description here

However, my app doesn't seem to be handling that so well as shown here:

enter image description here

Knowing that UITextField is contained within UISearchBar I naturally tried this solution without success: UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).adjustsFontForContentSizeCategory = true

I've also tried searching online/checking documentation but I can't seem to find a solution anywhere. Is there something I'm missing to get dynamic type working in a UISearchBar.

Update: @matt suggested I do a manual check and update the font that way. However, that is yielding another issue as the search bar itself is too small to fit the text as shown here: enter image description here

@matt suggested to update the height as well using the scaledValue(for:) method, however this doesn't seem to work. Here's the code I'm using:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)
        UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).font = UIFont.preferredFont(forTextStyle: .body)
        let textFieldFrame = UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).frame
        UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).frame = CGRect(x: textFieldFrame.minX, y: textFieldFrame.minY, width: textFieldFrame.width, height: UIFontMetrics.default.scaledValue(for: textFieldFrame.height))
}

The font seems to now be scaling with this updated code, yet the search bar's height isn't growing:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    searchBar.textField?.font = UIFont.preferredFont(forTextStyle: .body)
    if let textFieldFrame = searchBar.textField?.frame {
        searchBar.textField?.frame = CGRect(x: textFieldFrame.minX, y: textFieldFrame.minY, width: textFieldFrame.width, height: UIFontMetrics.default.scaledValue(for: textFieldFrame.height))
    }
}

Also, here's how I found the textField (just in case other users who get stuck would like to know):

extension UISearchBar {
    var textField: UITextField? {
        var _textField: UITextField? = nil
        subviews.forEach {
            $0.subviews.forEach {
                if let textField = $0 as? UITextField {
                    _textField = textField
                }
            }
        }
        return _textField
    }
}

Solution

  • I have followed these milestones to reach your goal:

    I used the code snippet provided in your post to get the searchbar textfield:

    extension UISearchBar {
    
        var textField: UITextField? {
    
            var _textField: UITextField? = nil
    
            subviews.forEach {
                $0.subviews.forEach {
                    if let textField = $0 as? UITextField {
                        _textField = textField
                    }
                }
            }
            return _textField
        }
    }
    

    However, you can also get it using the key searchField as follows:

    let textField = searchBar.value(forKey: "searchField") as? UITextField
    

    The snapshots hereunder show the final result:

    enter image description here

    You can now add dynamic type to a UISearchBar by adapting the code snippet above and customizing the visual personal choices (text style, font, margins...).