Adicionando digitação personalizada dinâmica ao seu projeto iOS — Nimble

Dec 02 2022
Introdução Dynamic Type é um recurso de acessibilidade no iOS e macOS. O recurso permite que o usuário defina uma escala de texto que é aplicada a todo o dispositivo, atualizada automaticamente.

Introdução

Dynamic Typeé um recurso de acessibilidade no iOS e macOS. O recurso permite que o usuário defina uma escala de texto que é aplicada a todo o dispositivo, atualizada automaticamente. A escala pode ser maior do que o padrão para ajudar usuários com baixa capacidade de visão ou menor para preencher a tela com mais dados de uma só vez.

A fonte padrão do UIKit e SwiftUI inclui esse recurso internamente, tudo o que o desenvolvedor precisa fazer é definir a fonte no rótulo com um estilo de fonte.

textLabel.font = .preferredFont(forTextStyle: .body)

Adicionando fonte personalizada

Comece escolhendo uma fonte para usar no projeto. É aconselhável usar uma fonte com vários pesos para transmitir diferentes significados no aplicativo.

As fontes podem ser baixadas de uma plataforma gratuita, como o Google Fonts .

Arraste os arquivos de fonte para o projeto Xcode. A melhor prática é criar uma pasta Fonte uma subpasta para cada família de fontes.

Abra o alvo principal Info.pliste adicione a chave UIAppFontscomo uma matriz. Dentro da matriz, adicione o nome do arquivo e a extensão de cada uma das fontes incluídas.

Os arquivos de fonte agora são importados para o projeto e podem ser usados ​​criando uma instância de UIFont.

textLabel.font = UIFont(name: "Font-Name", size: UIFont.labelFontSize)

for family in UIFont.familyNames.sorted() {
    let names = UIFont.fontNames(forFamilyName: family)
    print("Family: \(family) Font names: \(names)")
}

// Family: Zen Old Mincho Font names: ["ZenOldMincho-Regular", "ZenOldMincho-Bold"]

A maneira mais fácil de usar uma fonte personalizada no projeto Swift é atribuir a fonte diretamente ao rótulo.

textLabel.font = UIFont(name: "ZenOldMincho-Regular", size: UIFont.labelFontSize)

Ajustando o tamanho do texto no iOS

Configurações de acessibilidade

Inicie o aplicativo Configurações no dispositivo ou em um simulador. Navegue até Accessibility > Display & Text Size > Larger Text. O controle deslizante alterará o tamanho da fonte do sistema.

Alterar o tamanho do texto não iniciará nenhum aplicativo, mas o layout será refeito.

⚙️ Isso é para iOS 15, outras versões do iOS terão uma pequena diferença, mas a opção deve ser pesquisada com Text Sizeou Larger Text.

Substituições do ambiente Xcode

No Xcode, depois de executar com sucesso o aplicativo em um simulador, localize o arquivo Debug Area. O Debug Areacontém um botão com o símbolo de alternância. Este botão deve abrir o Environment Overridesmenu do simulador. O tamanho do texto pode ser ajustado com o controle deslizante para Dynamic Type.

Environment Overridessubstituirá as configurações do simulador e retornará as configurações mais recentes no simulador quando desligado.

Reiniciar o aplicativo com a configuração do dispositivo alterada deve mostrar o novo texto na nova escala. No entanto, não é possível alterar o tamanho da fonte durante a execução do aplicativo. O que falta é uma ferramenta de dimensionamento. Modifique o código para definir o texto do rótulo da seguinte forma:

let customFont = UIFont(name: "ZenOldMincho-Regular", size: UIFont.labelFontSize)!
label.font = UIFontMetrics.default.scaledFont(for: customFont)
label.adjustsFontForContentSizeCategory = true

Para alavancar UIFontMetricsa capacidade, apresentamos um auxiliar:

func customFont(
    _ font: DynamicFont,
    forTextStyle style: UIFont.TextStyle,
    overrideFontSize: UIContentSizeCategory? = nil
) -> UIFont?

Além de adicionar outra camada ao UIFontMetrics, o helper serve como gateway para as funcionalidades que o aplicativo pode fazer. A lista de requisitos inclui:

  • Todas as fontes personalizadas podem ser usadas em uma função.
  • Os tamanhos de texto são distinguíveis por seu estilo de texto (ou seja: corpo, título, legenda, etc.).
  • O tamanho do texto pode ser substituído pelas configurações do aplicativo.

Primeiramente, criamos um protocolo para escolha da fonte: DynamicFont.

protocol DynamicFont {
    func fontName() -> String
    func fontSize(style: UIFont.TextStyle) -> CGFloat
}

O exemplo de DynamicFontnossa demonstração é o seguinte.

enum ZenOldMincho: DynamicFont {
    case regular
    case bold

    func fontName() -> String {
        switch self {
        case .regular: return "ZenOldMincho-Regular"
        case .bold: return "ZenOldMincho-Bold"
        }
    }

