Découvrez comment les polices fonctionnent à un niveau inférieur et voyez comment tout cela est lié à SwiftUI
Polices Google est le site de référence pour les polices gratuites à utiliser lors de la conception d’interfaces utilisateur. Ce didacticiel vous montrera comment vous pouvez écrire un outil simple pour prévisualiser ces polices sans avoir à enregistrer chaque police avec le système.
L’application contient une vue fractionnée qui contient la liste des polices dans le panneau de gauche. Le panneau de droite affichera un aperçu des options de style de la police.
Configuration du projet
- Créez un nouveau projet Mac SwiftUI nommé
GoogleFontPrevew
- Activer les connexions sortantes (client) dans le bac à sable de l’application
- Obtenez une clé d’API Google à partir du Console de développeur Google.
Modèles de polices Google
L’API Google Fonts répertorie toutes les polices disponibles sur Polices Google. Les polices sont récupérées à l’aide de API Google Fonts. La réponse contient tous les styles et une URL pour accéder au fichier de police des styles. Les métadonnées, telles que le créateur et la description, sont manquantes. Cependant, nous en avons assez pour créer un simple aperçu, ce qui est le but.
{
"kind": "webfonts#webfontList",
"items": [
{
"family": "ABeeZee",
"variants": [
"regular",
"italic"
],
"subsets": [
"latin",
"latin-ext"
],
"version": "v22",
"lastModified": "2022-09-22",
"files": {
"regular": "http://fonts.gstatic.com/s/abeezee/v22/esDR31xSG-6AGleN6tKukbcHCpE.ttf",
"italic": "http://fonts.gstatic.com/s/abeezee/v22/esDT31xSG-6AGleN2tCklZUCGpG-GQ.ttf"
},
"category": "sans-serif",
"kind": "webfonts#webfont"
}
]
}
Modèles de polices Google
Créer un GoogleFont.swift
dossier. Ce fichier contiendra les structures de données que nous utiliserons comme modèles de vue.
Créez un mappage 1:1 du niveau supérieur de la réponse JSON de l’API.
struct GoogleResponse: Decodable {
let kind: String
let items: [GoogleFont]
}
Créez une structure GoogleFont pour représenter une entrée de police. Ce sera ce que le ContentView
utilise pour afficher une liste de polices. Il s’agit principalement d’un mappage 1:1 des éléments de police dans la réponse JSON. Les fichiers seront mappés sur un tableau trié à partir du dictionnaire qui se trouve dans le JSON.
Pour être utilisable, la police doit être Decodable
, Hashable
et Identifiable
. Étant donné que le nom de famille est unique pour chaque entrée, il peut être utilisé comme identifiant unique de l’objet.
struct GoogleFont: Hashable, Identifiable, Decodable {
var id : String { family }let family: String
let files: [GoogleFontStyle]
let version: String
let category: String
}
Le FontFile
est ce qui sera utilisé dans l’affichage des détails pour prévisualiser les styles de la famille de polices. Le style et l’URL de la police seront stockés pour une référence facile. Pour pouvoir utiliser le style dans une liste, il doit être Hashable
et Identifiable
. Un UUID sera généré pour identifier de manière unique le fichier de police.
struct FontFile: Hashable, Identifiable {
let style: Style
let id = UUID()
let url: URL
}
Créer un Style.swift
dossier. C’est là que le Style
enum sera défini. Google définit les pondérations entre 100 et 900. italic
est ajouté à la valeur si le poids a une variante en italique. Cependant, pour normal et normal-italic, Google utilise simplement regular
et italic
respectivement.
L’énumération a une fonction d’assistance pour obtenir un nom convivial du style. Le nom convivial sera utilisé comme titre de la zone de groupe qui enveloppera l’aperçu du style.
enum Style: String, Hashable {
case thin = "100"
case thinItalic = "100italic"
case extraLight = "200"
case extraLightItalic = "200italic"
case light = "300"
case lightItalic = "300italic"
case normal = "regular"
case normalItalic = "italic"
case medium = "500"
case mediumItalic = "500italic"
case semiBold = "600"
case semiBoldItalic = "600italic"
case bold = "700"
case boldItalic = "700italic"
case extraBold = "800"
case extraBoldItalic = "800italic"
case black = "900"
case blackItalic = "900italic"var friendlyName: String {
switch self {
case .thin: return "Thin"
case .thinItalic: return "Thin Italic"
case .extraLight: return "Extra Light"
case .extraLightItalic: return "Extra Light Italic"
case .light: return "Light"
case .lightItalic: return "Light Italic"
case .normal: return "Normal"
case .normalItalic: return "Italic"
case .medium: return "Medium"
case .mediumItalic: return "Medium Italic"
case .semiBold: return "Semi Bold"
case .semiBoldItalic: return "Semi Bold Italic"
case .bold: return "Bold"
case .boldItalic: return "Bold Italic"
case .extraBold: return "Extra Bold"
case .extraBoldItalic: return "Extra Bold Italic"
case .black: return "Black"
case .blackItalic: return "Black Italic"
}
}
}
Maintenant, avec tout cela en place, un décodage personnalisé peut être implémenté dans le GoogleFont
. Ajouter Decodable's
les clés d’initialisation et de codage requises. La bonne chose à propos de Xcode 14 est qu’il en remplira automatiquement une partie pour vous.
struct GoogleFont: Hashable, Decodable, Identifiable {
var id: String { family }let family: String
let files: [FontFile]
let version: String
let category: String
enum CodingKeys: CodingKey {
case family
case files
case version
case category
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.family = try container.decode(String.self, forKey: .family)
let files = try container.decode([String: String].self, forKey: .files)
self.version = try container.decode(String.self, forKey: .version)
self.category = try container.decode(String.self, forKey: .category)
var keys = Array(files.keys)
// lets do a bit of work to get our files that we want.
keys.sort { item1, item2 in
func itemFixer(_ value: String) -> String {
if value == "regular" {
return "400"
} else if value == "italic" {
return "400Italic"
}
return value
}
let fixedItem1 = itemFixer(item1)
let fixedItem2 = itemFixer(item2)
return fixedItem1 < fixedItem2
}
self.files = keys.compactMap { key in
guard let style = Style(rawValue: key),
let location = files[key]?.replacingOccurrences(of: "http", with: "https")
else {
return nil
}
return FontFile(style: style, url: URL(string: location)!)
}
}
}
- Décodez le
family
,version
,category
directement depuis le conteneur de polices. - Décodez le mappage de fichiers du JSON dans une variable temporaire.
- Obtenez les clés et triez-les dans l’ordre du plus clair (100) au plus foncé (900). Il y a une petite fonction locale dans le bloc qui prend
regular
et le mappe à400
et prenditalic
et le mappe à400italic
. Cela permet de trier normal et italique avant le moyen (500) et après la lumière (300). - Une fois les clés triées, utilisez
compactMap
pour mapper créer le triéFontFile
déployer. L’utilisation decompactMap
permet unenil
entrée à ignorer lors de la création du tableau de fichiers. Simap
a été utilisé, unnil
l’entrée serait insérée dans le tableau. - Remplacez http par https afin que l’application ne se plaigne pas d’utiliser des appels Web non sécurisés.
Le service de police Google
Créer un GoogleFontService.swift
dossier. Ce fichier aura une classe qui fait un appel réseau au API Google Fonts pour récupérer la liste des polices affichées dans l’application.
class GoogleFontService {
let apiKey = "** YOUR KEY HERE **"func syncFonts() async throws -> [GoogleFont] {
var components = URLComponents()
components.scheme = "https"
components.host = "www.googleapis.com"
components.path = "/webfonts/v1/webfonts"
components.queryItems = [
URLQueryItem(name: "key", value: apiKey),
URLQueryItem(name: "sort", value: "alpha")
]
let url = components.url!
let request = URLRequest(url: url)
let (data, _) = try await URLSession.shared.data(for: request)
let googleResponse = try JSONDecoder()
.decode(GoogleResponse.self, from: data)
return googleResponse.items
}
}
- Remplace le
apiKey
avec la valeur extraite de la console d’API Google. - Créez l’URL vers l’API à l’aide de URLComponents. Cela garantit que l’URL est correctement formatée.
- Demandez à l’API de trier la liste par ordre alphabétique.
- Appeler le API de police Google
- Renvoie les éléments GoogleFont décodés.
Si aucune valeur n’est renvoyée, assurez-vous que :
- La clé API Google est correcte
- Les connexions sortantes sont activées pour l’application.
Création d’une police Web utilisable
Pour créer une police Web utilisable, un mélange de Core Graphics et de Core Text sera utilisé. Core Graphics est un framework de bas niveau qui gère le dessin d’objets 2D, y compris le texte. Core Text, d’autre part, est le moteur de rendu et de mise en page de texte de bas niveau utilisé par SwiftUI pour rendre le texte et gérer les polices.
SwiftUI Font
a un constructeur qui prend un CTFont
(police Core Text) et renvoie un SwiftUI Font
. La fonction d’enregistrement téléchargera le fichier de police et entrera dans SwiftUI Font
. Créer un Font+Register.swift
fichier pour y mettre le code de l’extension.
extension Font {
static func register(url: URL, size: CGFloat = 18) async throws -> Font? {
do {
let request = URLRequest(url: url)
let (data, _) = try await URLSession.shared.data(for: request)guard let provider = CGDataProvider(data: data as CFData),
let cgFont = CGFont(provider)
else {
print("Unsucessfully registered font")
return nil
}
let ctFont = CTFontCreateWithGraphicsFont(cgFont, size, nil, nil)
return Font(ctFont)
} catch {
print(error)
}
return nil
}
}
- Récupérer les données de la police à partir de l’URL
- Créez un fournisseur de données Core Graphics à partir des données téléchargées. Le code tire parti du fait que la classe Data de Swift peut être reliée à CFData de CoreFoundation.
- Créez une police Core Graphics.
- Créez une police de texte de base.
- Utilisez la police Core Text pour créer une police SwiftUI.
- S’il y a une erreur, imprimez l’erreur et renvoyez
nil
.
Créer une vue principale
Le ContentView.swift
Le fichier contient la vue principale de l’application. La vue principale contiendra une vue fractionnée avec la liste à gauche et les détails à droite. Le ContentView
possède la liste des polices à afficher ainsi que l’état de la sélection en cours.
Le task
modificateur sera appelé pour charger les polices et définir la police sélectionnée initiale.
struct ContentView: View {
@State var fonts = [GoogleFont]()
@State var font: GoogleFont?var body: some View {
NavigationSplitView {
FontList(selectedFont: $font, fonts: fonts)
} detail: {
if let font = font {
DetailsView(font: font)
} else {
EmptyView()
}
}
.task {
do {
fonts = try await GoogleFontService().syncFonts()
await MainActor.run {
font = fonts[0]
}
} catch {
print (error)
}
}
}
}
- Créez une variable d’état pour contenir une liste des
GoogleFont
éléments. - Créez une variable d’état pour contenir l’élément actuellement sélectionné
GoogleFont
- Créez une vue fractionnée de navigation avec deux colonnes.
- Ajouter le
FontList
au premier bloc (côté gauche). Notez que l’état de sélection est lié à laFontList's
sélection. - Si la liste n’est pas vide, définissez les détails sur un
DetailsView
. Sinon, montrez unEmptyView
- Ajouter un
task
modificateur qui récupère les polices et définit la sélection actuelle sur la première police de la liste.
Liste des polices
La liste des polices affiche toutes les polices disponibles et met à jour la police actuellement sélectionnée. Créer un FontList.swift
dossier.
struct FontList: View {
@Binding var selectedFont: GoogleFont?
var fonts: [GoogleFont]var body: some View {
List(fonts, selection: $selectedFont) { font in
NavigationLink(value: font) {
VStack(alignment: .leading) {
Text(font.family)
.font(.system(size: 14, weight: .bold))
Text("\(font.files.count) styles")
.font(.system(size: 14))
}
.padding(8)
}
}
}
}
- Créez une liaison pour mettre à jour la police actuellement sélectionnée.
- Créez une variable pour contenir les polices à afficher.
- Créez une vue de liste pour afficher les polices et mettre à jour la sélection.
- Les cellules seront une simple pile verticale indiquant le nom de la famille de polices et le nombre de styles.
- Ajoutez une petite quantité de rembourrage pour un attrait visuel.
vue détaillée
Le DetailsView
présente un aperçu de chaque style appartenant à une famille de polices. Les styles seront affichés dans une pile verticale paresseuse. Créer un DetailView.swift
struct DetailsView: View {
var font: GoogleFontvar body: some View {
VStack {
Text(font.family)
.font(.system(size: 25, weight: .bold))
Spacer()
ScrollView {
LazyVStack(alignment: .leading) {
ForEach(font.files, id: \.id) { value in
StyleCell(style: value)
}
}
}
.id(font.family)
}
.padding()
}
}
- Affiche un en-tête avec le nom de la famille sélectionnée.
- Affichez une liste des styles de la famille de polices à l’aide d’une pile verticale paresseuse qui défile.
- Donnez à la vue de défilement un ID unique afin qu’elle défile vers le haut lors de sa création. Il semble que SwiftUI ait voulu réutiliser la vue de défilement, ce qui ferait en sorte que la position de défilement ne soit pas en haut à chaque nouvelle sélection.
StyleCellule
Le StyleCell
est responsable de la prévisualisation d’un style de police unique pour une famille sélectionnée. La cellule affichera AZ et 0–9 dans le style particulier donné. Lorsque la police est en cours de chargement, « Trying to load » s’affiche. Comme les polices sont temporaires, elles n’existent que tant qu’elles sont affichées.
struct StyleCell: View {
let style: FontFile
@State var font: Font?var body: some View {
GroupBox(label: Text(style.style.friendlyName)) {
if let font = font {
Text("ABCDEFGHJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n1234567890")
.font(font)
.frame(maxWidth: .infinity, alignment: .leading)
}
else {
Text("trying to load")
.font(.system(size: 20))
.frame(maxWidth: .infinity, alignment: .leading)
}
}
.task {
font = try? await Font.register(url: style.url, size: 32)
}
.padding()
}
}
- Enveloppez l’aperçu dans une zone de groupe contenant le style
friendlyName
comme titre. - S’il existe une police enregistrée, affichez l’aperçu. Sinon, affichez « Tentative de chargement ». Assurez-vous que ceux-ci sont affichés dans l’espace maximal autorisé horizontalement.
- Dans le
task
modificateur, enregistrez la police du style lors de la création de la vue.
L’exécution du projet permet maintenant à l’utilisateur de sélectionner une police et d’afficher son aperçu.