我想让用户 Select 一种自定义字体.

import SwiftUI

class FontPickerVM: ObservableObject {
    @AppStorage("custom_font") var font: Font? = nil
    // @Published var font: Font? = nil
}

extension Font: RawRepresentable {
    public typealias RawValue = String
    
    
    public init?(rawValue: RawValue) {
        
    }
    
    public var rawValue: RawValue {
        
    }
}

我如何实现FontRawRepresentable


我正在使用UIFontPickerViewController Select 一种字体.

import UIKit
import SwiftUI

public struct SUIFontPicker: UIViewControllerRepresentable {
    
    @Environment(\.presentationMode) var presentationMode
    private let onFontPick: (UIFontDescriptor) -> Void
    
    public init(onFontPick: @escaping (UIFontDescriptor) -> Void) {
        self.onFontPick = onFontPick
    }
    
    public func makeUIViewController(context: UIViewControllerRepresentableContext<SUIFontPicker>) -> UIFontPickerViewController {
        let configuration = UIFontPickerViewController.Configuration()
        configuration.includeFaces = true
        configuration.displayUsingSystemFont = false
        
        let vc = UIFontPickerViewController(configuration: configuration)
        vc.delegate = context.coordinator
        return vc
    }
    
    public func makeCoordinator() -> SUIFontPicker.Coordinator {
        return Coordinator(self, onFontPick: self.onFontPick)
    }
    
    public class Coordinator: NSObject, UIFontPickerViewControllerDelegate {
        
        var parent: SUIFontPicker
        private let onFontPick: (UIFontDescriptor) -> Void
        
        
        init(_ parent: SUIFontPicker, onFontPick: @escaping (UIFontDescriptor) -> Void) {
            self.parent = parent
            self.onFontPick = onFontPick
        }
        
        public func fontPickerViewControllerDidPickFont(_ viewController: UIFontPickerViewController) {
            guard let descriptor = viewController.selectedFontDescriptor else { return }
            onFontPick(descriptor)
            parent.presentationMode.wrappedValue.dismiss()
        }
        
        public func fontPickerViewControllerDidCancel(_ viewController: UIFontPickerViewController) {
            parent.presentationMode.wrappedValue.dismiss()
        }
    }
    
    public func updateUIViewController(_ uiViewController: UIFontPickerViewController,
                                       context: UIViewControllerRepresentableContext<SUIFontPicker>) {
        
    }
}

struct FontPicker: View {
    
    @State var isFontPickerPresented = false
    @Environment(\.font) var defaultFont
    
    @StateObject var vm = FontPickerVM()
    
    var body: some View {
        Button {
            isFontPickerPresented = true
            printDetails()
        } label: {
            Text("Select font")
        }
        .sheet(isPresented: $isFontPickerPresented) {
            SUIFontPicker { fontDescriptor in
                let newFont = UIFont(descriptor: fontDescriptor, size: 18)
                vm.font = Font(newFont as CTFont)
                printDetails()
            }
        }
        .font(vm.font)
    }
    
    func printDetails(){
        print("default font:", defaultFont ?? "")
        print("custom font:", vm.font ?? "")
    }
}



struct FontPicker_Previews: PreviewProvider {
    static var previews: some View {
        Form {
            FontPicker()
            FontPicker()
        }
    }
}

我必须指定大小为UIFont(descriptor: fontDescriptor, size: 18),但我只想更改字体系列.

推荐答案

但我只想更改字体系列.

如果是这种情况,则应将configuration.includeFaces设置为false,以便用户只能 Select 族,而不能 Select 族中的面(粗体、斜体等).

在此之后,UIFontPickerViewController返回的UIFontDescriptor在其fontAttributes中应该始终具有NSFontFamilyAttribute密钥.获取该键的值并将其存储在AppStorage中.

示例:

@AppStorage("customFont") var fontFamily: String?

var customFont: Font {
    if let fontFamily {
        return Font.custom(fontFamily, size: 18)
    } else {
        return .body
    }
}

@State var showSheet = false

var body: some View {
    Text("Foo Bar Baz")
        .font(customFont)
        .onTapGesture {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) {
            SUIFontPicker { desc in
                fontFamily = desc.fontAttributes[.family] as? String
            }
        }
}

如果字体描述符中还有其他您也想保存的信息(尽管根据我的经验,UIFontPickerViewController的字体描述符没有太多的"其他信息"),从技术上讲,您可以使用NSKeyedArchiver将字体描述符转换为Data.然后,您可以将Data存储在@AppStorage中.

let fontData = try? NSKeyedArchiver.archivedData(withRootObject: someFontDescriptor, requiringSecureCoding: true)
// store fontData into AppStorage 

Ios相关问答推荐

iOS中的分段拾取器—手柄点击已 Select 的项目

SWIFT中具有可选返回类型和泛型的网络请求

在选项卡内列出导航内视图断开导航标题的动画

无法在所有窗口上方显示图像

从后台线程调用DispatchObject的方法安全吗?

在flutter中使用flatter_screenutil这样的软件包的目的是什么?

Swift中是否有一种方法可以为可选属性创建一个计算(computed)属性

更改图标会导致 SwiftUI 动画出现故障?

从iOS键盘输入时Flutter TextField输入消失

Xcode 不支持 iOS 15.6

用溢出的长文本对齐 Flutter 中的行和列

如何在不支持并发的自动关闭中修复'async'调用?

.overlay 和 body 是什么 swift 概念

try 在 iOS 中分发 Flutter 应用程序时出现Invalid Provisioning Profile Signature错误

UIImageView 使用 CGAffineTransform 错误旋转

iOS,Swift 5,AppDelegate open url / .onOpenUrl - 如何找到调用我的自定义方案的调用应用程序

有什么方法可以预填充核心数据?

如何 comments .plist 文件中的行?

如何关闭情节提要弹出框

当键盘出现在 iOS 中时,将 UIView 上移