first commit
This commit is contained in:
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"nuxt.isNuxtApp": false
|
||||
}
|
||||
173
macOS_Development_Rules.md
Normal file
173
macOS_Development_Rules.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# 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:** `SFSafariViewController` macOS'te yoktur ve kullanılmamalıdır. Kullanıcıyı tarayıcıya (`Link` veya `NSWorkspace.open`) yönlendirmek Mac kültürüne daha uygundur.
|
||||
|
||||
### Uygulama Yaşam Döngüsü (App Lifecycle)
|
||||
* **Yapı:** `@main` struct'ı `App` protokolüne uymalıdır.
|
||||
* **WindowGroup:** Ana arayüzü `WindowGroup` içine al. Bu, macOS'in otomatik olarak çoklu pencere (multi-window) ve sekme (tab) desteği sunmasını sağlar.
|
||||
```swift
|
||||
@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 `.infinity` olmalı.
|
||||
* `idealWidth` / `idealHeight`: Uygulama ilk açıldığında önerilen boyut.
|
||||
```swift
|
||||
.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):** `NavigationLink` kullanma. macOS'te "push" navigasyonu (bir ekranın diğerinin üzerine kayması) nadirdir.
|
||||
* **Desen:** Sidebar + Detail (Split View) yapısını kullan.
|
||||
* **Uygulama:** `NavigationView` içine tüm panelleri (Sidebar, Detail) en baştan koy. Görünürlük, bir `@Binding` selection değişkeni ile yönetilir.
|
||||
```swift
|
||||
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. `Tag` yapısını `NavigationLink` yerine 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 `LazyVGrid` kullan.
|
||||
* **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.
|
||||
```swift
|
||||
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 `List` yerine SwiftUI `Table` gö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 `.onHover` ve `NSCursor` kullan.
|
||||
```swift
|
||||
.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 `View` dosyalarından ayır ve `Commands` protokolü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)` veya `CommandGroup(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 `Picker` koy. Otomatik olarak alt menü (submenu) ve tik işareti (checkmark) oluşturur.
|
||||
|
||||
---
|
||||
|
||||
## 6. ARAÇ ÇUBUĞU (Toolbar)
|
||||
|
||||
### Yapı ve Özelleştirme
|
||||
* **Kural:** `ToolbarItem` veya `ToolbarItemGroup` kullan.
|
||||
* **Customizable:** Kullanıcının sağ tıkla toolbar'ı düzenleyebilmesi için toolbar'a ve öğelere `.id()` ver.
|
||||
```swift
|
||||
.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 `@AppStorage` kullan (UserDefaults wrapper).
|
||||
* **Pencere Bazlı Ayarlar:** Her pencerenin kendi durumunu hatırlaması için (örn. Seçili sekme, Arama metni) `@SceneStorage` kullan.
|
||||
```swift
|
||||
@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`, `NSWindow` veya `NSCursor`'a erişmekten çekinme.
|
||||
* **Örnek (Sidebar Toggle):**
|
||||
```swift
|
||||
func toggleSidebar() {
|
||||
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. CHECKLIST FOR AGENTS
|
||||
|
||||
Bir özellik eklerken şu soruları sor:
|
||||
1. **Pencere:** Bu özellik yeni bir pencerede açıldığında nasıl davranacak? (`@SceneStorage` kullanıldı mı?)
|
||||
2. **Menü:** Bu aksiyonun Menü çubuğunda bir karşılığı ve klavye kısayolu var mı?
|
||||
3. **Mouse:** Hover efektleri var mı? Tıklanabilir alanlar belli mi?
|
||||
4. **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.
|
||||
5. **Boyut:** Pencere küçüldüğünde veya büyüdüğünde (`GridItem.adaptive`, `Table` sütunları) arayüz uyum sağlıyor mu?
|
||||
209
tvOS_Development_Rules.md
Normal file
209
tvOS_Development_Rules.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# MASTER RULESET: Professional tvOS Development with SwiftUI
|
||||
|
||||
**VERSION:** 2.0 (Comprehensive)
|
||||
**TARGET:** AI Coding Agents (Claude 3.5 Sonnet, GPT-4o, etc.)
|
||||
**CONTEXT:** Building a high-end, native Apple TV application.
|
||||
|
||||
Bu belge, tvOS ekosistemi için "State of the Art" uygulama geliştirme kurallarını içerir. Aşağıdaki yönergeler, dokunmatik ekran mantığından (Touch Paradigm) tamamen çıkıp, uzaktan kumanda odaklı (Focus Paradigm) bir yapı kurmayı zorunlu kılar.
|
||||
|
||||
---
|
||||
|
||||
## 1. TEMEL FELSEFE: "10-FOOT EXPERIENCE"
|
||||
|
||||
tvOS uygulamaları, kullanıcı ekrandan 3-4 metre uzaktayken (10-foot UI) kullanılır. Bu fiziksel mesafe, tasarım ve kod mimarisini kökten değiştirir.
|
||||
|
||||
### 1.1. Görsel Hiyerarşi ve Okunabilirlik
|
||||
* **KURAL:** Asla iOS yazı boyutlarını kullanma. Kullanıcı uzaktadır.
|
||||
* **Typography:** Apple'ın dinamik type stillerini (`.font(.headline)`, `.font(.caption)`) kullan, ancak bunları manuel olarak scale etmen gerekirse şu minimumları baz al:
|
||||
* **Title:** 57pt+
|
||||
* **Headline:** 38pt+
|
||||
* **Body:** 29pt+ (Asla 29pt altına düşme).
|
||||
* **Caption:** Sadece ikincil bilgiler için, min 25pt.
|
||||
* **Renk ve Kontrast:** TV ekranlarının renk gamutları farklıdır. Yüksek kontrastlı renkler kullan. Koyu mod (Dark Mode) tvOS'te varsayılan ve en çok tercih edilen moddur; tasarımını buna göre optimize et.
|
||||
|
||||
### 1.2. Safe Area ve Overscan
|
||||
* **KURAL:** TV üreticileri ekranın kenarlarını kırpabilir (Overscan). Kritik UI öğelerini asla ekranın tam kenarına yapıştırma.
|
||||
* **Standart Padding:** Ana içerik için kenarlardan **90px**, dikeyde ise **60px** güvenli alan bırak. SwiftUI'ın standart `safeAreaPadding` modifier'ı genellikle bunu doğru yönetir.
|
||||
|
||||
---
|
||||
|
||||
## 2. FOCUS ENGINE MİMARİSİ (CORE MECHANIC)
|
||||
|
||||
Uygulamanın kalbi **Focus Engine**'dir. Eğer Focus Engine doğru çalışmazsa, uygulama kullanılamaz.
|
||||
|
||||
### 2.1. Odaklanma Mantığı (The Golden Rule)
|
||||
* **KURAL:** Ekranda her an **mutlaka** bir nesne odaklanmış olmalıdır. Odaksız bir ekran, bozuk bir ekrandır.
|
||||
* **Varsayılan Odak:** Bir ekran açıldığında, sistem en sol-üstteki öğeye odaklanır. Bunu değiştirmek için `preferredDefaultFocus` veya SwiftUI'da `@FocusState` kullan.
|
||||
|
||||
### 2.2. Focus State Yönetimi (SwiftUI Pattern)
|
||||
Code Agent olarak, odağı programatik olarak yönetmen gereken durumlar olacaktır (örn. Yan menü açıldığında odağı oraya taşımak).
|
||||
|
||||
```swift
|
||||
struct HomeView: View {
|
||||
// Focus state tanımları (Enum kullanımı best practice'tir)
|
||||
enum FocusableField: Hashable {
|
||||
case sidebar
|
||||
case contentGrid
|
||||
case search
|
||||
}
|
||||
|
||||
@FocusState private var focusedField: FocusableField?
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
SidebarView()
|
||||
.focused($focusedField, equals: .sidebar)
|
||||
|
||||
ContentGridView()
|
||||
.focused($focusedField, equals: .contentGrid)
|
||||
}
|
||||
.onAppear {
|
||||
// Ekran açıldığında odağı zorla içeriğe ver
|
||||
focusedField = .contentGrid
|
||||
}
|
||||
}
|
||||
}
|
||||
2.3. Focus Section (Önemli)
|
||||
Büyük UI grupları (örneğin bir Sidebar ve bir Grid) arasında geçiş yaparken, sistem bazen odağı kaybedebilir veya yanlış bir öğeye atlayabilir. Bunu engellemek için .focusSection() kullan.
|
||||
|
||||
Kullanım: Sidebar konteynerine .focusSection() ekle. Bu, Focus Engine'e "Buraya girildiğinde, en son kalınan öğeyi hatırla veya en mantıklı giriş noktasına git" emrini verir.
|
||||
|
||||
3. NAVİGASYON PATENLERİ
|
||||
3.1. TabView (Ana İskelet)
|
||||
tvOS için en standart ve güvenli yapı TabView'dur.
|
||||
|
||||
Stil: .tabViewStyle(.automatic) kullan. tvOS 13 ve öncesinde üstte (Top Bar), sonrasında duruma göre Sidebar veya Top Bar olarak render edilir.
|
||||
|
||||
İpucu: Her tab'ın kendine ait bir NavigationView (veya yeni NavigationStack) barındırması gerekir.
|
||||
|
||||
3.2. Shelf (Raf) Yapısı
|
||||
Netflix/Apple TV+ tarzı içerik gösterimi için "Shelves" yapısını kullan.
|
||||
|
||||
Mimari: Dikey bir ScrollView içinde, yatay ScrollView'lar barındıran satırlar.
|
||||
|
||||
Önemli Detay: Yatay kaydırma yaparken (Shelf içinde), satırın başlığının da (Section Header) hafifçe büyümesi veya parlaması UX açısından harikadır.
|
||||
|
||||
Swift
|
||||
|
||||
ScrollView(.vertical) {
|
||||
VStack(alignment: .leading) {
|
||||
ForEach(categories) { category in
|
||||
Text(category.title)
|
||||
.font(.headline)
|
||||
.padding(.leading, 80) // Overscan koruması
|
||||
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 40) {
|
||||
ForEach(category.items) { item in
|
||||
MovieCard(item: item)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 80) // Başlangıç/Bitiş boşluğu
|
||||
.padding(.vertical, 40) // Odaklanınca büyüme payı (Clip olmaması için)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
4. UI BİLEŞENLERİ VE PARALLAX
|
||||
4.1. Kartlar ve Butonlar
|
||||
KURAL: tvOS'te "Tıklanabilir" her şey bir Button veya NavigationLink olmalıdır. Asla TapGesture kullanma.
|
||||
|
||||
Card Style: Kullanıcı bir öğeye odaklandığında, öğe hafifçe büyümeli, gölgesi artmalı ve üzerindeki parlama efekti (Shimmer) oynamalıdır.
|
||||
|
||||
Kod: SwiftUI'da bu bedavadır, ancak doğru modifier'ı kullanırsan:
|
||||
|
||||
Swift
|
||||
|
||||
Button(action: { openDetails() }) {
|
||||
// Resim ve Overlay
|
||||
ZStack(alignment: .bottom) {
|
||||
AsyncImage(url: url) { image in
|
||||
image.resizable().aspectRatio(contentMode: .fill)
|
||||
}
|
||||
Text("Title").padding()
|
||||
}
|
||||
}
|
||||
.buttonStyle(.card) // <--- SİHİRLİ MODIFIER
|
||||
4.2. Context Menüler
|
||||
Kullanıcı bir öğe üzerindeyken kumandadaki butona uzun basarsa ne olur?
|
||||
|
||||
Best Practice: .contextMenu ekleyerek "Favorilere Ekle", "Fragman İzle" gibi ikincil aksiyonları sun.
|
||||
|
||||
5. UZAKTAN KUMANDA (SIRI REMOTE) GİRİŞLERİ
|
||||
5.1. Metin Girişi (Text Input)
|
||||
YASAK: Kullanıcıyı kumanda ile e-posta/şifre yazmaya zorlama. Bu en kötü tvOS deneyimidir.
|
||||
|
||||
ÇÖZÜM 1 (Siri): Arama alanlarında .searchable() kullan. Bu, dikte özelliğini otomatik açar.
|
||||
|
||||
ÇÖZÜM 2 (Authentication): Giriş yapmak için QR Kod veya Web URL + Kod (Device Flow) yöntemini kullan. "Telefonda devam et" seçeneği sun.
|
||||
|
||||
ÇÖZÜM 3 (Continuity): TextField kullanıldığında, aynı iCloud hesabına bağlı iPhone'a otomatik bildirim gider. Buna güven ama tek çare yapma.
|
||||
|
||||
5.2. Play/Pause Butonu
|
||||
Kumandadaki Play/Pause butonu globaldir. Özel bir video oynatıcı yapıyorsan, bu butonun event'ini yakalamak için onPlayPauseCommand modifier'ını kullan.
|
||||
|
||||
6. MEDIA OYNATMA (VIDEO PLAYBACK)
|
||||
6.1. Native Player Kullanımı
|
||||
KURAL: Mümkünse her zaman AVPlayerViewController (UIKit wrap) veya SwiftUI VideoPlayer kullan.
|
||||
|
||||
Neden? Native player; Siri Remote ile ileri/geri sarma, altyazı menüsü, ses kontrolü ve Picture-in-Picture desteğini otomatik verir. Custom player yazmak tvOS'te çok zordur ve genellikle native hissi veremez.
|
||||
|
||||
6.2. Info Panel
|
||||
Video oynarken kullanıcı aşağı kaydırırsa (Swipe Down), bir bilgi paneli (Info/Metadata) açılmalıdır. AVPlayerItem'ın externalMetadata özelliğini doldurarak buraya Poster, Başlık ve Açıklama bilgilerini gönder.
|
||||
|
||||
7. KAYNAK YÖNETİMİ VE PERFORMANS
|
||||
7.1. App Size & ODR
|
||||
tvOS uygulamalarının ana paketi 4GB ile sınırlıdır (App Store kuralı). Ancak yerel depolama garanti edilmez; sistem yer açmak için verileri silebilir.
|
||||
|
||||
Best Practice: Büyük asset'leri (videolar, yüksek çözünürlüklü level görselleri) uygulamanın içine gömme. On-Demand Resources (ODR) kullan. Agent olarak kod yazarken, assetlerin sunucudan çekileceğini veya ODR etiketleriyle çağrılacağını varsay.
|
||||
|
||||
7.2. Görüntü Yükleme
|
||||
Listelerde yüzlerce yüksek çözünürlüklü poster olabilir. AsyncImage kullanırken mutlaka transaction animasyonu ekle ve disk cache mekanizması (örn. Kingfisher veya özel bir cache manager) kur. TV ekranı büyüktür, düşük çözünürlüklü resimler çok kötü görünür.
|
||||
|
||||
8. CHECKLIST FOR AGENT (GENERATION RULES)
|
||||
Kod üretirken aşağıdaki listeyi adım adım kontrol et:
|
||||
|
||||
Platform Check: Kod sadece tvOS için mi? (#if os(tvOS)) Gerekirse macOS/iOS kodlarını ayıkla.
|
||||
|
||||
Focus Check: Yazdığın View'da FocusState tanımlı mı? İlk açılışta nereye odaklanacağı belli mi?
|
||||
|
||||
Interaction: Tıklanabilir alanlar .buttonStyle(.card) veya .buttonStyle(.plain) ile sarmalanmış mı? (Default button stili bazen şeffaf olabilir, dikkat et).
|
||||
|
||||
Layout: List yerine LazyVGrid veya ScrollView + HStack (Shelf) yapısı tercih edildi mi? (List tvOS'te ekranı ikiye bölen eski bir stile sahiptir, full-screen deneyimi için Grid daha iyidir).
|
||||
|
||||
Input: Login ekranı mı yazıyorsun? Klavye açtırma. QR Kod gösteren bir View oluştur.
|
||||
|
||||
9. SAMPLE CODE STRUCTURE (STARTING POINT)
|
||||
Swift
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct TVApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
MainTabView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MainTabView: View {
|
||||
var body: some View {
|
||||
TabView {
|
||||
HomeView()
|
||||
.tabItem {
|
||||
Label("Home", systemImage: "tv")
|
||||
}
|
||||
|
||||
SearchView()
|
||||
.tabItem {
|
||||
Label("Search", systemImage: "magnifyingglass")
|
||||
}
|
||||
|
||||
SettingsView()
|
||||
.tabItem {
|
||||
Label("Settings", systemImage: "gear")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user