# Ö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. - Published: 2026-03-01 - Updated: 2026-04-29 - Author: SharpSkill - Tags: swiftui, ios, viewmodifier, design-system, swift - Reading time: 12 min --- ViewModifier'lar, sağlam ve sürdürülebilir bir SwiftUI design system'in temelini oluşturur. Stilleri ve davranışları yeniden kullanılabilir bileşenlerde kapsülleyerek görsel tutarlılık sağlar ve kod tekrarını ciddi ölçüde azaltır. Bu modüler yaklaşım, modern iOS arayüzlerinin oluşturulma şeklini dönüştürür. > **Bu makalenin kapsamı** > > Bu makale, temel yapıdan tam bir design system için gelişmiş kompozisyonlara kadar etkili özel ViewModifier'lar oluşturmaya yönelik temel desenleri sunar. ## ViewModifier'ın anatomisi ViewModifier, herhangi bir view'a uygulanabilen bir dönüşümü tanımlayan bir protokoldür. View extension'larından farklı olarak, ViewModifier'lar dahili durum tutabilir ve yapılandırılabilir parametreler kabul edebilir. ```swift // CardModifier.swift import SwiftUI // Basic structure of a custom ViewModifier struct CardModifier: ViewModifier { // Configurable parameters var cornerRadius: CGFloat = 12 var shadowRadius: CGFloat = 4 // The body method applies transformations func body(content: Content) -> some View { content .padding() .background(Color(.systemBackground)) .cornerRadius(cornerRadius) .shadow( color: .black.opacity(0.1), radius: shadowRadius, x: 0, y: 2 ) } } // Extension for fluent syntax extension View { func cardStyle( cornerRadius: CGFloat = 12, shadowRadius: CGFloat = 4 ) -> some View { modifier(CardModifier( cornerRadius: cornerRadius, shadowRadius: shadowRadius )) } } ``` `Content` deseni, modifier'ın uygulandığı view'ı temsil eder. Bu soyutlama, aynı modifier'ın herhangi bir SwiftUI view tipi üzerinde çalışmasını sağlar. ### CardModifier'ı pratikte kullanmak Oluşturulduktan sonra modifier'ı uygulamak sezgisel hale gelir ve kodda doğal şekilde okunur: ```swift // CardUsageExample.swift struct ProductCard: View { let product: Product var body: some View { VStack(alignment: .leading, spacing: 8) { // Product image AsyncImage(url: product.imageURL) { image in image .resizable() .aspectRatio(contentMode: .fill) } placeholder: { Rectangle() .fill(Color.gray.opacity(0.2)) } .frame(height: 150) .clipped() // Product information Text(product.name) .font(.headline) Text(product.price, format: .currency(code: "USD")) .foregroundStyle(.secondary) } .cardStyle() // Applying the modifier } } struct Product: Identifiable { let id: UUID let name: String let price: Decimal let imageURL: URL? } ``` Kodun netliği bakım kolaylığını belirgin şekilde artırır. Uygulamadaki tüm kart'ların stilini değiştirmek yalnızca ViewModifier'da tek bir değişiklik gerektirir. ## @ViewBuilder ile koşullu modifier'lar ViewModifier'lar, davranışlarını bağlama göre uyarlamak için koşullu mantık içerebilir. `@ViewBuilder` özelliği, dallanmalarla karmaşık view'lar oluşturmaya olanak tanır. ```swift // ConditionalBadgeModifier.swift struct BadgeModifier: ViewModifier { let count: Int let color: Color let showZero: Bool init(count: Int, color: Color = .red, showZero: Bool = false) { self.count = count self.color = color self.showZero = showZero } // @ViewBuilder enables conditions in the body @ViewBuilder func body(content: Content) -> some View { if count > 0 || showZero { content.overlay(alignment: .topTrailing) { badgeView } } else { // Returns the view without modification content } } // Badge view extracted for readability private var badgeView: some View { Text(count > 99 ? "99+" : "\(count)") .font(.caption2.bold()) .foregroundStyle(.white) .padding(.horizontal, 6) .padding(.vertical, 2) .background(color) .clipShape(Capsule()) .offset(x: 8, y: -8) } } extension View { func badge( count: Int, color: Color = .red, showZero: Bool = false ) -> some View { modifier(BadgeModifier( count: count, color: color, showZero: showZero )) } } ``` Bu desen, herhangi bir arayüz öğesine bildirimli ve yapılandırılabilir şekilde bildirim badge'leri eklemeyi mümkün kılar. > **Koşulluların performansı** > > SwiftUI, `@ViewBuilder` dallarını otomatik olarak optimize eder. Görüntülenmeyen view render edilmez ve karmaşık koşullarda bile performans korunur. ## Stil token'ları ile Design System Olgun bir design system, token'lara dayanır: uygulamanın görsel kimliğini tanımlayan semantik sabitler. ViewModifier'lar bu token'ları mükemmel şekilde kapsüller. ```swift // DesignTokens.swift import SwiftUI // Spacing tokens enum Spacing { static let xs: CGFloat = 4 static let sm: CGFloat = 8 static let md: CGFloat = 16 static let lg: CGFloat = 24 static let xl: CGFloat = 32 } // Radius tokens enum Radius { static let sm: CGFloat = 4 static let md: CGFloat = 8 static let lg: CGFloat = 16 static let full: CGFloat = 9999 } // Typography tokens enum Typography { static let displayLarge = Font.system(size: 34, weight: .bold) static let displayMedium = Font.system(size: 28, weight: .bold) static let titleLarge = Font.system(size: 22, weight: .semibold) static let titleMedium = Font.system(size: 17, weight: .semibold) static let bodyLarge = Font.system(size: 17, weight: .regular) static let bodyMedium = Font.system(size: 15, weight: .regular) static let caption = Font.system(size: 12, weight: .regular) } // Semantic color tokens enum SemanticColor { static let primary = Color("PrimaryColor") static let secondary = Color("SecondaryColor") static let success = Color.green static let warning = Color.orange static let error = Color.red static let surface = Color(.systemBackground) static let surfaceVariant = Color(.secondarySystemBackground) } ``` ### Token tabanlı ViewModifier'lar Bu token'lar, uygulamanın standart stillerini tanımlayan ViewModifier'larla doğal olarak entegre olur: ```swift // TextStyleModifiers.swift // Modifier for page titles struct PageTitleModifier: ViewModifier { func body(content: Content) -> some View { content .font(Typography.displayMedium) .foregroundStyle(Color.primary) } } // Modifier for section titles struct SectionTitleModifier: ViewModifier { func body(content: Content) -> some View { content .font(Typography.titleLarge) .foregroundStyle(Color.primary) } } // Modifier for secondary text struct SecondaryTextModifier: ViewModifier { func body(content: Content) -> some View { content .font(Typography.bodyMedium) .foregroundStyle(.secondary) } } // Fluent extensions for all text styles extension View { func pageTitle() -> some View { modifier(PageTitleModifier()) } func sectionTitle() -> some View { modifier(SectionTitleModifier()) } func secondaryText() -> some View { modifier(SecondaryTextModifier()) } } ``` Kullanım ifade gücü kazanır ve kendini belgelendirir, böylece kod yeni ekip üyeleri için bile okunabilir kalır. ## Durumlu etkileşimli modifier'lar ViewModifier'lar, karmaşık etkileşimli davranışlar oluşturmak için kendi durumlarını yönetebilir. Bu desen, animasyonlar ve kullanıcı geri bildirimi için güçlüdür. ```swift // PressableModifier.swift struct PressableModifier: ViewModifier { // Internal modifier state @State private var isPressed = false // Configuration let scale: CGFloat let opacity: CGFloat let animation: Animation init( scale: CGFloat = 0.95, opacity: CGFloat = 0.8, animation: Animation = .easeInOut(duration: 0.1) ) { self.scale = scale self.opacity = opacity self.animation = animation } func body(content: Content) -> some View { content .scaleEffect(isPressed ? scale : 1.0) .opacity(isPressed ? opacity : 1.0) .animation(animation, value: isPressed) .simultaneousGesture( DragGesture(minimumDistance: 0) .onChanged { _ in if !isPressed { isPressed = true } } .onEnded { _ in isPressed = false } ) } } extension View { func pressable( scale: CGFloat = 0.95, opacity: CGFloat = 0.8 ) -> some View { modifier(PressableModifier(scale: scale, opacity: opacity)) } } ``` ### Shimmer efekti ile yükleme animasyonu Dahili durum için başka bir örnek: yükleme placeholder'ları için shimmer efekti: ```swift // ShimmerModifier.swift struct ShimmerModifier: ViewModifier { // Continuous animation @State private var phase: CGFloat = 0 let duration: Double let bounce: Bool init(duration: Double = 1.5, bounce: Bool = false) { self.duration = duration self.bounce = bounce } func body(content: Content) -> some View { content .overlay( shimmerOverlay .mask(content) ) .onAppear { withAnimation( .linear(duration: duration) .repeatForever(autoreverses: bounce) ) { phase = 1 } } } private var shimmerOverlay: some View { GeometryReader { geometry in LinearGradient( colors: [ .clear, .white.opacity(0.5), .clear ], startPoint: .leading, endPoint: .trailing ) .frame(width: geometry.size.width * 2) .offset(x: -geometry.size.width + (geometry.size.width * 2 * phase)) } } } extension View { func shimmer(duration: Double = 1.5) -> some View { modifier(ShimmerModifier(duration: duration)) } } // Usage for loading skeletons struct SkeletonCard: View { var body: some View { VStack(alignment: .leading, spacing: Spacing.sm) { RoundedRectangle(cornerRadius: Radius.md) .fill(Color.gray.opacity(0.3)) .frame(height: 120) RoundedRectangle(cornerRadius: Radius.sm) .fill(Color.gray.opacity(0.3)) .frame(height: 20) RoundedRectangle(cornerRadius: Radius.sm) .fill(Color.gray.opacity(0.3)) .frame(width: 150, height: 16) } .shimmer() } } ``` ## ViewModifier'ların kompozisyonu ViewModifier'ların gerçek gücü, kompozisyonlarındadır. Birden fazla modifier birleştirilerek basit yapı taşlarından karmaşık stiller oluşturulabilir. ```swift // ComposedModifiers.swift // Base modifier for primary buttons struct PrimaryButtonStyleModifier: ViewModifier { let isEnabled: Bool func body(content: Content) -> some View { content .font(Typography.bodyLarge.weight(.semibold)) .foregroundStyle(.white) .frame(maxWidth: .infinity) .padding(.vertical, Spacing.md) .background(isEnabled ? SemanticColor.primary : Color.gray) .cornerRadius(Radius.md) } } // Modifier for secondary buttons (outline) struct SecondaryButtonStyleModifier: ViewModifier { let isEnabled: Bool func body(content: Content) -> some View { content .font(Typography.bodyLarge.weight(.semibold)) .foregroundStyle(isEnabled ? SemanticColor.primary : .gray) .frame(maxWidth: .infinity) .padding(.vertical, Spacing.md) .background(Color.clear) .overlay( RoundedRectangle(cornerRadius: Radius.md) .stroke( isEnabled ? SemanticColor.primary : Color.gray, lineWidth: 2 ) ) } } // Extension combining style and interaction extension View { func primaryButton(isEnabled: Bool = true) -> some View { self .modifier(PrimaryButtonStyleModifier(isEnabled: isEnabled)) .pressable(scale: isEnabled ? 0.98 : 1.0) .allowsHitTesting(isEnabled) } func secondaryButton(isEnabled: Bool = true) -> some View { self .modifier(SecondaryButtonStyleModifier(isEnabled: isEnabled)) .pressable(scale: isEnabled ? 0.98 : 1.0) .allowsHitTesting(isEnabled) } } ``` Bu kompozisyon, kod tekrarı olmadan varyasyonlar oluşturmayı sağlar. Her modifier bağımsız olarak test edilebilir ve değiştirilebilir kalır. > **Modifier'ların sırası önemlidir** > > Modifier'ların uygulanma sırası görsel sonucu etkiler. `padding`, `background`'tan önce gelirse içerik ile arka plan arasında boşluk oluşur; ters sıra ise arka planın etrafına padding ekler. ## Environment ile uyarlanabilir modifier'lar ViewModifier'lar, bağlama uyum sağlamak için SwiftUI environment değerlerini okuyabilir: koyu mod, erişilebilir metin boyutu, yön ve daha fazlası. ```swift // AdaptiveCardModifier.swift struct AdaptiveCardModifier: ViewModifier { // Reading environment values @Environment(\.colorScheme) private var colorScheme @Environment(\.dynamicTypeSize) private var dynamicTypeSize @Environment(\.horizontalSizeClass) private var horizontalSizeClass func body(content: Content) -> some View { content .padding(adaptivePadding) .background(backgroundColor) .cornerRadius(Radius.lg) .shadow( color: shadowColor, radius: shadowRadius, x: 0, y: shadowOffset ) } // Padding adapted to text size private var adaptivePadding: CGFloat { switch dynamicTypeSize { case .xSmall, .small, .medium: return Spacing.md case .large, .xLarge: return Spacing.lg default: return Spacing.xl } } // Background color based on theme private var backgroundColor: Color { colorScheme == .dark ? Color(.secondarySystemBackground) : Color(.systemBackground) } // More subtle shadow in dark mode private var shadowColor: Color { colorScheme == .dark ? .clear : .black.opacity(0.1) } private var shadowRadius: CGFloat { colorScheme == .dark ? 0 : 8 } private var shadowOffset: CGFloat { colorScheme == .dark ? 0 : 4 } } extension View { func adaptiveCard() -> some View { modifier(AdaptiveCardModifier()) } } ``` Bu modifier, kullanan view'larda ek kod gerektirmeden sistem tercihlerine otomatik olarak uyum sağlar. ## Tam bir Design System'i organize etmek Yapılandırılmış bir design system, keşif ve bakım kolaylığı için ViewModifier'ları işlevsel kategorilere göre gruplar: ```swift // DesignSystem.swift // MARK: - Namespace for the Design System enum DS { // MARK: Text Styles enum Text { static func title(_ content: some View) -> some View { content.modifier(PageTitleModifier()) } static func section(_ content: some View) -> some View { content.modifier(SectionTitleModifier()) } static func body(_ content: some View) -> some View { content.font(Typography.bodyLarge) } static func caption(_ content: some View) -> some View { content.modifier(SecondaryTextModifier()) } } // MARK: Container Styles enum Container { static func card(_ content: some View) -> some View { content.cardStyle() } static func adaptiveCard(_ content: some View) -> some View { content.adaptiveCard() } } // MARK: Button Styles enum Button { static func primary(_ content: some View, enabled: Bool = true) -> some View { content.primaryButton(isEnabled: enabled) } static func secondary(_ content: some View, enabled: Bool = true) -> some View { content.secondaryButton(isEnabled: enabled) } } // MARK: Effects enum Effect { static func shimmer(_ content: some View) -> some View { content.shimmer() } static func pressable(_ content: some View) -> some View { content.pressable() } } } ``` ### Design System'i kullanmak Bu organizasyon, kendi niyetini belgeleyen ifade gücü yüksek bir kod üretir: ```swift // DesignSystemUsage.swift struct ProfileScreen: View { let user: User @State private var isLoading = false var body: some View { ScrollView { VStack(spacing: Spacing.lg) { // Header with title style Text("Profile") .pageTitle() // User card UserCard(user: user) .adaptiveCard() // Settings section VStack(alignment: .leading, spacing: Spacing.md) { Text("Settings") .sectionTitle() SettingsRow(title: "Notifications") SettingsRow(title: "Privacy") SettingsRow(title: "Appearance") } .adaptiveCard() // Logout button Button("Log Out") { // Action } .secondaryButton() } .padding(Spacing.md) } } } struct UserCard: View { let user: User var body: some View { HStack(spacing: Spacing.md) { AsyncImage(url: user.avatarURL) { image in image .resizable() .aspectRatio(contentMode: .fill) } placeholder: { Circle() .fill(Color.gray.opacity(0.3)) .shimmer() } .frame(width: 60, height: 60) .clipShape(Circle()) VStack(alignment: .leading, spacing: Spacing.xs) { Text(user.name) .font(Typography.titleMedium) Text(user.email) .secondaryText() } Spacer() } } } ``` ## ViewModifier'ları test etmek ViewModifier'lar, doğru davranışı garanti etmek için bağımsız olarak test edilebilir: ```swift // ViewModifierTests.swift import XCTest import SwiftUI final class ViewModifierTests: XCTestCase { func testCardModifierAppliesCorrectCornerRadius() { // Arrange let modifier = CardModifier(cornerRadius: 20, shadowRadius: 4) // Assert XCTAssertEqual(modifier.cornerRadius, 20) } func testBadgeModifierHidesWhenCountIsZero() { // Badge should not display if count = 0 and showZero = false let modifier = BadgeModifier(count: 0, showZero: false) // Verify behavior via snapshot tests or UI tests } func testPressableModifierInitialState() { // Initial state should be not pressed // Testable via ViewInspector or UI tests } } ``` > **Testler için ViewInspector** > > ViewInspector kütüphanesi, birim testlerinde SwiftUI view içeriğini incelemeyi sağlar ve ViewModifier'ların doğrulanmasını kolaylaştırır. ## Sonuç ViewModifier'lar, tutarlı ve sürdürülebilir bir SwiftUI design system oluşturmak için tercih edilen araç olarak öne çıkar. Stilleri, animasyonları ve davranışları yeniden kullanılabilir bileşenlerde kapsülleme yetenekleri, modern iOS uygulamalarının mimarisini dönüştürür. ### SwiftUI Design System kontrol listesi - ✅ Design token'larını tanımlayın (spacing, radius, typography, colors) - ✅ Her stil için atomik ViewModifier'lar oluşturun - ✅ Koşullu modifier'lar için `@ViewBuilder` kullanın - ✅ Etkileşimler için `@State` ile dahili durumu yönetin - ✅ Karmaşık stiller oluşturmak için modifier'ları birleştirin - ✅ Otomatik uyum için environment'ı okuyun - ✅ Modifier'ları net bir namespace içinde organize edin - ✅ Akıcı sözdizimi için View extension'ları sağlayın - ✅ Modifier'ları bağımsız olarak test edin - ✅ Kullanımı somut örneklerle belgeleyin --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/ios/swiftui-viewmodifiers-patterns-design-system