From 02509206ec191fd77e3f69532b4edc407342f723 Mon Sep 17 00:00:00 2001 From: Akring Date: Mon, 19 Apr 2021 23:55:12 +0800 Subject: [PATCH] Add available font list --- FontFace.xcodeproj/project.pbxproj | 12 ++++ .../UserInterfaceState.xcuserstate | Bin 30531 -> 48288 bytes Shared/ContentView.swift | 48 ++----------- Shared/Model/FontModel.swift | 4 +- Shared/Model/FontProvider.swift | 37 ++++++++++ Shared/Utils/NSCharacterSetExtension.swift | 1 - Shared/View/AvailableFontListView.swift | 42 +++++++++++ Shared/View/CharacterGraidView.swift | 65 ++++++++++++++++++ 8 files changed, 164 insertions(+), 45 deletions(-) create mode 100644 Shared/Model/FontProvider.swift create mode 100644 Shared/View/AvailableFontListView.swift create mode 100644 Shared/View/CharacterGraidView.swift diff --git a/FontFace.xcodeproj/project.pbxproj b/FontFace.xcodeproj/project.pbxproj index 920ec7f..c0f9a07 100644 --- a/FontFace.xcodeproj/project.pbxproj +++ b/FontFace.xcodeproj/project.pbxproj @@ -16,6 +16,9 @@ 5705572E262C5F8B004A1FE3 /* FontModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5705572D262C5F8B004A1FE3 /* FontModel.swift */; }; 57055733262C602A004A1FE3 /* CharacterCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57055732262C602A004A1FE3 /* CharacterCellView.swift */; }; 5705573A262C61DF004A1FE3 /* NSCharacterSetExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57055739262C61DF004A1FE3 /* NSCharacterSetExtension.swift */; }; + 57EDE58B262DC13600D4533C /* CharacterGraidView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57EDE58A262DC13600D4533C /* CharacterGraidView.swift */; }; + 57EDE58F262DC22500D4533C /* FontProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57EDE58E262DC22500D4533C /* FontProvider.swift */; }; + 57EDE593262DC5FC00D4533C /* AvailableFontListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57EDE592262DC5FC00D4533C /* AvailableFontListView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -30,6 +33,9 @@ 5705572D262C5F8B004A1FE3 /* FontModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontModel.swift; sourceTree = ""; }; 57055732262C602A004A1FE3 /* CharacterCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterCellView.swift; sourceTree = ""; }; 57055739262C61DF004A1FE3 /* NSCharacterSetExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSCharacterSetExtension.swift; sourceTree = ""; }; + 57EDE58A262DC13600D4533C /* CharacterGraidView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CharacterGraidView.swift; sourceTree = ""; }; + 57EDE58E262DC22500D4533C /* FontProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontProvider.swift; sourceTree = ""; }; + 57EDE592262DC5FC00D4533C /* AvailableFontListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvailableFontListView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -103,6 +109,7 @@ isa = PBXGroup; children = ( 5705572D262C5F8B004A1FE3 /* FontModel.swift */, + 57EDE58E262DC22500D4533C /* FontProvider.swift */, ); path = Model; sourceTree = ""; @@ -111,6 +118,8 @@ isa = PBXGroup; children = ( 57055732262C602A004A1FE3 /* CharacterCellView.swift */, + 57EDE58A262DC13600D4533C /* CharacterGraidView.swift */, + 57EDE592262DC5FC00D4533C /* AvailableFontListView.swift */, ); path = View; sourceTree = ""; @@ -232,8 +241,11 @@ 5705571D262C5EA1004A1FE3 /* ContentView.swift in Sources */, 5705571B262C5EA1004A1FE3 /* FontFaceApp.swift in Sources */, 5705572E262C5F8B004A1FE3 /* FontModel.swift in Sources */, + 57EDE58B262DC13600D4533C /* CharacterGraidView.swift in Sources */, + 57EDE593262DC5FC00D4533C /* AvailableFontListView.swift in Sources */, 5705573A262C61DF004A1FE3 /* NSCharacterSetExtension.swift in Sources */, 57055733262C602A004A1FE3 /* CharacterCellView.swift in Sources */, + 57EDE58F262DC22500D4533C /* FontProvider.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/FontFace.xcodeproj/project.xcworkspace/xcuserdata/lvjun.xcuserdatad/UserInterfaceState.xcuserstate b/FontFace.xcodeproj/project.xcworkspace/xcuserdata/lvjun.xcuserdatad/UserInterfaceState.xcuserstate index 959a580d6564c39b7905e6b74a227816bc824d3e..2f77a87352ef1580c6582936db645e393ea5be2d 100644 GIT binary patch delta 25179 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 delta 13291 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 V(bZj|V<7Z=i$;6CoBmdG|9`;umB9c2 diff --git a/Shared/ContentView.swift b/Shared/ContentView.swift index 55c78b3..f34ac28 100644 --- a/Shared/ContentView.swift +++ b/Shared/ContentView.swift @@ -9,54 +9,18 @@ import SwiftUI struct ContentView: View { - let columns = [ - GridItem(.flexible()), - GridItem(.flexible()), - GridItem(.flexible()), - GridItem(.flexible()), - GridItem(.flexible()), - GridItem(.flexible()), - ] + @State private var selectedFontFamily: String = "" + @State private var selectedFontName: String = "" var body: some View { - ZStack(alignment: .top) { - ScrollView { - LazyVGrid(columns: columns) { - ForEach(fetchFontList(), id: \.self) { characterModel in - CharacterCellView(name: "PingFang", characterModel: characterModel) - } - } + NavigationView { + AvailableFontListView(selectedFontFamily: $selectedFontFamily, selectedFontName: $selectedFontName) + CharacterGraidView(selectedFontFamily: $selectedFontFamily, selectedFontName: $selectedFontName) } } - .padding() - .frame(minWidth: 600, idealWidth: 700, minHeight: 400, idealHeight: 800, alignment: .center) - .toolbar { - ToolbarItem(placement: .primaryAction) { - Menu { - - } label: { - Image(systemName: "square.and.arrow.up.on.square.fill") - } - } - } - } } -/// 获取字符集及其 Unicode -/// - Returns: StringCharacter -func fetchFontList() -> [CharacterModel] { - if let name = NSFontManager.shared.availableFontFamilies.first { - - if let font = NSFontManager.shared.font(withFamily: name, traits: .boldFontMask, weight: 1, size: 30) { - - let set = font.coveredCharacterSet as NSCharacterSet - - return set.characters - } - } - - return [] -} + struct ContentView_Previews: PreviewProvider { static var previews: some View { diff --git a/Shared/Model/FontModel.swift b/Shared/Model/FontModel.swift index 96183ec..65351b1 100644 --- a/Shared/Model/FontModel.swift +++ b/Shared/Model/FontModel.swift @@ -9,7 +9,7 @@ import Foundation struct FontModel: Hashable { var name: String - var characters: [CharacterModel] + var familyName: String } struct CharacterModel: Hashable { @@ -17,6 +17,6 @@ struct CharacterModel: Hashable { var unicode: String } -let mockFontModel = FontModel(name: "PingFang", characters: [mockCharacterModel]) +let mockFontModel = FontModel(name: "PingFang", familyName: "PingFang") let mockCharacterModel = CharacterModel(character: "T", unicode: "\u{e638}") diff --git a/Shared/Model/FontProvider.swift b/Shared/Model/FontProvider.swift new file mode 100644 index 0000000..d594579 --- /dev/null +++ b/Shared/Model/FontProvider.swift @@ -0,0 +1,37 @@ +// +// FontProvider.swift +// FontFace (macOS) +// +// Created by 吕俊 on 2021/4/19. +// + +import Cocoa + +class FontProvider { + static let shared = FontProvider() + private init() {} + + /// Fetch CharacterModel for given font family + /// - Returns: CharacterModel List + func fetchCharacterList(familyName: String) -> [CharacterModel] { + + guard let font = NSFontManager.shared.font(withFamily: familyName, traits: .smallCapsFontMask, weight: 1, size: 30) else { return [] } + + let set = font.coveredCharacterSet as NSCharacterSet + + return set.characters.filter({ !$0.character.isEmpty || !$0.unicode.isEmpty}) + } + + /// Fetch available font List + /// - Returns: FontModel List + func fetchAvailableFontList() -> [FontModel] { + + return NSFontManager.shared.availableFontFamilies + .compactMap({NSFontManager.shared.font(withFamily: $0, traits: .boldFontMask, weight: 1, size: 30)}) + .compactMap { font -> FontModel? in + guard let name = font.displayName else { return nil } + guard let familyName = font.familyName else { return nil } + return FontModel(name: name, familyName: familyName) + } + } +} diff --git a/Shared/Utils/NSCharacterSetExtension.swift b/Shared/Utils/NSCharacterSetExtension.swift index f10bc1d..55fd100 100644 --- a/Shared/Utils/NSCharacterSetExtension.swift +++ b/Shared/Utils/NSCharacterSetExtension.swift @@ -22,7 +22,6 @@ extension NSCharacterSet { let unicodeValue = String(data: dataenc!, encoding: String.Encoding.utf8) ?? "" let v = CharacterModel(character: String(s), unicode: unicodeValue) - results.append(v) } } diff --git a/Shared/View/AvailableFontListView.swift b/Shared/View/AvailableFontListView.swift new file mode 100644 index 0000000..158f959 --- /dev/null +++ b/Shared/View/AvailableFontListView.swift @@ -0,0 +1,42 @@ +// +// AvailableFontListView.swift +// FontFace (macOS) +// +// Created by 吕俊 on 2021/4/19. +// + +import SwiftUI + +struct AvailableFontListView: View { + + @Binding var selectedFontFamily: String + + @Binding var selectedFontName: String + + var body: some View { + ScrollView { + LazyVStack(alignment: .leading) { + ForEach(FontProvider.shared.fetchAvailableFontList(), id: \.self) { fontModel in + HStack { + Text(fontModel.name) + .font(.body) + .padding() + + Spacer() + } + .onTapGesture { + selectedFontFamily = fontModel.familyName + selectedFontName = fontModel.name + } + .background(fontModel.name == selectedFontName ? Color.red : Color.clear) + } + } + } + } +} + +struct AvailableFontListView_Previews: PreviewProvider { + static var previews: some View { + AvailableFontListView(selectedFontFamily: .constant(""), selectedFontName: .constant("")) + } +} diff --git a/Shared/View/CharacterGraidView.swift b/Shared/View/CharacterGraidView.swift new file mode 100644 index 0000000..08e4aea --- /dev/null +++ b/Shared/View/CharacterGraidView.swift @@ -0,0 +1,65 @@ +// +// CharacterGraidView.swift +// FontFace (macOS) +// +// Created by 吕俊 on 2021/4/19. +// + +import SwiftUI + +struct CharacterGraidView: View { + + @Binding var selectedFontFamily: String + @Binding var selectedFontName: String + + let columns = [ + GridItem(.flexible()), + GridItem(.flexible()), + GridItem(.flexible()), + GridItem(.flexible()), + GridItem(.flexible()), + GridItem(.flexible()), + ] + + var body: some View { + ZStack(alignment: .top) { + let list = FontProvider.shared.fetchCharacterList(familyName: selectedFontFamily) + + if list.isEmpty { + Text("Select a font to preview") + .font(.title) + } else { + ScrollView { + LazyVGrid(columns: columns) { + ForEach(list, id: \.self) { characterModel in + CharacterCellView(name: selectedFontName, characterModel: characterModel) + } + } + } + } + } + .padding() + .frame(minWidth: 600, idealWidth: 700, minHeight: 400, idealHeight: 800, alignment: .center) + .toolbar { + ToolbarItem(placement: .primaryAction) { + Menu { + + } label: { + Image(systemName: "square.and.arrow.up.on.square.fill") + } + } + } + } + + func empty() -> Bool { + return true + } +} + + + +struct CharacterGraidView_Previews: PreviewProvider { + static var previews: some View { + CharacterGraidView(selectedFontFamily: .constant("PingFang"), selectedFontName: .constant("PingFang")) + } +}