# SwiftUI: iOS icin Modern Arayuzler Olusturma > SwiftUI ile modern kullanici arayuzleri olusturma rehberi: deklaratif sozdizimi, bilesenler, animasyonlar ve iOS 18 icin en iyi uygulamalar. - Published: 2026-01-05 - Updated: 2026-04-02 - Author: Anthony Fillion-Maillet - Tags: swiftui, ios, swift, ui, apple - Reading time: 12 min --- SwiftUI, tum Apple platformlarinda arayuz gelistirmeyi koklu bir bicimde donusturdu. Deklaratif sozdizimi ve yerel entegrasyonu sayesinde bu framework, daha once mumkun olandan cok daha az kodla zarif uygulamalar olusturmaya olanak tanir. iOS 18 ile birlikte performans alaninda onemli iyilestirmeler ve yeni yetenekler sunulmaktadir. > **Why SwiftUI in 2026?** > > SwiftUI, iOS 18 ile tam olgunluga ulasti. Optimize edilmis render, iyilestirilmis bellek yonetimi ve basitlestirilmis UIKit entegrasyonu sunmaktadir. Yeni iOS uygulamalari icin standart haline gelmistir. ## Deklaratif Paradigmayi Anlamak Kod yazmaya baslamadan once SwiftUI'yi farkli kilan seyin ne oldugunu kavramak gerekir. UIKit'te (eski framework) gelistirici, iOS'a arayuzu adim adim **nasil** olusturacagini soylemek zorundaydi: "bir etiket olustur, buraya yerlestir, kullanici dokundugunda rengini degistir". Bu **imperatif** (buyurgan) paradigmadir. SwiftUI farkli calisir: gelistirici **ne** goruntulemek istedigini tarif eder ve framework gerisini halleder. Aradaki fark, donus donus yol tarifi vermekle (imperatif) yalnizca varilacak yeri belirtmek (deklaratif) arasindaki fark gibidir. ### Ilk View SwiftUI'de her arayuz ogesi bir `View`'dir. View, ekranda neyin gorunmesi gerektigini tanimlayan bir struct'tir. Asagida bir baslik, alt baslik ve buton iceren ilk ekran yer almaktadir: ```swift // ContentView.swift import SwiftUI // Each screen is a struct that implements the View protocol struct ContentView: View { // The "body" property describes what the view displays // "some View" means "a type of View, but Swift figures it out automatically" var body: some View { // VStack = "Vertical Stack": stacks elements vertically // spacing: 20 = 20 points of space between each element VStack(spacing: 20) { // A simple text with style modifiers Text("Welcome to SharpSkill") .font(.largeTitle) // Large title font .fontWeight(.bold) // Bold text Text("Prepare for your iOS interviews") .font(.subheadline) // Smaller font .foregroundColor(.secondary) // Gray secondary color // Button with an action (closure) and a label Button("Get Started") { print("Button tapped!") } .buttonStyle(.borderedProminent) // Filled blue button style } .padding() // Adds margins around the VStack } } ``` Yapiya dikkat edilmesi onemlidir: istenen ogeler (metinler, buton) tanimlanir, dikey olarak istiflenirler (VStack) ve stiller **modifier**'lar (`.font()`, `.padding()`) araciligiyla uygulanir. > **Temel prensip**: SwiftUI'de UI nesneleri "olusturulmaz" -- istenen arayuz **tarif edilir**. SwiftUI, gercek ogelerin olusturulmasini, guncellenmesini ve yok edilmesini kendi basina yonetir. ### Modifier'lar: View'leri Donusturmek Modifier'lar, bir view'den sonra zincirlenen ve onu donusturen metotlardir. Ust uste uygulanan filtreler gibi calisirlar. **Sira onemlidir** cunku her modifier bir oncekini saran yeni bir view olusturur. Asagidaki ornek, siralanmanin neden kritik oldugunu gostermektedir: ```swift // ModifiersOrder.swift // Example 1: padding THEN background Text("SwiftUI") .padding() // 1. Adds 16pt of space around the text .background(.blue) // 2. Blue background covers text + padding .foregroundColor(.white) // Result: white text on blue rectangle with margins // Example 2: background THEN padding (reversed order!) Text("SwiftUI") .background(.blue) // 1. Tight blue background around text only .padding() // 2. Transparent padding around blue background .foregroundColor(.white) // Result: white text on small blue rectangle, surrounded by empty space ``` Fark nedir? Birinci durumda padding arka planin "icinde"dir. Ikinci durumda ise "disinda"dir. Incelikli ama duzen tasariminda ustunluk saglamak icin temel bir ayrinti. ## Arayuzleri Stack'lerle Duzenlemek SwiftUI, view'leri duzenlemek icin uc ana kapsayici sunar. Bunlar, iceriklerini farkli sekillerde dizilten kutular olarak dusunulebilir. ### VStack, HStack ve ZStack - **VStack** (Vertical Stack): ogeleri yukaridan asagiya istifler - **HStack** (Horizontal Stack): ogeleri soldan saga hizalar - **ZStack** (Z-axis Stack): ogeleri ust uste katmanlar Asagida her uc stack'i birlestiren bir kullanici profil karti olusturulmaktadir. Amac: dogrulama rozeti ile birlikte bir fotograf, ardindan kullanicinin adi ve rolu gostermek. ```swift // ProfileCard.swift struct ProfileCard: View { var body: some View { // Main HStack: photo on left, info on right HStack(spacing: 16) { // ZStack to overlay the badge on the photo ZStack(alignment: .bottomTrailing) { // Profile image (circle) Image("avatar") .resizable() .frame(width: 60, height: 60) .clipShape(Circle()) // Green "verified" badge at bottom right Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) .background(Circle().fill(.white)) // White circle behind } // VStack to stack name and role vertically VStack(alignment: .leading, spacing: 4) { Text("Marie Dupont") .font(.headline) Text("iOS Developer") .font(.subheadline) .foregroundColor(.secondary) } // Spacer pushes everything to the left Spacer() // Chevron on right indicates it's tappable Image(systemName: "chevron.right") .foregroundColor(.gray) } .padding() .background(Color(.systemGray6)) .cornerRadius(12) } } ``` Buradaki kilit nokta `Spacer()`'dir: mevcut tum alani kaplayan gorunmez bir oge. Bu olmadan ogeler ortaya hizalanir. Spacer sayesinde sola itilirler ve chevron saga sabitlenmis olarak kalir. > **Xcode Tip** > > Xcode'da herhangi bir view uzerinde `⌘ + click` kullanilarak gorsel inspektore erisilebilir. Kod yazmadan modifier eklemek mumkundur. ## @State ve @Binding ile Durum Yonetimi Durum yonetimi SwiftUI'nin kalbini olusturur. Durum (state), degisebilen ve arayuzu guncellemesi gereken herhangi bir veridir. Durum degistiginde SwiftUI etkilenen view'leri otomatik olarak yeniden hesaplar. ### @State: Bir View'in Yerel Durumu `@State`, SwiftUI'ye "bu degiskeni izle ve degistiginde view'i yenile" diyen bir property wrapper'dir. Tek bir view'in yerel durumu icin idealdir. Mekanizmayi anlamak icin asagida interaktif bir sayac yer almaktadir: ```swift // CounterView.swift struct CounterView: View { // @State creates a "source of truth" for this view // private because state shouldn't be modified from outside @State private var count = 0 var body: some View { VStack(spacing: 30) { // This Text updates automatically when count changes Text("\(count)") .font(.system(size: 72, weight: .bold)) HStack(spacing: 40) { // Decrement button Button(action: { count -= 1 // Modifies state → view refreshes }) { Image(systemName: "minus.circle.fill") .font(.largeTitle) } // Increment button Button(action: { count += 1 }) { Image(systemName: "plus.circle.fill") .font(.largeTitle) } } } } } ``` Bir butona dokunuldugunda `count` degeri degisir. SwiftUI bu degisikligi algilar ve goruntuyu guncellemek icin `body`'yi yeniden calistirir. Etiketi elle guncellemek gerekmez -- her sey otomatik olarak gerceklesir. ### @Binding: View'ler Arasinda Durum Paylasimi Bazen bir alt view (child), ust view'in (parent) durumunu degistirmek zorunda kalir. `@Binding` tam burada devreye girer: mevcut bir `@State`'e cift yonlu bir baglanti olusturur. Asagida somut bir ornek bulunmaktadir: gercek zamanli dogrulamali bir kullanici adi giris alani. ```swift // UsernameValidation.swift // Parent view: owns the state struct SignupForm: View { @State private var username = "" // Source of truth @State private var isValid = false // Validation state var body: some View { VStack(spacing: 20) { // We pass BINDINGS (with $) to the child view UsernameField(username: $username, isValid: $isValid) Button("Create Account") { // Submit the form } .disabled(!isValid) // Disabled if invalid .buttonStyle(.borderedProminent) } .padding() } } ``` Alt view binding'leri alir ve bunlari degistirebilir: ```swift // Child view: receives and modifies state via @Binding struct UsernameField: View { @Binding var username: String // Connection to parent's @State @Binding var isValid: Bool var body: some View { VStack(alignment: .leading, spacing: 8) { TextField("Username", text: $username) .textFieldStyle(.roundedBorder) .onChange(of: username) { oldValue, newValue in // Validation: at least 3 characters isValid = newValue.count >= 3 } // Visual feedback HStack { Image(systemName: isValid ? "checkmark.circle" : "xmark.circle") Text("Minimum 3 characters") } .font(.caption) .foregroundColor(isValid ? .green : .red) } } } ``` Kullanici TextField'a yazdiginda, `username` binding araciligiyla degistirilir. Ust view bu degisikligi gorur ve kullanabilir. Temiz bir cift yonlu iletisimdir. > **Warning** > > `@State` yalnizca basit, yerel durumlar icin kullanilmalidir. Birden fazla ekran arasinda paylasilan veriler veya karmasik mantik icin `@Observable` (iOS 17+) veya MVVM kaliplarini tercih etmek daha dogrudur. ## Dinamik Listeleri Goruntuleme Listeler mobil uygulamalarin her yerinde bulunur. SwiftUI, veri koleksiyonlarini yerel iOS stiliyle (ayiricilar, kayma eylemleri vb.) goruntulemek icin `List` sunar. ### Basit Bir Liste Olusturma Bir listeyi goruntulemek icin iki sey gereklidir: veri ve onu tanimlama yolu. `Identifiable` protokolu, SwiftUI'ye hangi view'in hangi veriye karsilik geldigini bildirir. Once veri modeli tanimlanir: ```swift // Models/Interview.swift // Identifiable lets SwiftUI track each element struct Interview: Identifiable { let id = UUID() // Auto-generated unique identifier let technology: String let difficulty: String let questionCount: Int } ``` Simdi liste. Fikir, `ForEach` ile veriler uzerinde yinelemek ve her oge icin bir satir olusturmaktir: ```swift // InterviewListView.swift struct InterviewListView: View { // Data to display (in reality, this would come from an API) @State private var interviews = [ Interview(technology: "iOS", difficulty: "Intermediate", questionCount: 25), Interview(technology: "Android", difficulty: "Advanced", questionCount: 30), Interview(technology: "React", difficulty: "Beginner", questionCount: 20) ] var body: some View { // NavigationStack enables the navigation bar NavigationStack { List { // ForEach iterates over each interview // Thanks to Identifiable, no need to specify id: ForEach(interviews) { interview in InterviewRow(interview: interview) } } .navigationTitle("My Interviews") } } } ``` Her satir icin view, okunabilirlik acisindan ayri bir bilesene cikartilmistir: ```swift // InterviewRow.swift struct InterviewRow: View { let interview: Interview var body: some View { HStack { // Left column: title and subtitle VStack(alignment: .leading, spacing: 4) { Text(interview.technology) .font(.headline) Text(interview.difficulty) .font(.subheadline) .foregroundColor(.secondary) } Spacer() // Badge with question count Text("\(interview.questionCount) Q") .font(.caption) .fontWeight(.medium) .padding(.horizontal, 8) .padding(.vertical, 4) .background(Color.blue.opacity(0.1)) .cornerRadius(8) } .padding(.vertical, 4) } } ``` `InterviewRow`'u ayri bir struct'a cikarmak kodu daha okunabilir ve yeniden kullanilabilir hale getirir. Bu, SwiftUI'de yerlesik bir en iyi uygulamadir. ### Eylem Ekleme: Silme ve Yeniden Siralama iOS listeleri dogal olarak silme (sola kaydir) ve yeniden siralama (surukle ve birak) destekler. SwiftUI bunu `.onDelete` ve `.onMove` ile son derece kolaylastirir: ```swift // InterviewListView.swift (enhanced version) struct InterviewListView: View { @State private var interviews = [/* ... data ... */] var body: some View { NavigationStack { List { ForEach(interviews) { interview in InterviewRow(interview: interview) } // Swipe to delete .onDelete(perform: deleteInterview) // Drag and drop to reorder .onMove(perform: moveInterview) } .navigationTitle("My Interviews") .toolbar { // "Edit" button that activates edit mode EditButton() } } } // Deletes elements at specified indices private func deleteInterview(at offsets: IndexSet) { interviews.remove(atOffsets: offsets) } // Moves elements from one position to another private func moveInterview(from source: IndexSet, to destination: Int) { interviews.move(fromOffsets: source, toOffset: destination) } } ``` Yalnizca 4 ek satir kod (`.onDelete`, `.onMove`, `EditButton` ve iki fonksiyon) ile tamamen interaktif bir liste elde edilir. SwiftUI'nin gucu budur. ## Arayuzleri Animasyonlandirma SwiftUI animasyonlarda one cikar. Animasyonlarin cok fazla kod gerektirdigi UIKit'in aksine, burada her sey deklaratiftir: son durum tarif edilir ve SwiftUI gecisi animasyonlandirir. ### withAnimation ile Ortuk Animasyonlar Animasyonlamanin en basit yolu, bir durum degisikligini `withAnimation` icine sarmaktir. SwiftUI neyin degistigini algilar ve etkilenen gorsel ozellikleri otomatik olarak animasyonlandirir. ```swift // AnimatedCard.swift struct AnimatedCard: View { @State private var isExpanded = false var body: some View { VStack { RoundedRectangle(cornerRadius: 20) .fill(.blue) // Dimensions change based on state .frame( width: isExpanded ? 300 : 150, height: isExpanded ? 200 : 100 ) Button(isExpanded ? "Collapse" : "Expand") { // withAnimation animates ALL visual changes // resulting from this state change withAnimation(.spring(response: 0.5, dampingFraction: 0.7)) { isExpanded.toggle() } } .padding(.top) } } } ``` Butona dokunuldugunda `isExpanded` degeri degisir. `withAnimation` sayesinde dikdortgenin boyut degisikligi yay efektiyle animasyonlandirilir. Neyin animasyonlandirilacagini belirtmek gerekmez -- SwiftUI bunu kendisi belirler. ### Gecisler: Gorunme ve Kaybolmayi Animasyonlandirma Gecisler (transition) bir view'in nasil gorunecegini veya kaybolacagini belirler. Varsayilan olarak fade (`opacity`) uygulanir, ancak ozellestirilebilir: ```swift // TransitionDemo.swift struct TransitionDemo: View { @State private var showDetails = false var body: some View { VStack(spacing: 20) { Button("Show Details") { withAnimation(.easeInOut(duration: 0.3)) { showDetails.toggle() } } // This view appears/disappears with a transition if showDetails { DetailCard() // Asymmetric transition: different for entry and exit .transition( .asymmetric( insertion: .scale.combined(with: .opacity), // Entry: zoom + fade removal: .slide // Exit: slide ) ) } } } } struct DetailCard: View { var body: some View { VStack(alignment: .leading, spacing: 12) { Text("Interview Details") .font(.headline) Text("25 questions • 45 minutes") .foregroundColor(.secondary) } .padding() .background(Color(.systemGray6)) .cornerRadius(16) } } ``` Bu ornekte kart merkezden buyuyerek (scale + opacity) gorunur ve yana kayarak (slide) kaybolur. Bu mikro-animasyonlar arayuze canli ve profesyonel bir hava katar. > **Performance** > > SwiftUI animasyonlari otomatik olarak optimize eder. Dogal bir his icin `.spring()` tercih edilmeli ve kullanicilari rahatsiz edecek asiri uzun animasyonlardan (> 0.5s) kacinilmalidir. ## Asenkron Veri Yukleme Gercek uygulamalarda veriler genellikle bir API'den gelir. Swift Concurrency (async/await), `.task` modifier'i araciligiyla SwiftUI ile kusursuz bir sekilde entegre olur. ### Loading / Error / Success Kalıbi Asagida API verileri icin standart goruntulenme kalıbi yer almaktadir. Uc durum ele alinir: yukleme, hata ve basari. ```swift // AsyncDataView.swift struct AsyncDataView: View { @State private var questions: [Question] = [] @State private var isLoading = true @State private var errorMessage: String? var body: some View { Group { if isLoading { // State: loading in progress ProgressView("Loading...") } else if let error = errorMessage { // State: error VStack(spacing: 16) { Image(systemName: "exclamationmark.triangle") .font(.largeTitle) .foregroundColor(.orange) Text(error) Button("Retry") { Task { await loadQuestions() } } } } else { // State: success, display data List(questions) { question in Text(question.title) } } } // .task runs automatically when the view appears .task { await loadQuestions() } // Pull-to-refresh .refreshable { await loadQuestions() } } private func loadQuestions() async { isLoading = true errorMessage = nil do { // Async API call questions = try await QuestionService.shared.fetchQuestions() } catch { errorMessage = "Unable to load questions" } isLoading = false } } ``` `.task` modifier'i hayati onem tasir: view gorundugundu asenkron bir gorev baslatir ve view kayboldugundu otomatik olarak iptal eder. Bellek sizintisi riski ortadan kalkar. ## Sonuc SwiftUI, modern iOS gelistirme icin vazgecilmez hale gelmistir. iOS 18 ile framework, uretim uygulamalari icin mukemmel uygunluk saglayan bir olgunluga ulasmistir. ### Temel Cikarimlar - **Deklaratif paradigma**: ne istendigini tarif etmek, nasil insa edilecegini degil - **@State ve @Binding**: durumu reaktif olarak yonetmek ve view'ler arasinda yaymak - **Stack'ler**: esnek duzenler icin VStack, HStack ve ZStack'i birlestirmek - **List**: minimum kodla koleksiyonlari goruntulemek ve yerel etkileşimler saglamak - **Animasyonlar**: otomatik akici gecisler icin `withAnimation` kullanmak - **Async/await**: `.task` ile veri yuklemek ve yukleme/hata durumlarini yonetmek ### Checklist - Imperatif (UIKit) ve deklaratif (SwiftUI) arasindaki farki kavramak - Modifier'lari ve uygulama siralarini kavramak - @State, @Binding ve @Observable'in ne zaman kullanilacagini bilmek - Stack'lerle duzen olusturmak - Eylemlerle (silme, tasima) liste uygulamak - withAnimation ile durum degisikliklerini animasyonlandirmak SwiftUI, tum Apple ekosistemi genelinde zarif uygulamalar olusturmanin kapilarini acar. Ogrenmenin en iyi yolu pratik yapmaktir: kucuk bir kisisel proje olusturmak ve bu makaledeki her kavramla deney yapmak tavsiye edilir. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/ios/swiftui-building-modern-interfaces