🎯 Exemplos recomendados
Balanced sample collections from various categories for you to explore
Exemplos de Recursos de Área de Trabalho macOS Swift
Exemplos de recursos específicos de desktop macOS Swift incluindo diálogos de arquivo, caixas de mensagem e bandeja do sistema
💻 Caixas de Mensagem swift
🟢 simple
⭐⭐
Exibir diálogos de alerta, diálogos de confirmação e caixas de mensagem personalizadas usando NSAlert
⏱️ 25 min
🏷️ swift, macos, desktop, gui
Prerequisites:
Basic Swift, Cocoa/AppKit
// macOS Swift Message Boxes Examples
// Using NSAlert for various dialog types
import Cocoa
import Foundation
// 1. Simple Alert Dialog
class SimpleAlertDialog {
func showAlert(title: String, message: String) {
print("\n--- Simple Alert ---")
print("Title: \(title)")
print("Message: \(message)")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
alert.addButton(withTitle: "OK")
let response = alert.runModal()
print("Response: \(response.rawValue)")
}
func showError(title: String, message: String) {
print("\n--- Error Alert ---")
print("Title: \(title)")
print("Message: \(message)")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .critical
alert.addButton(withTitle: "OK")
alert.runModal()
}
func showWarning(title: String, message: String) {
print("\n--- Warning Alert ---")
print("Title: \(title)")
print("Message: \(message)")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .warning
alert.addButton(withTitle: "OK")
alert.runModal()
}
}
// 2. Confirmation Dialog
class ConfirmationDialog {
enum Response {
case yes
case no
case cancelled
init(_ modalResponse: NSApplication.ModalResponse) {
switch modalResponse {
case .alertFirstButtonReturn:
self = .yes
case .alertSecondButtonReturn:
self = .no
default:
self = .cancelled
}
}
}
func confirm(title: String, message: String) -> Response {
print("\n--- Confirmation Dialog ---")
print("Question: \(message)")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
alert.addButton(withTitle: "Yes")
alert.addButton(withTitle: "No")
let response = Response(alert.runModal())
print("User response: \(response)")
return response
}
func confirmDestructive(title: String, message: String, item: String) -> Response {
print("\n--- Destructive Confirmation ---")
print("Warning: This action cannot be undone")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message + "\n\nAre you sure you want to delete '\(item)'?"
alert.alertStyle = .critical
alert.addButton(withTitle: "Delete")
alert.addButton(withTitle: "Cancel")
let response = Response(alert.runModal())
return response
}
func confirmWithThirdOption(title: String, message: String) -> Int {
print("\n--- Three-Option Confirmation ---")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
alert.addButton(withTitle: "Yes")
alert.addButton(withTitle: "No")
alert.addButton(withTitle: "Cancel")
let response = alert.runModal()
print("Response button: \(response.rawValue - NSApplication.ModalResponse.alertFirstButtonReturn.rawValue)")
return response.rawValue - NSApplication.ModalResponse.alertFirstButtonReturn.rawValue
}
}
// 3. Custom Alert with Icon
class CustomIconAlert {
func showWithIcon(title: String, message: String, icon: NSImage?) {
print("\n--- Custom Icon Alert ---")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
if let icon = icon {
alert.icon = icon
print("Custom icon set: \(icon.size)")
}
alert.addButton(withTitle: "OK")
alert.runModal()
}
func showWithSystemIcon(title: String, message: String, iconName: String) {
print("\n--- System Icon Alert ---")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
if let icon = NSImage(systemSymbolName: iconName, accessibilityDescription: nil) {
alert.icon = icon
print("Using system icon: \(iconName)")
}
alert.alertStyle = .informational
alert.addButton(withTitle: "OK")
alert.runModal()
}
}
// 4. Input Dialog
class InputDialog {
func showTextInput(title: String, message: String, defaultValue: String = "") -> String? {
print("\n--- Text Input Dialog ---")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: "Cancel")
let input = NSTextField(frame: NSRect(x: 0, y: 0, width: 300, height: 24))
input.stringValue = defaultValue
alert.accessoryView = input
alert.window.initialFirstResponder = input
let response = alert.runModal()
if response == .alertFirstButtonReturn {
let value = input.stringValue
print("Input: \(value)")
return value
}
print("Input cancelled")
return nil
}
func showPasswordInput(title: String, message: String) -> String? {
print("\n--- Password Input Dialog ---")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: "Cancel")
let input = NSSecureTextField(frame: NSRect(x: 0, y: 0, width: 300, height: 24))
alert.accessoryView = input
alert.window.initialFirstResponder = input
let response = alert.runModal()
if response == .alertFirstButtonReturn {
let password = input.stringValue
print("Password received: \(password.count) characters")
return password
}
return nil
}
func showMultiLineInput(title: String, message: String, defaultValue: String = "") -> String? {
print("\n--- Multi-line Input Dialog ---")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: "Cancel")
let scrollView = NSScrollView(frame: NSRect(x: 0, y: 0, width: 400, height: 100))
let textView = NSTextView()
textView.textContainer?.containerSize = NSSize(width: 400, height: 100)
textView.textContainer?.widthTracksTextView = true
textView.isHorizontallyResizable = false
textView.isVerticallyResizable = true
textView.autoresizingMask = .height
textView.string = defaultValue
scrollView.documentView = textView
scrollView.hasVerticalScroller = true
alert.accessoryView = scrollView
alert.window.initialFirstResponder = textView
let response = alert.runModal()
if response == .alertFirstButtonReturn {
let value = textView.string
print("Multi-line input: \(value.count) characters")
return value
}
return nil
}
}
// 5. Selection Dialog
class SelectionDialog {
func showChoice(title: String, message: String, options: [String]) -> Int? {
print("\n--- Selection Dialog ---")
print("Options: \(options)")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
for option in options {
alert.addButton(withTitle: option)
}
let response = alert.runModal()
let selectedIndex = Int(response.rawValue - NSApplication.ModalResponse.alertFirstButtonReturn.rawValue)
if selectedIndex >= 0 && selectedIndex < options.count {
print("Selected: \(options[selectedIndex]) (index: \(selectedIndex))")
return selectedIndex
}
return nil
}
func showRadioGroup(title: String, message: String, options: [(label: String, tag: Int)]) -> Int? {
print("\n--- Radio Group Dialog ---")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: "Cancel")
let radioGroup = NSStackView()
radioGroup.orientation = .vertical
radioGroup.spacing = 8
var buttons: [NSButton] = []
for (index, option) in options.enumerated() {
let button = NSButton(radioButtonWithTitle: option.label, target: nil, action: nil)
button.tag = option.tag
if index == 0 { button.state = .on }
buttons.append(button)
radioGroup.addArrangedSubview(button)
}
alert.accessoryView = radioGroup
let response = alert.runModal()
if response == .alertFirstButtonReturn,
let selected = buttons.first(where: { $0.state == .on }) {
print("Selected: \(selected.title) (tag: \(selected.tag))")
return selected.tag
}
return nil
}
}
// 6. Progress Dialog
class ProgressDialog {
func showProgress(title: String, message: String, maxValue: Double = 100) {
print("\n--- Progress Dialog ---")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
let progress = NSProgressIndicator(frame: NSRect(x: 0, y: 0, width: 300, height: 20))
progress.minValue = 0
progress.maxValue = maxValue
progress.isIndeterminate = false
alert.accessoryView = progress
progress.startAnimation(nil)
// Show as sheet or non-modal
alert.window.level = .floating
alert.runModal()
progress.stopAnimation(nil)
}
func showIndeterminateProgress(title: String, message: String) {
print("\n--- Indeterminate Progress Dialog ---")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
let progress = NSProgressIndicator(frame: NSRect(x: 0, y: 0, width: 300, height: 20))
progress.style = .spinning
progress.isIndeterminate = true
alert.accessoryView = progress
progress.startAnimation(nil)
alert.runModal()
progress.stopAnimation(nil)
}
}
// 7. Sheet Dialog (Attached to Window)
class SheetDialog {
func showSheet(window: NSWindow, title: String, message: String) {
print("\n--- Sheet Dialog ---")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
alert.addButton(withTitle: "OK")
alert.beginSheetModal(for: window) { response in
print("Sheet closed with response: \(response.rawValue)")
}
}
func showSheetConfirm(window: NSWindow, title: String, message: String, completion: @escaping (Bool) -> Void) {
print("\n--- Sheet Confirmation ---")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .warning
alert.addButton(withTitle: "Yes")
alert.addButton(withTitle: "No")
alert.beginSheetModal(for: window) { response in
let confirmed = response == .alertFirstButtonReturn
print("Sheet confirm: \(confirmed)")
completion(confirmed)
}
}
}
// 8. Toast Notification
class ToastNotification {
func show(message: String, duration: TimeInterval = 3) {
print("\n--- Toast Notification ---")
print("Message: \(message)")
let alert = NSAlert()
alert.messageText = ""
alert.informativeText = message
alert.alertStyle = .informational
alert.showsSuppressionButton = true
alert.suppressionButton.title = ""
// Auto-dismiss after duration
DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
alert.window.close()
}
alert.runModal()
}
func showInWindow(window: NSWindow, message: String, position: NSRect) {
print("\n--- Toast in Window ---")
let toast = NSPanel(
contentRect: NSRect(x: 0, y: 0, width: 300, height: 50),
styleMask: [.borderless],
backing: .buffered,
defer: false
)
let label = NSTextField(labelWithString: message)
label.frame = NSRect(x: 10, y: 10, width: 280, height: 30)
label.alignment = .center
toast.contentView?.addSubview(label)
toast.level = .floating
toast.alphaValue = 0.9
window.addChildWindow(toast, ordered: .above)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
window.removeChildWindow(toast)
toast.close()
}
}
}
// 9. Login Dialog
class LoginDialog {
func showLogin() -> (username: String, password: String)? {
print("\n--- Login Dialog ---")
let alert = NSAlert()
alert.messageText = "Login"
alert.informativeText = "Enter your credentials"
alert.alertStyle = .informational
alert.addButton(withTitle: "Login")
alert.addButton(withTitle: "Cancel")
let view = NSView(frame: NSRect(x: 0, y: 0, width: 250, height: 70))
let usernameLabel = NSTextField(labelWithString: "Username:")
usernameLabel.frame = NSRect(x: 0, y: 45, width: 80, height: 20)
view.addSubview(usernameLabel)
let usernameField = NSTextField(frame: NSRect(x: 90, y: 45, width: 150, height: 20))
view.addSubview(usernameField)
let passwordLabel = NSTextField(labelWithString: "Password:")
passwordLabel.frame = NSRect(x: 0, y: 15, width: 80, height: 20)
view.addSubview(passwordLabel)
let passwordField = NSSecureTextField(frame: NSRect(x: 90, y: 15, width: 150, height: 20))
view.addSubview(passwordField)
alert.accessoryView = view
alert.window.initialFirstResponder = usernameField
let response = alert.runModal()
if response == .alertFirstButtonReturn {
let username = usernameField.stringValue
let password = passwordField.stringValue
print("Login attempt for: \(username)")
return (username, password)
}
return nil
}
}
// 10. Complex Dialog with Multiple Controls
class ComplexDialog {
func showPreferences() -> [String: Any]? {
print("\n--- Complex Preferences Dialog ---")
let alert = NSAlert()
alert.messageText = "Preferences"
alert.alertStyle = .informational
alert.addButton(withTitle: "Save")
alert.addButton(withTitle: "Cancel")
let view = NSView(frame: NSRect(x: 0, y: 0, width: 300, height: 180))
// Checkbox 1
let checkbox1 = NSButton(checkboxWithTitle: "Enable notifications", target: nil, action: nil)
checkbox1.state = .on
checkbox1.frame = NSRect(x: 10, y: 150, width: 280, height: 20)
view.addSubview(checkbox1)
// Checkbox 2
let checkbox2 = NSButton(checkboxWithTitle: "Auto-save documents", target: nil, action: nil)
checkbox2.state = .on
checkbox2.frame = NSRect(x: 10, y: 120, width: 280, height: 20)
view.addSubview(checkbox2)
// Popup button
let popupLabel = NSTextField(labelWithString: "Theme:")
popupLabel.frame = NSRect(x: 10, y: 95, width: 80, height: 17)
view.addSubview(popupLabel)
let popup = NSPopUpButton(frame: NSRect(x: 100, y: 90, width: 190, height: 25))
popup.addItem(withTitle: "Light")
popup.addItem(withTitle: "Dark")
popup.addItem(withTitle: "Auto")
view.addSubview(popup)
// Slider
let sliderLabel = NSTextField(labelWithString: "Font size:")
sliderLabel.frame = NSRect(x: 10, y: 65, width: 80, height: 17)
view.addSubview(sliderLabel)
let slider = NSSlider(frame: NSRect(x: 100, y: 60, width: 190, height: 20))
slider.minValue = 10
slider.maxValue = 24
slider.integerValue = 13
view.addSubview(slider)
// Text field
let textFieldLabel = NSTextField(labelWithString: "Name:")
textFieldLabel.frame = NSRect(x: 10, y: 35, width: 80, height: 17)
view.addSubview(textFieldLabel)
let nameField = NSTextField(frame: NSRect(x: 100, y: 32, width: 190, height: 22))
view.addSubview(nameField)
alert.accessoryView = view
let response = alert.runModal()
if response == .alertFirstButtonReturn {
let preferences: [String: Any] = [
"notifications": checkbox1.state == .on,
"autoSave": checkbox2.state == .on,
"theme": popup.titleOfSelectedItem ?? "",
"fontSize": slider.integerValue,
"name": nameField.stringValue
]
print("Preferences saved: \(preferences)")
return preferences
}
return nil
}
}
// 11. Alert with Checkbox
class CheckboxAlert {
func showAlertWithCheckbox(title: String, message: String, checkboxText: String, completion: @escaping (Bool, Bool) -> Void) {
print("\n--- Alert with Checkbox ---")
let alert = NSAlert()
alert.messageText = title
alert.informativeText = message
alert.alertStyle = .informational
alert.addButton(withTitle: "OK")
alert.showsSuppressionButton = true
alert.suppressionButton.title = checkboxText
let response = alert.runModal()
let checked = alert.suppressionButton.state == .on
let okPressed = response == .alertFirstButtonReturn
print("OK pressed: \(okPressed), Checkbox: \(checked)")
completion(okPressed, checked)
}
func showDontAskAgain(title: String, message: String) {
showAlertWithCheckbox(title: title, message: message, checkboxText: "Don't ask again") { _, dontAsk in
if dontAsk {
print("User chose not to be asked again")
// Save preference to UserDefaults
UserDefaults.standard.set(true, forKey: "dontAskAgain")
}
}
}
}
// 12. Modal Window Dialog
class ModalWindowDialog {
func showModalWindow(title: String, content: String) {
print("\n--- Modal Window Dialog ---")
let window = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 400, height: 300),
styleMask: [.titled, .closable],
backing: .buffered,
defer: false
)
window.title = title
let contentView = NSView(frame: NSRect(x: 0, y: 0, width: 400, height: 300))
let textField = NSTextField(labelWithString: content)
textField.frame = NSRect(x: 20, y: 150, width: 360, height: 100)
textField.alignment = .center
contentView.addSubview(textField)
let closeButton = NSButton(frame: NSRect(x: 150, y: 20, width: 100, height: 30))
closeButton.title = "Close"
closeButton.target = window
closeButton.action = #selector(NSWindow.close)
contentView.addSubview(closeButton)
window.contentView = contentView
window.isReleasedWhenClosed = false
let app = NSApplication.shared
app.runModal(for: window)
window.close()
}
}
// Main demonstration
func demonstrateMessageBoxes() {
print("=== macOS Swift Message Boxes Examples ===")
// Note: These dialogs require GUI context
print("\nNote: Message boxes require NSApplication context")
print("The following examples show the implementation pattern")
print("\n--- Example Alert Types ---")
let simpleAlert = SimpleAlertDialog()
print("Simple alert: title='Information', message='This is a message'")
let confirmation = ConfirmationDialog()
print("Confirmation: title='Confirm', message='Are you sure?'")
let inputDialog = InputDialog()
print("Text input: title='Enter Name', message='Please enter your name:'")
let loginDialog = LoginDialog()
print("Login dialog: requesting username and password")
let complex = ComplexDialog()
print("Complex dialog: multiple controls (checkboxes, popup, slider, text field)")
print("\n=== All Message Box Examples Completed ===")
}
// Run demonstration
demonstrateMessageBoxes()
💻 Diálogos de Arquivo swift
🟡 intermediate
⭐⭐⭐
Abrir e salvar diálogos de arquivo com filtros e opções personalizadas usando NSOpenPanel e NSSavePanel
⏱️ 30 min
🏷️ swift, macos, desktop, gui
Prerequisites:
Intermediate Swift, Cocoa/AppKit
// macOS Swift File Dialogs Examples
// Using NSOpenPanel and NSSavePanel for file selection
import Cocoa
import Foundation
// 1. Basic Open File Dialog
class BasicFileDialog {
@discardableResult
func openFile() -> String? {
print("\n--- Basic Open File Dialog ---")
let panel = NSOpenPanel()
panel.title = "Select a file"
panel.showsResizeIndicator = true
panel.canChooseFiles = true
panel.canChooseDirectories = false
panel.allowsMultipleSelection = false
let response = panel.runModal()
if response == .OK {
if let url = panel.url {
print("Selected file: \(url.path)")
return url.path
}
}
print("No file selected")
return nil
}
}
// 2. Open File with Filters
class FileDialogWithFilters {
enum FileFilter {
case images
case text
case pdf
case all
case custom(types: [String])
var extensions: [String] {
switch self {
case .images:
return ["jpg", "jpeg", "png", "gif", "bmp", "tiff"]
case .text:
return ["txt", "md", "rtf"]
case .pdf:
return ["pdf"]
case .all:
return ["*"]
case .custom(let types):
return types
}
}
var description: String {
switch self {
case .images:
return "Image Files"
case .text:
return "Text Files"
case .pdf:
return "PDF Documents"
case .all:
return "All Files"
case .custom(let types):
return types.joined(separator: ", ")
}
}
}
func openFile(allowedTypes: [FileFilter]) -> [URL]? {
print("\n--- Open File with Filters ---")
let panel = NSOpenPanel()
panel.title = "Select a file"
panel.canChooseFiles = true
panel.canChooseDirectories = false
panel.allowsMultipleSelection = true
// Set allowed file types
let allTypes = allowedTypes.flatMap { $0.extensions }
panel.allowedFileTypes = allTypes.contains("*") ? nil : allTypes
// Display filters in title
let filterDescriptions = allowedTypes.map { $0.description }.joined(separator: ", ")
print("Allowed types: \(filterDescriptions)")
let response = panel.runModal()
if response == .OK {
let urls = panel.urls
print("Selected \(urls.count) file(s):")
urls.forEach { print(" - \($0.lastPathComponent)") }
return urls
}
return nil
}
}
// 3. Save File Dialog
class SaveFileDialog {
@discardableResult
func saveFile(defaultName: String = "untitled.txt") -> URL? {
print("\n--- Save File Dialog ---")
let panel = NSSavePanel()
panel.title = "Save File"
panel.nameFieldStringValue = defaultName
panel.canCreateDirectories = true
panel.isExtensionHidden = false
let response = panel.runModal()
if response == .OK {
if let url = panel.url {
print("Save to: \(url.path)")
// Create empty file
if let content = "Sample content\n".data(using: .utf8) {
try? content.write(to: url)
print("File created successfully")
}
return url
}
}
print("Save cancelled")
return nil
}
func saveFileWithExtension(defaultName: String, allowedExtensions: [String]) -> URL? {
print("\n--- Save File with Extension Validation ---")
let panel = NSSavePanel()
panel.title = "Save File"
panel.nameFieldStringValue = defaultName
panel.allowedFileTypes = allowedExtensions
panel.allowsOtherFileTypes = false
let response = panel.runModal()
if response == .OK {
if let url = panel.url {
print("Saved to: \(url.path)")
print("Extension: \(url.pathExtension)")
return url
}
}
return nil
}
}
// 4. Open Directory Dialog
class DirectoryDialog {
func selectDirectory() -> URL? {
print("\n--- Open Directory Dialog ---")
let panel = NSOpenPanel()
panel.title = "Select a folder"
panel.canChooseFiles = false
panel.canChooseDirectories = true
panel.allowsMultipleSelection = false
let response = panel.runModal()
if response == .OK {
if let url = panel.url {
print("Selected directory: \(url.path)")
// List directory contents
if let files = try? FileManager.default.contentsOfDirectory(atPath: url.path) {
print("Contents (\(files.count) items):")
files.forEach { print(" - \($0)") }
}
return url
}
}
return nil
}
func selectMultipleDirectories() -> [URL]? {
print("\n--- Select Multiple Directories ---")
let panel = NSOpenPanel()
panel.title = "Select folders"
panel.canChooseFiles = false
panel.canChooseDirectories = true
panel.allowsMultipleSelection = true
let response = panel.runModal()
if response == .OK {
let urls = panel.urls
print("Selected \(urls.count) director(y/ies):")
urls.forEach { print(" - \($0.path)") }
return urls
}
return nil
}
}
// 5. Open Panel with Custom Options
class CustomFileDialog {
func openWithCustomOptions() -> [URL]? {
print("\n--- Open Panel with Custom Options ---")
let panel = NSOpenPanel()
panel.title = "Select files to import"
panel.prompt = "Import"
panel.message = "Choose one or more files to import into the application"
// Configure options
panel.canChooseFiles = true
panel.canChooseDirectories = false
panel.allowsMultipleSelection = true
// Set starting directory
if let downloadsURL = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first {
panel.directoryURL = downloadsURL
print("Starting in Downloads folder")
}
// Show hidden files
panel.showsHiddenFiles = false
let response = panel.runModal()
if response == .OK {
let urls = panel.urls
print("Selected \(urls.count) file(s) for import")
return urls
}
return nil
}
func saveWithAccessoryView() -> URL? {
print("\n--- Save Panel with Accessory View ---")
let panel = NSSavePanel()
panel.title = "Export Settings"
// Create accessory view
let accessoryView = NSView(frame: NSRect(x: 0, y: 0, width: 300, height: 50))
let checkbox = NSButton(checkboxWithTitle: "Include user preferences", target: nil, action: nil)
checkbox.state = .on
checkbox.frame = NSRect(x: 20, y: 25, width: 260, height: 20)
accessoryView.addSubview(checkbox)
let label = NSTextField(labelWithString: "Export options:")
label.frame = NSRect(x: 20, y: 5, width: 260, height: 15)
accessoryView.addSubview(label)
panel.accessoryView = accessoryView
let response = panel.runModal()
if response == .OK, let url = panel.url {
let includePrefs = checkbox.state == .on
print("Exported to: \(url.path)")
print("Include preferences: \(includePrefs)")
return url
}
return nil
}
}
// 6. Async File Dialog
class AsyncFileDialog {
func openFileAsync(completion: @escaping (URL?) -> Void) {
print("\n--- Async File Dialog ---")
DispatchQueue.main.async {
let panel = NSOpenPanel()
panel.title = "Select a file"
panel.canChooseFiles = true
panel.canChooseDirectories = false
panel.begin { response in
if response == .OK {
if let url = panel.url {
print("File selected asynchronously: \(url.path)")
completion(url)
return
}
}
print("No file selected")
completion(nil)
}
}
}
}
// 7. Recent Files Dialog
class RecentFilesDialog {
func showRecentDocuments() -> [URL]? {
print("\n--- Recent Documents ---")
let panel = NSOpenPanel()
panel.title = "Recent Documents"
// Use the shared document controller
let documentController = NSDocumentController.shared
if let recentURLs = documentController.recentDocumentURLs, !recentURLs.isEmpty {
print("Found \(recentURLs.count) recent document(s):")
recentURLs.prefix(10).forEach { url in
print(" - \(url.lastPathComponent)")
}
// Show panel in directory of most recent
if let mostRecent = recentURLs.first,
let parentURL = mostRecent.deletingLastPathComponent() as URL? {
panel.directoryURL = parentURL
}
let response = panel.runModal()
if response == .OK, let url = panel.url {
return [url]
}
} else {
print("No recent documents found")
// Show regular dialog
let response = panel.runModal()
if response == .OK, let url = panel.url {
return [url]
}
}
return nil
}
}
// 8. File Dialog with Preview
class FileDialogWithPreview {
func openWithPreview() -> URL? {
print("\n--- File Dialog with Preview ---")
let panel = NSOpenPanel()
panel.title = "Select an image"
panel.canChooseFiles = true
panel.canChooseDirectories = false
panel.allowedFileTypes = ["jpg", "jpeg", "png", "gif", "pdf"]
// Enable preview
panel.canDownloadItems = true
// Create custom preview accessory view
let previewView = NSImageView()
previewView.frame = NSRect(x: 0, y: 0, width: 200, height: 200)
previewView.imageScaling = .scaleProportionallyUpOrDown
previewView.wantsLayer = true
previewView.layer?.borderWidth = 1
previewView.layer?.borderColor = NSColor.separatorColor.cgColor
panel.accessoryView = previewView
// Note: In a real app, you would implement a delegate to update preview
// when selection changes
let response = panel.runModal()
if response == .OK, let url = panel.url {
print("Selected: \(url.lastPathComponent)")
// Load and display preview
if let image = NSImage(contentsOf: url) {
previewView.image = image
print("Image size: \(image.size)")
}
return url
}
return nil
}
}
// 9. File Dialog with Validation
class ValidatedFileDialog {
func openWithValidation(minFileSize: Int = 0, maxFileSize: Int = Int.max) -> URL? {
print("\n--- File Dialog with Validation ---")
let panel = NSOpenPanel()
panel.title = "Select a file"
panel.canChooseFiles = true
panel.canChooseDirectories = false
panel.message = "File size must be between \(minFileSize) and \(maxFileSize) bytes"
let response = panel.runModal()
if response == .OK, let url = panel.url {
// Validate file size
if let attributes = try? FileManager.default.attributesOfItem(atPath: url.path),
let fileSize = attributes[.size] as? UInt64 {
print("File: \(url.lastPathComponent)")
print("Size: \(fileSize) bytes")
if fileSize < UInt64(minFileSize) {
let alert = NSAlert()
alert.messageText = "File too small"
alert.informativeText = "Selected file is smaller than minimum required size (\(minFileSize) bytes)"
alert.alertStyle = .warning
alert.runModal()
return nil
}
if fileSize > UInt64(maxFileSize) {
let alert = NSAlert()
alert.messageText = "File too large"
alert.informativeText = "Selected file exceeds maximum allowed size (\(maxFileSize) bytes)"
alert.alertStyle = .warning
alert.runModal()
return nil
}
return url
}
}
return nil
}
}
// 10. Comprehensive File Dialog Manager
class FileDialogManager {
enum DialogMode {
case open
case save
case selectFolder
case selectMultiple
}
struct DialogConfig {
var title: String = "Select File"
var message: String? = nil
var defaultName: String = "untitled"
var allowedTypes: [String]? = nil
var allowsMultiple: Bool = false
var canSelectFolders: Bool = false
var startDirectory: URL? = nil
var prompt: String? = nil
static func openDialog(title: String = "Open",
message: String? = nil,
types: [String]? = nil,
multiple: Bool = false) -> DialogConfig {
var config = DialogConfig()
config.title = title
config.message = message
config.allowedTypes = types
config.allowsMultiple = multiple
return config
}
static func saveDialog(title: String = "Save",
defaultName: String = "untitled",
types: [String]? = nil) -> DialogConfig {
var config = DialogConfig()
config.title = title
config.defaultName = defaultName
config.allowedTypes = types
return config
}
}
func show(mode: DialogMode, config: DialogConfig) -> [URL]? {
print("\n--- File Dialog Manager ---")
print("Mode: \(mode), Title: \(config.title)")
switch mode {
case .open, .selectMultiple:
let panel = NSOpenPanel()
panel.title = config.title
panel.message = config.message
panel.canChooseFiles = !config.canSelectFolders
panel.canChooseDirectories = config.canSelectFolders
panel.allowsMultipleSelection = config.allowsMultiple
panel.allowedFileTypes = config.allowedTypes
if let prompt = config.prompt {
panel.prompt = prompt
}
panel.directoryURL = config.startDirectory
let response = panel.runModal()
if response == .OK {
return panel.urls.isEmpty ? nil : panel.urls
}
case .save:
let panel = NSSavePanel()
panel.title = config.title
panel.message = config.message
panel.nameFieldStringValue = config.defaultName
panel.allowedFileTypes = config.allowedTypes
panel.directoryURL = config.startDirectory
if let prompt = config.prompt {
panel.prompt = prompt
}
let response = panel.runModal()
if response == .OK, let url = panel.url {
return [url]
}
case .selectFolder:
let panel = NSOpenPanel()
panel.title = config.title
panel.canChooseFiles = false
panel.canChooseDirectories = true
panel.allowsMultipleSelection = config.allowsMultiple
panel.directoryURL = config.startDirectory
let response = panel.runModal()
if response == .OK {
return panel.urls.isEmpty ? nil : panel.urls
}
}
return nil
}
}
// Main demonstration
func demonstrateFileDialogs() {
print("=== macOS Swift File Dialogs Examples ===")
let manager = FileDialogManager()
// Note: These dialogs require GUI context
print("\nNote: Dialogs require NSApplication context")
print("The following examples show the implementation pattern")
// Example configurations
print("\n--- Example Configurations ---")
let openConfig = FileDialogManager.DialogConfig.openDialog(
title: "Open Image",
message: "Select an image file",
types: ["jpg", "jpeg", "png", "gif"],
multiple: true
)
print("Open config: \(openConfig.title), types: \(openConfig.allowedTypes ?? [])")
let saveConfig = FileDialogManager.DialogConfig.saveDialog(
title: "Save Document",
defaultName: "document.txt",
types: ["txt", "md"]
)
print("Save config: \(saveConfig.title), default: \(saveConfig.defaultName)")
print("\n=== All File Dialog Examples Completed ===")
}
// Run demonstration
demonstrateFileDialogs()
💻 Bandeja do Sistema swift
🟡 intermediate
⭐⭐⭐
Criar e gerenciar aplicações de barra de menu com NSStatusItem
⏱️ 35 min
🏷️ swift, macos, desktop, gui
Prerequisites:
Intermediate Swift, Cocoa/AppKit
// macOS Swift System Tray Examples
// Creating and managing menu bar applications with NSStatusItem
import Cocoa
import Foundation
// 1. Basic Status Item
class BasicStatusItem {
private var statusItem: NSStatusItem?
func createStatusItem() {
print("\n--- Basic Status Item ---")
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem?.button {
button.title = "🔔"
print("Status item created with icon")
}
}
func removeStatusItem() {
print("\n--- Removing Status Item ---")
if let statusItem = statusItem {
NSStatusBar.system.removeStatusItem(statusItem)
self.statusItem = nil
print("Status item removed")
}
}
}
// 2. Status Item with Menu
class StatusItemWithMenu {
private var statusItem: NSStatusItem?
func createWithMenu() {
print("\n--- Status Item with Menu ---")
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem?.button {
button.title = "⚙️"
}
let menu = NSMenu()
menu.addItem(NSMenuItem(title: "About", action: #selector(showAbout), keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Preferences", action: #selector(showPreferences), keyEquivalent: ","))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))
statusItem?.menu = menu
print("Menu created with 4 items")
}
@objc private func showAbout() {
print("Menu action: About")
showAlert(message: "About this application")
}
@objc private func showPreferences() {
print("Menu action: Preferences")
showAlert(message: "Open preferences")
}
@objc private func quitApplication() {
print("Menu action: Quit")
NSApplication.shared.terminate(nil)
}
private func showAlert(message: String) {
let alert = NSAlert()
alert.messageText = message
alert.alertStyle = .informational
alert.runModal()
}
func cleanup() {
if let statusItem = statusItem {
NSStatusBar.system.removeStatusItem(statusItem)
}
}
}
// 3. Status Item with Custom View
class StatusItemWithCustomView {
private var statusItem: NSStatusItem?
private var clickCount = 0
func createWithCustomView() {
print("\n--- Status Item with Custom View ---")
statusItem = NSStatusBar.system.statusItem(withLength: 60)
guard let button = statusItem?.button else {
print("Failed to get status item button")
return
}
button.title = ""
button.action = #selector(statusItemClicked)
button.target = self
// Create custom view
let customView = NSView(frame: NSRect(x: 0, y: 0, width: 60, height: 20))
customView.wantsLayer = true
customView.layer?.backgroundColor = NSColor.controlBackgroundColor.cgColor
customView.layer?.cornerRadius = 5
let label = NSTextField(labelWithString: "Menu")
label.frame = NSRect(x: 10, y: 2, width: 40, height: 16)
label.alignment = .center
customView.addSubview(label)
button.addSubview(customView)
print("Custom view status item created")
}
@objc private func statusItemClicked() {
clickCount += 1
print("Status item clicked (count: \(clickCount))")
let menu = NSMenu()
menu.addItem(NSMenuItem(title: "Clicked \(clickCount) times", action: nil, keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Reset", action: #selector(resetCounter), keyEquivalent: ""))
statusItem?.menu = menu
statusItem?.button?.performClick(nil)
}
@objc private func resetCounter() {
clickCount = 0
print("Counter reset")
}
func cleanup() {
if let statusItem = statusItem {
NSStatusBar.system.removeStatusItem(statusItem)
}
}
}
// 4. Dynamic Menu
class DynamicStatusMenu {
private var statusItem: NSStatusItem?
func createDynamicMenu() {
print("\n--- Dynamic Status Menu ---")
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem?.button {
button.title = "📊"
}
refreshMenu()
}
func refreshMenu() {
let menu = NSMenu()
// Dynamic timestamp
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .medium
let timeString = dateFormatter.string(from: Date())
menu.addItem(NSMenuItem(title: "Time: \(timeString)", action: nil, keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
// Dynamic system info
let memoryUsage = getMemoryUsage()
menu.addItem(NSMenuItem(title: "Memory: \(memoryUsage)", action: nil, keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Refresh", action: #selector(refreshClicked), keyEquivalent: "r"))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))
statusItem?.menu = menu
}
private func getMemoryUsage() -> String {
let task = ProcessInfo.processInfo
let used = UInt64(task.physicalMemory)
let formatted = ByteCountFormatter.string(fromByteCount: Int64(used), countStyle: .memory)
return formatted
}
@objc private func refreshClicked() {
print("Refreshing menu...")
refreshMenu()
}
@objc private func quitApplication() {
NSApplication.shared.terminate(nil)
}
func cleanup() {
if let statusItem = statusItem {
NSStatusBar.system.removeStatusItem(statusItem)
}
}
}
// 5. Status Item with Toggle
class ToggleStatusItem {
private var statusItem: NSStatusItem?
private var isEnabled = false
func createToggleItem() {
print("\n--- Toggle Status Item ---")
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
updateIcon()
let menu = NSMenu()
let toggleItem = NSMenuItem(
title: "Enable",
action: #selector(toggleEnabled),
keyEquivalent: ""
)
toggleItem.tag = 1
menu.addItem(toggleItem)
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))
statusItem?.menu = menu
}
@objc private func toggleEnabled() {
isEnabled.toggle()
updateIcon()
updateMenuItem()
print("Toggled: \(isEnabled ? "Enabled" : "Disabled")")
}
private func updateIcon() {
statusItem?.button?.title = isEnabled ? "🟢" : "🔴"
}
private func updateMenuItem() {
guard let menu = statusItem?.menu,
let item = menu.item(withTag: 1) else { return }
item.title = isEnabled ? "Disable" : "Enable"
}
@objc private func quitApplication() {
NSApplication.shared.terminate(nil)
}
func cleanup() {
if let statusItem = statusItem {
NSStatusBar.system.removeStatusItem(statusItem)
}
}
}
// 6. Status Item with Popover
class PopoverStatusItem {
private var statusItem: NSStatusItem?
private var popover: NSPopover?
func createWithPopover() {
print("\n--- Status Item with Popover ---")
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem?.button {
button.title = "ℹ️"
button.action = #selector(togglePopover)
button.target = self
}
// Create popover
popover = NSPopover()
popover?.contentSize = NSSize(width: 300, height: 200)
popover?.behavior = .transient
popover?.contentViewController = PopoverViewController()
}
@objc private func togglePopover() {
guard let button = statusItem?.button else { return }
if let popover = popover, popover.isShown {
popover.performClose(nil)
} else {
popover?.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
}
}
func cleanup() {
popover?.close()
if let statusItem = statusItem {
NSStatusBar.system.removeStatusItem(statusItem)
}
}
}
// Popover View Controller
class PopoverViewController: NSViewController {
override func loadView() {
view = NSView(frame: NSRect(x: 0, y: 0, width: 300, height: 200))
let label = NSTextField(labelWithString: "Popover Content")
label.frame = NSRect(x: 20, y: 150, width: 260, height: 30)
label.alignment = .center
label.font = NSFont.boldSystemFont(ofSize: 16)
view.addSubview(label)
let info = NSTextField(labelWithString: "This is a popover window.")
info.frame = NSRect(x: 20, y: 100, width: 260, height: 20)
info.alignment = .center
view.addSubview(info)
let closeButton = NSButton(frame: NSRect(x: 100, y: 20, width: 100, height: 30))
closeButton.title = "Close"
closeButton.target = self
closeButton.action = #selector(closePopover)
view.addSubview(closeButton)
}
@objc private func closePopover() {
if let popover = presentingViewController as? NSPopover {
popover.performClose(nil)
}
}
}
// 7. Status Item with Badge
class BadgedStatusItem {
private var statusItem: NSStatusItem?
private var badgeCount = 0
func createBadgedItem() {
print("\n--- Badged Status Item ---")
statusItem = NSStatusBar.system.statusItem(withLength: 40)
updateBadge()
let menu = NSMenu()
menu.addItem(NSMenuItem(title: "Increment", action: #selector(incrementBadge), keyEquivalent: "+"))
menu.addItem(NSMenuItem(title: "Decrement", action: #selector(decrementBadge), keyEquivalent: "-"))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Clear", action: #selector(clearBadge), keyEquivalent: "0"))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))
statusItem?.menu = menu
}
private func updateBadge() {
guard let button = statusItem?.button else { return }
if badgeCount > 0 {
button.title = "\(badgeCount)"
// Add red circle effect
button.wantsLayer = true
button.layer?.backgroundColor = NSColor.red.cgColor
button.layer?.cornerRadius = 10
} else {
button.title = "📬"
button.layer?.backgroundColor = nil
}
print("Badge count: \(badgeCount)")
}
@objc private func incrementBadge() {
badgeCount += 1
updateBadge()
}
@objc private func decrementBadge() {
badgeCount = max(0, badgeCount - 1)
updateBadge()
}
@objc private func clearBadge() {
badgeCount = 0
updateBadge()
}
@objc private func quitApplication() {
NSApplication.shared.terminate(nil)
}
func cleanup() {
if let statusItem = statusItem {
NSStatusBar.system.removeStatusItem(statusItem)
}
}
}
// 8. Status Item with Tooltip
class TooltipStatusItem {
private var statusItem: NSStatusItem?
func createWithTooltip() {
print("\n--- Status Item with Tooltip ---")
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem?.button {
button.title = "💡"
button.toolTip = "Click for options"
}
let menu = NSMenu()
let infoItem = NSMenuItem(title: "System Information", action: nil, keyEquivalent: "")
infoItem.toolTip = "View detailed system info"
menu.addItem(infoItem)
let prefItem = NSMenuItem(title: "Settings", action: nil, keyEquivalent: "")
prefItem.toolTip = "Open application settings"
menu.addItem(prefItem)
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))
statusItem?.menu = menu
print("Tooltip status item created")
}
@objc private func quitApplication() {
NSApplication.shared.terminate(nil)
}
func updateTooltip(_ tooltip: String) {
statusItem?.button?.toolTip = tooltip
print("Tooltip updated: \(tooltip)")
}
func cleanup() {
if let statusItem = statusItem {
NSStatusBar.system.removeStatusItem(statusItem)
}
}
}
// 9. Animated Status Item
class AnimatedStatusItem {
private var statusItem: NSStatusItem?
private var frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
private var currentFrame = 0
private var timer: Timer?
private var isAnimating = false
func createAnimatedItem() {
print("\n--- Animated Status Item ---")
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem?.button {
button.title = frames[0]
}
let menu = NSMenu()
menu.addItem(NSMenuItem(title: "Start Animation", action: #selector(startAnimation), keyEquivalent: "s"))
menu.addItem(NSMenuItem(title: "Stop Animation", action: #selector(stopAnimation), keyEquivalent: "x"))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))
statusItem?.menu = menu
}
@objc private func startAnimation() {
guard !isAnimating else { return }
isAnimating = true
timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
self?.updateFrame()
}
print("Animation started")
}
@objc private func stopAnimation() {
isAnimating = false
timer?.invalidate()
timer = nil
statusItem?.button?.title = "⏸"
print("Animation stopped")
}
private func updateFrame() {
currentFrame = (currentFrame + 1) % frames.count
statusItem?.button?.title = frames[currentFrame]
}
@objc private func quitApplication() {
stopAnimation()
NSApplication.shared.terminate(nil)
}
func cleanup() {
stopAnimation()
if let statusItem = statusItem {
NSStatusBar.system.removeStatusItem(statusItem)
}
}
}
// 10. Complete Menu Bar Application
class MenuBarApplication {
private var statusItem: NSStatusItem?
private var appState: AppState = AppState()
func create() {
print("\n--- Complete Menu Bar Application ---")
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem?.button {
button.title = "🚀"
}
buildMenu()
setupNotifications()
}
private func buildMenu() {
let menu = NSMenu()
// App section
menu.addItem(NSMenuItem(title: "About", action: #selector(showAbout), keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
// Features section
menu.addItem(NSMenuItem(title: "Connect", action: #selector(toggleConnection), keyEquivalent: "c"))
menu.addItem(NSMenuItem(title: "Sync", action: #selector(syncData), keyEquivalent: "s"))
menu.addItem(NSMenuItem.separator())
// Status section
menu.addItem(NSMenuItem(title: "Status: \(appState.status)", action: nil, keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
// Preferences
menu.addItem(NSMenuItem(title: "Preferences...", action: #selector(showPreferences), keyEquivalent: ","))
// Quit
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Quit", action: #selector(quitApplication), keyEquivalent: "q"))
statusItem?.menu = menu
}
private func setupNotifications() {
NotificationCenter.default.addObserver(
self,
selector: #selector(statusChanged),
name: .statusDidChange,
object: nil
)
}
@objc private func showAbout() {
print("Menu: About")
showAlert(message: "Menu Bar Application v1.0")
}
@objc private func toggleConnection() {
appState.isConnected.toggle()
print("Toggled connection: \(appState.isConnected)")
updateStatusIcon()
buildMenu()
}
@objc private func syncData() {
print("Syncing data...")
showAlert(message: "Sync completed")
}
@objc private func showPreferences() {
print("Menu: Preferences")
showAlert(message: "Open preferences window")
}
@objc private func quitApplication() {
NSApplication.shared.terminate(nil)
}
@objc private func statusChanged() {
print("Status changed: \(appState.status)")
buildMenu()
}
private func updateStatusIcon() {
statusItem?.button?.title = appState.isConnected ? "🟢" : "🔴"
}
private func showAlert(message: String) {
let alert = NSAlert()
alert.messageText = message
alert.alertStyle = .informational
alert.runModal()
}
func cleanup() {
NotificationCenter.default.removeObserver(self)
if let statusItem = statusItem {
NSStatusBar.system.removeStatusItem(statusItem)
}
}
}
// Application State
class AppState {
var isConnected = false
var status: String {
return isConnected ? "Connected" : "Disconnected"
}
}
extension Notification.Name {
static let statusDidChange = Notification.Name("statusDidChange")
}
// Main demonstration
func demonstrateSystemTray() {
print("=== macOS Swift System Tray Examples ===")
// Note: System tray items require NSApplication context
print("\nNote: Status items require NSApplication context")
print("The following examples show the implementation pattern")
print("\n--- Example Configurations ---")
print("1. Basic Status Item - Simple icon in menu bar")
print("2. Status Item with Menu - Dropdown menu with actions")
print("3. Custom View - Custom view with click handling")
print("4. Dynamic Menu - Menu that updates dynamically")
print("5. Toggle Status Item - Toggle between states")
print("6. Popover - Show popover window on click")
print("7. Badge - Show notification badge count")
print("8. Tooltip - Show tooltip on hover")
print("9. Animated Item - Animated status icon")
print("10. Complete App - Full menu bar application")
print("\n=== All System Tray Examples Completed ===")
}
// Run demonstration
demonstrateSystemTray()