Thought this might be a great opportunity for my first post! π
Objectives:
- Add a
paste
command to your app menu (window commands) and aCmd+V
keyboard shortcut - Make a
copy
button that you can add to your window commands
TL;DR:
Add the PasteButton system button in your window commands.
Find snippet forcopy
button action at the end.
Setup
Create an ObservableObject to store and publish your clipboard content to the rest of the app.
// AppClipboard.swift
class AppClipboard: ObservableObject {
@Published var content: URL?
init(content: URL? = nil) {
self.content = content
}
}
Attach an instance in your app entry point:
// MyApp.swift
import SwiftUI
@main
struct MyApp: App {
@StateObject var clipboard = AppClipboard() // <---- 1. //
var body: some Scene {
WindowGroup("My App Name") {
ContentView()
.environmentObject(clipboard) // <---- 2. //
}
}
}
And now you can access the object in child views:
import SwiftUI
struct MyView: View {
@EnvironmentObject var clipboard: AppClipboard // <---- 1. //
var body: some View {
Text(clipboard.content) // <----- 2. //
}
}
Next, create a FocusBinding by extending the FocusedValues struct and adding a key for the clipboard. This makes the ObservableObject
we created earlier accessible from the window menu commands we will create later.
// FocusedValuesExtension.swift
private struct ClipboardContentKey: FocusedValueKey {
typealias Value = Binding<String?>
}
extension FocusedValues {
var clipboardContent: Binding<String?>? {
get { self[ClipboardContentKey.self] }
set { self[ClipboardContentKey.self] = newValue }
}
}
Now in your window menu commands add the clipboard FocusedBinding
like so:
// Commands.swift
import SwiftUI
struct MyWindowCommands: Commands {
@FocusedBinding(\.clipboardContent) var clipboardContent // <--- 1. //
var body: some Commands {
// Option 1: Replace system pasteboard menu...
CommandGroup(replacing: CommandGroupPlacement.pasteboard) {
PasteButton(payloadType: String.self) { values in
guard let value = values.first else { return }
// make sure we updating on the main thread (or we get a warning)
DispatchQueue.main.async{
clipboardContent = value // <---- 2. //
}
return
}
.keyboardShortcut("V", modifiers: [.command]) // <--- shortcut 3. //
}
// Option 2: Create custom menu
CommandMenu("My Custom Menu") {
PasteButton(payloadType: String.self) { values in
guard let value = values.first else { return }
DispatchQueue.main.async{
clipboardContent = value
}
return
}
.keyboardShortcut("V", modifiers: [.command])
}
}
}
If you build your app in XCode, you should now see a working paste
under the Edit
menu or your custom menu.
Making a copy
button
This is a little trickier because it depends on how you plan to use the copy action in your app. But I will include here a simple button action snippet that lets you put content in the system clipboard.
Button(action: {
NSPasteboard.general.declareTypes([.string], owner: nil)
let pasteboard = NSPasteboard.general // <----- This is important! Do not convert to a one liner!
pasteboard.setString(webURL.absoluteString, forType: .string)
}, label: {
Image(systemName: "square.on.square")
})
You can incorporate this into your window commands like the paste
button.
Hope you found this useful!
Top comments (0)