8.3 KiB
8.3 KiB
RULESET: macOS App Development with SwiftUI (Based on 'macOS by Tutorials')
Bu dosya, "macOS by Tutorials" (Ray Wenderlich / Kodeco) kitabındaki best practice'lere dayanarak yüksek kaliteli, native hissettiren bir macOS uygulaması geliştirmek için gerekli kuralları, mimari desenleri ve kod standartlarını içerir.
GÖREV: Bir macOS uygulaması geliştirirken aşağıdaki kurallara kesinlikle uy. iOS alışkanlıklarını (NavigationLink vb.) macOS'e aynen kopyalama. Mac kullanıcılarının beklentilerine (Menü çubuğu, Çoklu Pencere, Klavye Kısayolları) öncelik ver.
1. TEMEL FELSEFE VE MİMARİ (Core Philosophy)
iOS Portu Değil, Mac Uygulaması
- Kural: Uygulamayı sadece "büyük ekranlı bir iPad uygulaması" gibi tasarlama. Mac kullanıcıları fare/trackpad kullanır, pencereleri boyutlandırır ve klavye kısayollarına bağımlıdır.
- Kural:
SFSafariViewControllermacOS'te yoktur ve kullanılmamalıdır. Kullanıcıyı tarayıcıya (LinkveyaNSWorkspace.open) yönlendirmek Mac kültürüne daha uygundur.
Uygulama Yaşam Döngüsü (App Lifecycle)
- Yapı:
@mainstruct'ıAppprotokolüne uymalıdır. - WindowGroup: Ana arayüzü
WindowGroupiçine al. Bu, macOS'in otomatik olarak çoklu pencere (multi-window) ve sekme (tab) desteği sunmasını sağlar.@main struct MyApp: App { @StateObject var appState = AppState() // Global state var body: some Scene { WindowGroup { ContentView() .environmentObject(appState) } .commands { // Menü komutları buraya SidebarCommands() ToolbarCommands() } } }
2. PENCERE YÖNETİMİ (Window Management)
Pencere Boyutlandırma (Sizing)
- Kural: iOS'in aksine, Mac'te pencereler serbestçe boyutlandırılabilir. Ancak mantıklı sınırlar koymalısın.
- Kod:
ContentView'un en dış katmanına.frame()modifier ekle.minWidth/minHeight: UI'ın bozulmayacağı en küçük boyut.maxWidth/maxHeight: Genellikle.infinityolmalı.idealWidth/idealHeight: Uygulama ilk açıldığında önerilen boyut.
.frame(minWidth: 700, idealWidth: 1000, maxWidth: .infinity, minHeight: 400, idealHeight: 800, maxHeight: .infinity)
Pencere Başlıkları (Titles)
- Kural: Çoklu pencerelerde her pencerenin içeriğine göre dinamik başlığı olmalıdır.
- Kod:
.navigationTitle(_:)kullan. Eğer sidebar'da bir seçim varsa başlık ona göre değişmeli, yoksa varsayılan uygulama adı görünmelidir.
3. NAVİGASYON VE SIDEBAR (Navigation)
NavigationView Yapısı
- Kural (KRİTİK):
NavigationLinkkullanma. macOS'te "push" navigasyonu (bir ekranın diğerinin üzerine kayması) nadirdir. - Desen: Sidebar + Detail (Split View) yapısını kullan.
- Uygulama:
NavigationViewiçine tüm panelleri (Sidebar, Detail) en baştan koy. Görünürlük, bir@Bindingselection değişkeni ile yönetilir.NavigationView { SidebarView(selection: $selection) // Sol Panel DetailView(selection: selection) // Sağ Panel }
Sidebar Tasarımı
- List Style:
.listStyle(.sidebar)kullan. Bu, satırların hafif transparan olmasını ve seçimin sistem rengiyle (AccentColor) vurgulanmasını sağlar. - Selection:
List(selection: $selection)bağlamasını kullan.TagyapısınıNavigationLinkyerine tercih et. - Kategoriler: Listeyi
Section'lara böl. Başlıkları büyük harfle yazma (iOS gibi değil), Mac standartlarına uy.
4. UI BİLEŞENLERİ (UI Components)
Grid (Izgara) Görünümü
- Kullanım: Çok sayıda öğe gösterirken
LazyVGridkullan. - Sütunlar: Sabit sütun sayısı yerine
.adaptive(minimum:maximum:)kullanarak pencere genişletildiğinde sütun sayısının artmasını sağla.let columns = [GridItem(.adaptive(minimum: 250, maximum: 250), spacing: 20)]
Table (Tablo) Görünümü (macOS Exclusive)
- Kural: Veri yoğunluklu listeler için
Listyerine SwiftUITablegörünümünü kullan. - Özellikler: Sıralama (
sortOrder), Çoklu seçim (selection: Set<ID>), ve Sütun genişlikleri (width(min:ideal:max)) mutlaka eklenmeli. - Alternatif Satır Renkleri:
Table, varsayılan olarak satırları alternatif renklerle (zebra stripe) boyar. Bunu koru.
Kart Tasarımı ve Hover Efektleri
- Kural: Mac kullanıcıları "tıklanabilir" alanları anlamak için fare imlecini kullanır.
- İpucu: Tıklanabilir alanlarda imleci değiştirmek için
.onHoverveNSCursorkullan..onHover { inside in inside ? NSCursor.pointingHand.push() : NSCursor.pop() } - Bug Fix: Kartlara gölge (
shadow) eklerken.clipped()kullanmayı unutma, aksi takdirde gölge iç elementlere de uygulanabilir (SwiftUI bug workaround).
5. MENÜLER (Menus)
Command Protocol
- Yapı: Menüleri
Viewdosyalarından ayır veCommandsprotokolüne uyan ayrı bir dosyada (örn.Menus.swift) tut. - Hazır Gruplar: Apple'ın hazır menü gruplarını mutlaka ekle:
SidebarCommands(): View menüsüne "Toggle Sidebar" ekler.ToolbarCommands(): View menüsüne Toolbar özelleştirme seçeneklerini ekler.
Özel Menü Öğeleri
- Yerleşim:
CommandGroup(before: .help)veyaCommandGroup(after: .newItem)gibi yerleşimler kullan. - Klavye Kısayolları:
.keyboardShortcut(_:modifiers:)ile her önemli aksiyona kısayol ata.- Not: Menülerde kısayollar otomatik olarak büyük harf görünür, ekstra Shift eklemene gerek yoktur.
- Picker Entegrasyonu: Ayarlar için (örn. Tema seçimi) menü içine
Pickerkoy. Otomatik olarak alt menü (submenu) ve tik işareti (checkmark) oluşturur.
6. ARAÇ ÇUBUĞU (Toolbar)
Yapı ve Özelleştirme
- Kural:
ToolbarItemveyaToolbarItemGroupkullan. - Customizable: Kullanıcının sağ tıkla toolbar'ı düzenleyebilmesi için toolbar'a ve öğelere
.id()ver..toolbar(id: "mainToolbar") { ToolbarItem(id: "refresh", placement: .primaryAction) { ... } } - Placement: *
.navigation: Sol taraf (Sidebar toggle vb.).primaryAction: Sağ taraf (En önemli buton).automatic: Sistem karar verir.
Arama (Search)
- Kullanım:
.searchable(text: $searchText)modifier'ınıNavigationView'a ekle. Bu, toolbar'a otomatik olarak native bir arama çubuğu yerleştirir.
7. VERİ VE STATE YÖNETİMİ (Data Persistence)
Sandboxing & Network
- Kritik: macOS uygulamaları varsayılan olarak internete çıkamaz. "Signing & Capabilities" altından Outgoing Connections (Client) tikini açmalısın.
Ayarların Saklanması (Persistence)
- Global Ayarlar: Uygulama geneli ayarlar (örn. Tema, "Show Totals") için
@AppStoragekullan (UserDefaults wrapper). - Pencere Bazlı Ayarlar: Her pencerenin kendi durumunu hatırlaması için (örn. Seçili sekme, Arama metni)
@SceneStoragekullan.@SceneStorage("viewMode") var viewMode: ViewMode = .grid
8. SWIFTUI & APPKIT ENTEGRASYONU
Ne Zaman AppKit Kullanılmalı?
- SwiftUI'ın yetersiz kaldığı durumlarda (örn. Sidebar'ı kod ile açıp kapatmak, Window odağını değiştirmek, First Responder yönetimi)
NSApp,NSWindowveyaNSCursor'a erişmekten çekinme. - Örnek (Sidebar Toggle):
func toggleSidebar() { NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil) }
9. CHECKLIST FOR AGENTS
Bir özellik eklerken şu soruları sor:
- Pencere: Bu özellik yeni bir pencerede açıldığında nasıl davranacak? (
@SceneStoragekullanıldı mı?) - Menü: Bu aksiyonun Menü çubuğunda bir karşılığı ve klavye kısayolu var mı?
- Mouse: Hover efektleri var mı? Tıklanabilir alanlar belli mi?
- Platform: Bu bir iOS davranışı mı (örn. Bottom Sheet, Navigation Push)? Eğer öyleyse macOS karşılığını (Sheet, Split View değişimi) kullan.
- Boyut: Pencere küçüldüğünde veya büyüdüğünde (
GridItem.adaptive,Tablesütunları) arayüz uyum sağlıyor mu?