    func fontSize(style: UIFont.TextStyle) -> CGFloat {
        switch style {
        case .largeTitle: return 34.0
        case .title1: return 28.0
        case .title2: return 22.0
        case .title3: return 20.0
        case .headline: return 18.0
        case .body: return 17.0
        case .callout: return 16.0
        case .subheadline: return 15.0
        case .footnote: return 13.0
        case .caption1: return 12.0
        case .caption2: return 11.0
        default: return 17.0
        }
    }
}

extension UIFont {
    static func customFont(
        _ font: DynamicFont,
        forTextStyle style: UIFont.TextStyle,
        overrideFontSize: UIContentSizeCategory? = nil
    ) -> UIFont? {
        guard let customFont = UIFont(name: font.fontName(), size: font.fontSize(style: style)) else { return nil }
        let scaledFont: UIFont
        let metrics = UIFontMetrics(forTextStyle: style)
        return scaledFont = metrics.scaledFont(
            for: customFont, compatibleWith: UITraitCollection(
                preferredContentSizeCategory: overrideFontSize ?? .unspecified
            )
        )
    }
}

textLabel.font = .customFont(ZenOldMincho.regular, forTextStyle: .body)
overridedLabel.font = .customFont(ZenOldMincho.bold, forTextStyle: .headline, overrideFontSize: .small)

A versão acima do helper pode ser usada imediatamente no iOS 11. UILabelDeve ter adjustsFontForContentSizeCategoryconfigurado truepara permitir atualizações automáticas quando o sistema altera o tamanho do texto.

label.adjustsFontForContentSizeCategory = true
label.font = .customFont(ZenOldMincho.regular, forTextStyle: .body)

label.adjustsFontForContentSizeCategory = false
label.font = .customFont(ZenOldMincho.regular, forTextStyle: .body, overrideFontSize: .small)

iOS 10

UIFontMetrics.scaledFontestá disponível apenas no iOS 11 e posterior. Para iOS 10, a escala da fonte precisará ser fornecida manualmente para cada estilo de texto e categoria de tamanho, bem como atualizar a fonte quando o tamanho do texto do sistema mudar.

Escala de fonte

Introduzir um novo protocolo:

protocol DynamicFontIOS10 {
    func font(for style: UIFont.TextStyle, sizeCategory: UIContentSizeCategory) -> UIFont?
}

extension ZenOldMincho: DynamicFontIOS10 {
    func font(for style: UIFont.TextStyle, sizeCategory: UIContentSizeCategory) -> UIFont? {
        guard let style = ZenOldMincho.fontSizeTable[style],
              let size = style[sizeCategory]
        else { return nil }
        return UIFont(name: fontName(), size: size)
    }
}

extension ZenOldMincho {
    static let fontSizeTable: [UIFont.TextStyle: [UIContentSizeCategory: CGFloat]] = [
        .headline: [
            .accessibilityExtraExtraExtraLarge: 23.0,
            .accessibilityExtraExtraLarge: 23.0,
            .accessibilityExtraLarge: 23.0,
            .accessibilityLarge: 23.0,
            .accessibilityMedium: 23.0,
            .extraExtraExtraLarge: 23.0,
            .extraExtraLarge: 21.0,
            .extraLarge: 19.0,
            .large: 17.0,
            .medium: 16.0,
            .small: 15.0,
            .extraSmall: 14.0
        ],
        .body: [
            .accessibilityExtraExtraExtraLarge: 53.0,
            .accessibilityExtraExtraLarge: 47.0,
            .accessibilityExtraLarge: 40.0,
            .accessibilityLarge: 33.0,
            .accessibilityMedium: 28.0,
            .extraExtraExtraLarge: 23.0,
            .extraExtraLarge: 21.0,
            .extraLarge: 19.0,
            .large: 17.0,
            .medium: 16.0,
            .small: 15.0,
            .extraSmall: 14.0
        ]
    // Fill with all text style
}

A função customFontprecisará ser modificada para acomodar as escalas do iOS 10.

static func customFont(
    _ font: DynamicFont,
    forTextStyle style: UIFont.TextStyle,
    overrideFontSize: UIContentSizeCategory? = nil
) -> UIFont? {
    guard let customFont = UIFont(name: font.fontName(), size: font.fontSize(style: style)) else { return nil }
    let scaledFont: UIFont
    if #available(iOS 11.0, *) {
        let metrics = UIFontMetrics(forTextStyle: style)
        scaledFont = metrics.scaledFont(
            for: customFont, compatibleWith: UITraitCollection(
                preferredContentSizeCategory: overrideFontSize ?? .unspecified
            )
        )
    } else {
        let sizeCategory = overrideFontSize ?? UIApplication.shared.preferredContentSizeCategory
        guard let fontIOS10 = font as? DynamicFontIOS10,
              let customFontIOS10 = fontIOS10.font(for: style, sizeCategory: sizeCategory)
        else { return customFont }
        scaledFont = customFontIOS10
    }
    return scaledFont
}

