mirror of
https://github.com/rive-app/rive-ios.git
synced 2026-01-18 17:11:28 +01:00
Add fallback font support for iOS and macOS
This builds on top of #7661 and adds an iOS / macOS API for setting fallback fonts. At a high-level, this adds a class property to `RiveFont` for getting / setting the fallback font(s) **based on the system** (or _optionally_, a `UIFont/NSFont`)(accessible in Objective-C and Swift via `RiveFont.fallbackFonts`). This property is an array of a objects conforming to a new protocol: `RiveFallbackFontProvider` (platform-agnostic). By default, if no fallbacks are set, or an empty array is set, the default system font of regular weight will be used. In terms of naming, the `RiveFallbackFontDescriptorDesign` and `RiveFallbackFontDescriptorWeight` types each have cases that mirror those available in 1st party Apple APIs, so that usage and expectations are similar across our APIs, as well as those provided by UIKit / AppKit. ## Example Usage ```swift RiveFont.fallbackFonts = [ RiveFallbackFontDescriptor(systemDesign: .default, weight: .bold), RiveFallbackFontDescriptor(systemDesign: .monospaced, weight: .ultraLight), UIFont.systemFont(ofSize: 20, weight: .bold) ] ``` ## RiveFallbackFontProvider `RiveFallbackFontProvider` is a protocol that defines the interface for types that can be used to return system fonts, or any font that can be used as a fallback. `RiveFallbackFontDescriptor` and `UIFont/NSFont` conform to this protocol; both can be used to define fallback fonts. ## RiveFallbackFontDescriptor `RiveFallbackFontDescriptor` is a platform-agnostic way of defining the _type_ of system font you want to request as a fallback, if necessary. It contains a couple of properties: `design` and `weight`. These are used in conjunction with each other to start with and update a system font (generated by `[UI/NS]Font.systemFont(ofSize:weight:)`, potentially matching on more than one font. ## Unit Tests Unit tests have been written to verify that `design` and `weight` create different fonts, based on the provided values. The tests at a high-level are the same: for each case of both properties, check that there is at least one matching font. For each property, check that each font name is unique. On iOS, the font names are unique based on system design _and_ weight. I felt this was better than asserting against a specific font name, in case Apple changes that from under our feet. Additionally, what the default system fallback is set to is also tested. ## IRL Testing This was tested by creating a riv file that contained a text run whose font was exported containing only the glyphs used, and setting the text run to some text that did not use the exported glyphs. Tested on: - [x] iOS (Simulator) - [x] iPadOS - [x] macOS Diffs= a4e15fb7b Add fallback font support for iOS and macOS (#7690) Co-authored-by: David Skuza <david@rive.app>
This commit is contained in:
BIN
Example-iOS/Assets/fallback_fonts.riv
Normal file
BIN
Example-iOS/Assets/fallback_fonts.riv
Normal file
Binary file not shown.
@@ -316,6 +316,10 @@
|
||||
E5B5C2192B238829006E57C8 /* asset_load_check.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5B5C2172B238829006E57C8 /* asset_load_check.riv */; };
|
||||
E5CD7D7127DC331900BFE5E2 /* SwiftMeshAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5CD7D7027DC331900BFE5E2 /* SwiftMeshAnimation.swift */; };
|
||||
E5E87A012AE5A83800E7295F /* SwiftVariableFPS.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E87A002AE5A83700E7295F /* SwiftVariableFPS.swift */; };
|
||||
F2C623362C874E3A0006E0CA /* fallback_fonts.riv in Resources */ = {isa = PBXBuildFile; fileRef = F2C623352C874E3A0006E0CA /* fallback_fonts.riv */; };
|
||||
F2C623372C874E3A0006E0CA /* fallback_fonts.riv in Resources */ = {isa = PBXBuildFile; fileRef = F2C623352C874E3A0006E0CA /* fallback_fonts.riv */; };
|
||||
F2C623392C874E690006E0CA /* SwiftFallbackFonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C623382C874E690006E0CA /* SwiftFallbackFonts.swift */; };
|
||||
F2C6233A2C874E690006E0CA /* SwiftFallbackFonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2C623382C874E690006E0CA /* SwiftFallbackFonts.swift */; };
|
||||
F8772AF02AD94A4400AB5920 /* marty.riv in Resources */ = {isa = PBXBuildFile; fileRef = 83C89ACE2988709400044C17 /* marty.riv */; };
|
||||
F8772AF12AD94A4400AB5920 /* paper.riv in Resources */ = {isa = PBXBuildFile; fileRef = 83C89AD0298870A700044C17 /* paper.riv */; };
|
||||
F8772AF22AD94A4400AB5920 /* Bear.riv in Resources */ = {isa = PBXBuildFile; fileRef = 83DE4CB42AB3974300B88B72 /* Bear.riv */; };
|
||||
@@ -472,6 +476,8 @@
|
||||
E5B5C2172B238829006E57C8 /* asset_load_check.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = asset_load_check.riv; sourceTree = "<group>"; };
|
||||
E5CD7D7027DC331900BFE5E2 /* SwiftMeshAnimation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftMeshAnimation.swift; sourceTree = "<group>"; };
|
||||
E5E87A002AE5A83700E7295F /* SwiftVariableFPS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftVariableFPS.swift; sourceTree = "<group>"; };
|
||||
F2C623352C874E3A0006E0CA /* fallback_fonts.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = fallback_fonts.riv; sourceTree = "<group>"; };
|
||||
F2C623382C874E690006E0CA /* SwiftFallbackFonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftFallbackFonts.swift; sourceTree = "<group>"; };
|
||||
F8DA7B442AF523A800FF3CBF /* DismissableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DismissableView.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -589,6 +595,7 @@
|
||||
C9696B0E24FC6FD10041502A /* Assets */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F2C623352C874E3A0006E0CA /* fallback_fonts.riv */,
|
||||
2E83910C2C050BC4003BCF2A /* runtime_nested_inputs.riv */,
|
||||
0490915C2BC832D100F2C12B /* lip-sync_test.riv */,
|
||||
049091522BC832AF00F2C12B /* ping_pong_audio_demo.riv */,
|
||||
@@ -670,6 +677,7 @@
|
||||
041265212B0BC5A5009400EC /* SwiftSimpleAssets.swift */,
|
||||
0490914B2BC8326E00F2C12B /* SwiftAudioAssets.swift */,
|
||||
049091622BC948AA00F2C12B /* SwiftOutOfBandAudioAssets.swift */,
|
||||
F2C623382C874E690006E0CA /* SwiftFallbackFonts.swift */,
|
||||
);
|
||||
path = SwiftUI;
|
||||
sourceTree = "<group>";
|
||||
@@ -993,6 +1001,7 @@
|
||||
040554212B7A2858008F076A /* Assets.xcassets in Resources */,
|
||||
040554222B7A2858008F076A /* constrained.riv in Resources */,
|
||||
040554232B7A2858008F076A /* Bear.riv in Resources */,
|
||||
F2C623362C874E3A0006E0CA /* fallback_fonts.riv in Resources */,
|
||||
040554242B7A2858008F076A /* life_bar.riv in Resources */,
|
||||
040554252B7A2858008F076A /* wacky.riv in Resources */,
|
||||
040554262B7A2858008F076A /* asset_load_check.riv in Resources */,
|
||||
@@ -1123,6 +1132,7 @@
|
||||
C9C73E9E24FC471E00EF9516 /* Assets.xcassets in Resources */,
|
||||
0450446126B3F71E007B25CA /* constrained.riv in Resources */,
|
||||
83DE4CB52AB397A800B88B72 /* Bear.riv in Resources */,
|
||||
F2C623372C874E3A0006E0CA /* fallback_fonts.riv in Resources */,
|
||||
C9D3DE68264F49F4001BA265 /* life_bar.riv in Resources */,
|
||||
042C88DB2644447500E7DBB2 /* wacky.riv in Resources */,
|
||||
E5B5C2182B238829006E57C8 /* asset_load_check.riv in Resources */,
|
||||
@@ -1167,6 +1177,7 @@
|
||||
040553DE2B7A2858008F076A /* SwiftSimpleAssets.swift in Sources */,
|
||||
040553DF2B7A2858008F076A /* StressTest.swift in Sources */,
|
||||
040553E02B7A2858008F076A /* SwiftVariableFPS.swift in Sources */,
|
||||
F2C623392C874E690006E0CA /* SwiftFallbackFonts.swift in Sources */,
|
||||
040553E12B7A2858008F076A /* ExamplesMaster.swift in Sources */,
|
||||
040553E22B7A2858008F076A /* SwiftTouchEvents.swift in Sources */,
|
||||
040553E32B7A2858008F076A /* ClockViewModel.swift in Sources */,
|
||||
@@ -1219,6 +1230,7 @@
|
||||
041265222B0BC5A5009400EC /* SwiftSimpleAssets.swift in Sources */,
|
||||
83C89ACB29886ECB00044C17 /* StressTest.swift in Sources */,
|
||||
E5E87A012AE5A83800E7295F /* SwiftVariableFPS.swift in Sources */,
|
||||
F2C6233A2C874E690006E0CA /* SwiftFallbackFonts.swift in Sources */,
|
||||
C3357CA1280F42EC00F03B6F /* ExamplesMaster.swift in Sources */,
|
||||
C3ECAC272817BE4600A81123 /* SwiftTouchEvents.swift in Sources */,
|
||||
C3ECAC2F281840A300A81123 /* ClockViewModel.swift in Sources */,
|
||||
|
||||
55
Example-iOS/Source/Examples/SwiftUI/SwiftFallbackFonts.swift
Normal file
55
Example-iOS/Source/Examples/SwiftUI/SwiftFallbackFonts.swift
Normal file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// SwiftFallbackFonts.swift
|
||||
// RiveExample
|
||||
//
|
||||
// Created by David Skuza on 9/3/24.
|
||||
// Copyright © 2024 Rive. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import RiveRuntime
|
||||
|
||||
struct SwiftFallbackFonts: View, DismissableView {
|
||||
var dismiss: () -> Void = {}
|
||||
|
||||
@StateObject private var viewModel = RiveViewModel(fileName: "fallback_fonts")
|
||||
|
||||
private var runBinding: Binding<String> {
|
||||
Binding {
|
||||
return self.viewModel.getTextRunValue("text") ?? ""
|
||||
}
|
||||
set: { text in
|
||||
try? self.viewModel.setTextRunValue("text", textValue: text)
|
||||
self.viewModel.play()
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack() {
|
||||
viewModel.view().scaledToFit()
|
||||
|
||||
Text(
|
||||
"The included Rive font only contains characters in the set A...G. Fallback font(s) will be used to draw missing characters."
|
||||
)
|
||||
.fixedSize(horizontal: false, vertical: /*@START_MENU_TOKEN@*/true/*@END_MENU_TOKEN@*/)
|
||||
.font(.caption)
|
||||
.padding()
|
||||
|
||||
TextField("Add text with missing characters", text: runBinding)
|
||||
.textFieldStyle(.roundedBorder)
|
||||
.padding()
|
||||
|
||||
Spacer().frame(maxHeight: .infinity)
|
||||
}
|
||||
.onAppear {
|
||||
RiveFont.fallbackFonts = [
|
||||
// You can use a font descriptor that will generate a system font
|
||||
RiveFallbackFontDescriptor(design: .default, weight: .regular, width: .standard),
|
||||
// ...or an explicit system font
|
||||
UIFont.systemFont(ofSize: 12, weight: .heavy),
|
||||
// ...or a UIFont by name, or any way of initializing a UIFont
|
||||
UIFont(name: "Times New Roman", size: 12)!
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,8 +44,9 @@ class ExamplesMasterTableViewController: UITableViewController {
|
||||
("Rive Events", typeErased(dismissableView: SwiftEvents())),
|
||||
("Variable FPS", typeErased(dismissableView: SwiftVariableFPS())),
|
||||
("Simple Assets", typeErased(dismissableView: SwiftSimpleAssets())),
|
||||
("Audio Assets", typeErased(dismissableView: SwiftAudioAssets())),
|
||||
("External Audio Assets", typeErased(dismissableView: SwiftOutOfBandAudioAssets()))
|
||||
("Audio Assets", typeErased(dismissableView: SwiftAudioAssets())),
|
||||
("External Audio Assets", typeErased(dismissableView: SwiftOutOfBandAudioAssets())),
|
||||
("Fallback Fonts", typeErased(dismissableView: SwiftFallbackFonts())),
|
||||
]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user