SwiftUI: iOS icin Modern Arayuzler Olusturma
SwiftUI ile modern kullanici arayuzleri olusturma rehberi: deklaratif sozdizimi, bilesenler, animasyonlar ve iOS 18 icin en iyi uygulamalar.

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.
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:
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:
// 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 spaceFark 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.
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'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:
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.
// 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:
// 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.
@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.
iOS mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
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:
// 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:
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:
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:
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.
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:
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.
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.
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
withAnimationkullanmak - Async/await:
.taskile 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
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
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.
Etiketler
Paylaş
İlgili makaleler

SwiftUI Performans: LazyVStack ve Karmaşık Listeleri Optimize Etme
LazyVStack ve SwiftUI listeleri için optimizasyon teknikleri. Bellek tüketimini azaltın, kaydırma performansını iyileştirin ve sık karşılaşılan tuzakları önleyin.

Özel SwiftUI ViewModifier'lar: Design System için yeniden kullanılabilir desenler
Tutarlı bir design system için SwiftUI'da özel ViewModifier'lar oluşturun. iOS view'lerini verimli şekilde stillendirmek için desenler, en iyi uygulamalar ve pratik örnekler.

SwiftUI @Observable vs @State: 2026'da Hangisini Ne Zaman Kullanmalı
iOS uygulamaları için doğru durum yönetimi aracını seçmek üzere SwiftUI'daki @Observable ve @State arasındaki farkları öğrenin.