0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SwiftUIでNSFontPanelを使ったフォントピッカーを作成

Last updated at Posted at 2023-10-02

概要

  • 現状(2023-10月)、NSColorPanelのSwiftUIの代替にColorPickerが出ているが、NSFontPanelのSwiftUIの代替はでていない。
  • そのためNSFontPanelをSwiftUIで扱えるピッカー用のViewを作成してみる。
  • (下記のSelect...ボタンが今回作成したフォントピッカー)
image

参考

GitHub

実装

  • BindingでNSFontを渡し、裏でNSFontPanelを呼び出して変更を反映させる。
  • これによりSwiftUI側では特に意識せずにNSFontを渡してNSFontPanelのUIでフォントの更新が可能となる。
import SwiftUI

/// refs: [tyagishi / FontPicker](https://github.com/tyagishi/FontPicker/tree/main)
fileprivate class FontPickerDelegate {
    
    private var parent: FontPicker

    init(_ parent: FontPicker) {
        self.parent = parent
    }
    
    @objc
    private func changeFont(_ id: Any) {
        parent.fontSelected()
    }

}

struct FontPicker: View {
    
    private let label: LocalizedStringKey?
    @Binding private var font: NSFont
    @State private var fontPickerDelegate: FontPickerDelegate? = nil
    
    init(_ label: LocalizedStringKey? = nil, selection: Binding<NSFont>) {
        self.label = label
        self._font = selection
    }
    
    var body: some View {
        Button {
            fontPickerDelegate = FontPickerDelegate(self)
            NSFontManager.shared.target = fontPickerDelegate
            NSFontPanel.shared.setPanelFont(font, isMultiple: false)
            NSFontPanel.shared.orderBack(nil)
        } label: {
            if let label {
                Text(label)
            } else {
                EmptyView()
            }
        }
    }
    
    fileprivate func fontSelected() {
        font = NSFontPanel.shared.convert(self.font)
    }
}
struct FontData {
    var name: String
    var pointSize: CGFloat
}
  • 外部的にはNSFontを公開して、内部的にFontDataでデータの保存を行っている。
public class Settings: ObservableObject {
    
    static let shared = Settings()
    
    @AppStorage("selected-font")
    private var fontData: FontData = .init(name: "Helvetica", pointSize: 14)

    var nsFont: NSFont {
        get {
            fontData.nsFont
        }
        
        set(newValue) {
            fontData = newValue.fontData
        }
    }
}
  • 呼び出し例は以下の通り。
import SwiftUI

struct ContentView: View {
    
    @ObservedObject var settings = Settings.shared
    
    var body: some View {
        Form {
            Section {
                HStack {
                    ...
                    FontPicker("Select...", selection: $settings.nsFont)
                }
                ...
                .frame(maxWidth: .infinity, alignment: .trailing)
                
            }
            
            Section {
                Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, ...")
                    .font(Font(settings.nsFont))
            } header: {
                Text("Preview")
            }
        }
        .formStyle(.grouped)
    }
}
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?