Atualizar automaticamente o tamanho do texto

Atualmente, o aplicativo mostrará a fonte correta somente quando for carregado pela primeira vez. A página precisará definir a fonte para cada componente sempre que o tamanho do texto for alterado. Isso pode ser feito ouvindo a NotificationCenternotificação do UIContentSizeCategory.didChangeNotification.

protocol DynamicFontController {

    func setUpContentSizeNotification(disposeBag: DisposeBag)
    func updateFonts(notification: Notification)
}

extension DynamicFontController where Self: UIViewController {

    func setUpContentSizeNotification(disposeBag: DisposeBag) {
        NotificationCenter.default.rx.notification(UIContentSizeCategory.didChangeNotification, object: nil)
            .withUnretained(self)
            .take(until: rx.deallocated)
            .subscribe { owner, value in
                owner.updateFonts(notification: value)
            }
            .disposed(by: disposeBag)
    }
}

Para quem não RxSwift, não esqueça de ligar NotificationCenter.removeObserverno deinit.

O tamanho da fonte será atualizado automaticamente à medida que o tamanho do texto do sistema for alterado.

SwiftUI

SwiftUI tem um modificador de visualização especificamente para modificar a fonte.

.font(.custom(name, size: size))

Text("Hello").font(.custom("ZenOldMincho-Regular", size: 16.0))

Atualizando tamanho da fonte

Para permitir a atualização do tamanho da fonte, um novo modificador de visualização será usado para dar conta da mudança no tamanho do texto do sistema.

Se o projeto for puramente SwiftUI, a declaração existente de customFont(_ font: DynamicFont, forTextStyle style: UIFont.TextStyle, overrideFontSize: UIContentSizeCategory? = nil) -> UIFont?pode ser ignorada.

A declaração de um método SwiftUI equivalente é:

extension View {
    func scaledFont(
        font: DynamicFont,
        forTextStyle style: UIFont.TextStyle,
        overrideFontSize: ContentSizeCategory? = nil
    ) -> some View {
        return modifier(
            ScaledFont(
                name: font.fontName(),
                size: font.fontSize(style: style),
                overrideFontSize: overrideFontSize
            )
        )
    }
}

A declaração de ScaledFonté:

struct ScaledFont: ViewModifier {
    @Environment(\.sizeCategory) var sizeCategory
    var name: String
    var size: CGFloat
    var overrideFontSize: ContentSizeCategory?
    func body(content: Content) -> some View {
      let scaledSize = UIFontMetrics.default.scaledValue(for: size)
      return content.font(.custom(name, size: scaledSize))
    }
}

Com o modificador, scaledFontpodemos alterar a fonte em qualquer exibição com uma assinatura semelhante. A exibição resultante atualizará o tamanho da fonte à medida que as configurações do sistema forem alteradas.

Text("Hello")
  .scaledFont(font: ZenOldMincho.bold, forTextStyle: .headline)

A implementação atual de ScaledFont: ViewModifiernão inclui a substituição do tamanho da fonte no aplicativo.

Para permitir a substituição, UIFontMetrics.scaledValue(for:, compatibleWith:)será usado. Isso é o mesmo na UIKitimplementação; no entanto, a conversão de ContentSizeCategorypara UIContentSizeCategory, para satisfazer scaledValuea entrada de, só é possível para iOS 14 e superior.

Modifique o código para ScaledFont.body:

func body(content: Content) -> some View {
    let scaledSize = UIFontMetrics.default.scaledValue(for: size, compatibleWith: UITraitCollection(
        preferredContentSizeCategory: UIContentSizeCategory(overrideFontSize)
    ))
    return content.font(.custom(name, size: scaledSize))
}

Usando o modificador scaledFont, agora podemos substituir o tamanho do texto.

Text("Hello")
  .scaledFont(font: ZenOldMincho.bold, forTextStyle: .headline, overrideFontSize: .extraLarge)

Swift está em constante evolução para permitir fácil implementação de acessibilidade para melhorar a qualidade de vida do usuário. As funções auxiliares apresentadas neste blog podem ser convenientemente integradas em projetos novos e existentes.

A fonte dinâmica permitirá que os usuários avançados vejam mais dados rapidamente, além de ajudar os usuários com problemas de visão a usar o aplicativo sem problemas. Fonte dinâmica é um dos muitos recursos de acessibilidade que os desenvolvedores podem adotar para melhorar seus aplicativos.

O código-fonte deste projeto está disponível em nosso repositório .

Referências

Dimensionando fontes personalizadas automaticamente com o Dynamic Type Como usar o Dynamic Type com uma fonte personalizada Prático Dynamic Type

Originalmente publicado em https://nimblehq.co .