Oturma odasının en büyük ekranı Apple TV'de. 4K HDR, Dolby Atmos, büyük ekran deneyimi... Ve SwiftUI ile geliştirmesi sandığından kolay. Tek fark: dokunmatik ekran yok, uzaktan kumanda var. Bu da tamamen farklı bir interaction model demek.
İçindekiler
- tvOS Geliştirmeye Giriş
- Focus Engine: tvOS'un Kalbi
- SwiftUI ile tvOS UI
- Top Shelf Extensions
- Video Playback ve AVKit
- Multi-User Desteği
- Game Controller
- Production Best Practices
tvOS Geliştirmeye Giriş {#tvos-giris}
tvOS, iOS'un büyük ekran versiyonu. Ama temel fark: dokunmatik yok. Kullanıcı Siri Remote ile gezinir - bu da "focus-based navigation" demek.
Özellik | iOS | tvOS |
|---|---|---|
**Input** | Multi-touch | Siri Remote + Focus |
**Ekran** | Küçük-orta | Büyük (40-85") |
**Mesafe** | 30cm | 2-5 metre |
**Font boyutu** | 17pt | 29pt+ |
**Etkileşim** | Tap, swipe | Focus, select, menu |
**Storage** | Sınırlı değil | 500KB kalıcı |
⚠️ Dikkat: tvOS'ta kalıcı depolama sadece 500KB! Büyük veriler CloudKit veya on-demand resources ile yönetilmeli.
Dış Kaynaklar:
Focus Engine {#focus-engine}
tvOS'un en önemli kavramı focus. Ekranda bir anda sadece bir öğe "focused" olabilir. Siri Remote ile yön tuşlarıyla focus hareket ettirilir.
swift
1struct MovieGridView: View {2 let movies: [Movie]3 @FocusState private var focusedMovie: Movie.ID?4 5 var body: some View {6 ScrollView(.horizontal) {7 LazyHStack(spacing: 40) {8 ForEach(movies) { movie in9 MovieCard(movie: movie)10 .focusable()11 .focused($focusedMovie, equals: movie.id)12 // Focus'ta büyütme efekti13 .scaleEffect(focusedMovie == movie.id ? 1.1 : 1.0)14 .shadow(15 radius: focusedMovie == movie.id ? 20 : 516 )17 .animation(.easeInOut(duration: 0.2), value: focusedMovie)18 .onSubmit {19 // Siri Remote'ta tıklama20 navigateToMovie(movie)21 }22 }23 }24 .padding(.horizontal, 50)25 }26 }27}28 29struct MovieCard: View {30 let movie: Movie31 @Environment(\.isFocused) var isFocused32 33 var body: some View {34 VStack {35 AsyncImage(url: movie.posterURL) { image in36 image.resizable().aspectRatio(2/3, contentMode: .fill)37 } placeholder: {38 Rectangle().fill(.gray.opacity(0.3))39 }40 .frame(width: 220, height: 330)41 .clipShape(RoundedRectangle(cornerRadius: 10))42 43 if isFocused {44 Text(movie.title)45 .font(.caption)46 .transition(.opacity)47 }48 }49 }50}SwiftUI ile tvOS UI {#swiftui-tvos}
swift
1struct ContentView: View {2 var body: some View {3 TabView {4 // tvOS tab'ları ekranın üstünde5 HomeView()6 .tabItem { Label("Ana Sayfa", systemImage: "house") }7 8 MoviesView()9 .tabItem { Label("Filmler", systemImage: "film") }10 11 SearchView()12 .tabItem { Label("Ara", systemImage: "magnifyingglass") }13 14 SettingsView()15 .tabItem { Label("Ayarlar", systemImage: "gear") }16 }17 }18}19 20// Büyük ekran için özel layout21struct HomeView: View {22 var body: some View {23 ScrollView {24 VStack(alignment: .leading, spacing: 50) {25 // Hero banner26 FeaturedCarousel(items: featuredMovies)27 .frame(height: 500)28 29 // Kategoriler30 ForEach(categories) { category in31 VStack(alignment: .leading, spacing: 20) {32 Text(category.name)33 .font(.title2)34 .padding(.leading, 50)35 36 ScrollView(.horizontal, showsIndicators: false) {37 LazyHStack(spacing: 40) {38 ForEach(category.movies) { movie in39 MovieCard(movie: movie)40 }41 }42 .padding(.horizontal, 50)43 }44 .focusSection() // Bu section içinde focus yatay hareket eder45 }46 }47 }48 }49 }50}Top Shelf Extensions {#top-shelf}
Top Shelf, Apple TV ana ekranında uygulamanın üst kısmında gösterilen içerik:
swift
1import TVServices2 3class TopShelfProvider: TVTopShelfContentProvider {4 override func loadTopShelfContent() async -> TVTopShelfContent? {5 // Sectioned content (dizi/film kategorileri gibi)6 let sections = try? await loadCategories()7 8 let items = sections?.map { category -> TVTopShelfSectionedItem in9 let sectionItems = category.items.map { item -> TVTopShelfSectionedItem.Item in10 let topShelfItem = TVTopShelfSectionedItem.Item(identifier: item.id)11 topShelfItem.title = item.title12 13 let image = TVTopShelfSectionedItem.Item.Image()14 image.url = item.imageURL15 topShelfItem.setImage(image, for: .screenScale2x)16 17 return topShelfItem18 }19 20 let section = TVTopShelfSectionedItem(identifier: category.id)21 section.title = category.name22 section.items = sectionItems23 return section24 } ?? []25 26 return TVTopShelfSectionedContent(sections: items)27 }28}Video Playback {#video}
swift
1import AVKit2 3struct VideoPlayerView: View {4 let videoURL: URL5 @StateObject private var playerVM = VideoPlayerViewModel()6 7 var body: some View {8 VideoPlayer(player: playerVM.player) {9 // Overlay UI10 VStack {11 Spacer()12 HStack {13 Text(playerVM.currentTimeString)14 Spacer()15 Text(playerVM.durationString)16 }17 .padding()18 .background(.ultraThinMaterial)19 }20 }21 .onAppear {22 playerVM.setup(url: videoURL)23 playerVM.player.play()24 }25 .onDisappear {26 playerVM.player.pause()27 }28 }29}Multi-User Desteği {#multi-user}
tvOS, aynı Apple TV'yi kullanan birden fazla kullanıcıyı destekler. Her kullanıcının kendi profili, tercihleri ve önerileri olabilir:
swift
1import TVServices2 3class UserManager {4 // Aktif kullanıcıyı tespit et5 func getCurrentUser() async -> TVUserManager.User? {6 let userManager = TVUserManager()7 return userManager.currentUser8 }9 10 // Kullanıcı değişikliğini dinle11 func observeUserChanges() {12 NotificationCenter.default.addObserver(13 forName: .TVUserManagerCurrentUserDidChange,14 object: nil,15 queue: .main16 ) { [weak self] _ in17 Task {18 await self?.handleUserChange()19 }20 }21 }22 23 private func handleUserChange() async {24 // Kullanıcı değişince:25 // 1. Profil verilerini yükle26 // 2. Önerileri güncelle27 // 3. İzleme geçmişini değiştir28 }29}Multi-User Kuralları
Kural | Açıklama |
|---|---|
Kullanıcı başına veri | Her kullanıcının tercihleri ayrı tutulmalı |
Geçiş hızı | Kullanıcı değiştiğinde UI anında güncellenmeli |
Varsayılan profil | Giriş yapılmadığında ortak profil göster |
iCloud entegrasyonu | Her kullanıcının kendi iCloud hesabı farklı |
Game Controller Desteği {#game-controller}
Apple TV'de oyun geliştirmek için Game Controller framework'ü kullanılır. Siri Remote'un yanı sıra MFi controller ve PlayStation/Xbox gamepad'leri de desteklenir:
swift
1import GameController2 3class GameControllerManager: ObservableObject {4 @Published var connectedController: GCController?5 6 init() {7 setupControllerObservers()8 }9 10 private func setupControllerObservers() {11 // Controller bağlantısı12 NotificationCenter.default.addObserver(13 forName: .GCControllerDidConnect,14 object: nil,15 queue: .main16 ) { [weak self] notification in17 if let controller = notification.object as? GCController {18 self?.configureController(controller)19 }20 }21 22 // Zaten bağlı controller'ları kontrol et23 if let controller = GCController.controllers().first {24 configureController(controller)25 }26 }27 28 private func configureController(_ controller: GCController) {29 connectedController = controller30 31 // Extended gamepad (PS/Xbox controller)32 if let gamepad = controller.extendedGamepad {33 gamepad.buttonA.pressedChangedHandler = { _, _, pressed in34 if pressed { self.handleAction() }35 }36 gamepad.leftThumbstick.valueChangedHandler = { _, xValue, yValue in37 self.handleMovement(x: xValue, y: yValue)38 }39 }40 41 // Siri Remote (micro gamepad)42 if let remote = controller.microGamepad {43 remote.buttonA.pressedChangedHandler = { _, _, pressed in44 if pressed { self.handleSelect() }45 }46 }47 }48 49 private func handleAction() { /* Aksiyon butonu */ }50 private func handleSelect() { /* Seçim */ }51 private func handleMovement(x: Float, y: Float) { /* Hareket */ }52}Desteklenen Controller Türleri
Controller | Kullanım | API |
|---|---|---|
**Siri Remote** | Temel navigasyon + basit oyunlar | `microGamepad` |
**MFi Controller** | Tam oyun desteği | `extendedGamepad` |
**PS DualSense** | Haptic feedback + adaptive triggers | `extendedGamepad` + `physicalInputProfile` |
**Xbox Controller** | Standart gamepad | `extendedGamepad` |
Production Best Practices {#best-practices}
🔑 Çıkarımlar
- Focus engine tvOS'un temeli - her UI elemanı focusable olmalı
- Büyük fontlar kullan - 2+ metre mesafeden okunmalı
- Focus section ile navigation akışını kontrol et
- Top Shelf içeriğini taze tut - kullanıcının ilk gördüğü yer
- 500KB storage limiti - büyük veri CloudKit'te
- Siri Remote gesture desteği ekle - swipe ile hızlı navigation
Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
Okuyucu Ödülü
Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var:
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.

