@FocusState چیست، مدیریت فوکوس و صفحه کلید در SwiftUI

نویسنده: IT Sectr منتشر شده: 2026-06-26 زمان مطالعه: 9 دقیقه

@FocusState يک property wrapper در SwiftUI است که در iOS 15 معرفی شد و به شما امکان می‌دهد تا به صورت برنامه‌ای فوکوس ورود را در فیلدهای متنی و سایر المان‌ها مدیریت کنید. پیش از ظهور آن، توسعه‌دهندگان مجبور بودند از UIViewRepresentable برای دسترسی به روش‌های UIKit مانند becomeFirstResponder و resignFirstResponder استفاده کنند. @FocusState این مشکل را به شکل ذاتی حل می‌کند: شما یک ویژگی را از طریق مودیفکاتور .focused() به فیلد متصل می‌کنید، پس از آن تنظیم و بازنشستن فوکوس با یک تعیین مقدار ساده انجام می‌شود. به استناد به Apple Developer Documentation — FocusState (2025)، @FocusState از دو حالت پشتیبانی می‌کند: Bool برای مدیریت ساده (فوکوس هست یا نیست) و enum برای چندین فیلد، که هر case مربوط به یک فیلد ورودی خاص است.

اصلی

  • @FocusState — یک property wrapper برای مدیریت برنامه‌ای فوکوس در SwiftUI، قابل دسترس از iOS 15.
  • حالت Bool — برای یک فیلد از @FocusState var isFocused: Bool با .focused($isFocused) استفاده کنید.
  • حالت Enum — برای چندین فیلد از enum با پروتکل FocusStateValue و .focused($field, equals: .fieldName) استفاده کنید.
  • مخفی کردن صفحه کلید — برای مخفی کردن صفحه کلید، فوکوس را بر روی nil یا false قرار دهید.
  • فوکوس خودکار — مقدار اولیه را در .onAppear تنظیم کنید تا صفحه کلید برای باز شدن نمایش داده شود.

@FocusState در SwiftUI چیست

@FocusState یک property wrapper است که وضعیت فوکوس را به یک فیلد ورودی یا المان focusable دیگر در SwiftUI متصل می‌کند. بر خلاف UIKit که مدیریت فوکوس از طریق روش‌های becomeFirstResponder و resignFirstResponder انجام می‌شود، SwiftUI از رویکرد اعلامی استفاده می‌کند: شما وضعیت (@FocusState) را اعلان و آن را از طریق مودیفکاتور .focused() به عنصر متصل می‌کنید. تغییر وضعیت به طور خودکار فوکوس را تغییر می‌دهد.

پیش از ظهور @FocusState در iOS 15، توسعه‌دهندگان مجبور بودند پیچیده‌های UIViewRepresentable در اطراف UITextField ایجاد کنند یا از کتابخانه‌های شخص ثالث استفاده کنند. @FocusState مستقیماً در SwiftUI یکپارچه شده است و با TextField، TextEditor، SecureField و SearchField کار می‌کند. این کد را تمیزتر می‌کند، تعداد پل‌های UIKit را کاهش می‌دهد و آزمون‌پذیری را بهبود می‌بخشد.

به استناد به WWDC Session 10136 — What's new in SwiftUI (2024)، @FocusState از سیستم کلیدهای ترجیح SwiftUI برای انتقال اطلاعات فوکوس بین عناصر استفاده می‌کند. وقتی فیلدی فوکوس را دریافت می‌کند، SwiftUI به طور خودکار ویژگی @FocusState مرتبط را به‌روز می‌کند که به شما امکان را می‌دهد به تغییرات فوکوس در کد واکنش نشان دهید.

مدیریت فوکوس با Bool

ساده‌ترین راه استفاده از @FocusState نوع Bool است. وقتی فیلد در فوکوس است، ویژگی برابر true است. وقتی فوکوس برداشته شود — false. شما می‌توانید با تعیین true فوکوس را به زور تنظیم کنید یا با تعیین false آن را بازنشستید.

swift
struct LoginForm: View {
    @State var email = ""
    @FocusState var isEmailFocused: Bool
    
    var body: some View {
        VStack {
            TextField("Email", text: $email)
                .focused($isEmailFocused)
            
            Button("نمایش صفحه کلید") {
                isEmailFocused = true
            }
            Button("مخفی کردن صفحه کلید") {
                isEmailFocused = false
            }
        }
    }
}

در این مثال، isEmailFocused وقتی کاربر روی فیلد متنی ضربه می‌زند به طور خودکار true و وقتی صفحه کلید مخفی می‌شود false می‌شود. دکمه‌ها به شما امکان مدیریت برنامه‌ای فوکوس را می‌دهند — این برای صفحات کلید سفارشی، دکمه‌های «بعدی» و مواقعی که بعد ارسال فرم باید صفحه کلید را به زور مخفی کنید مفید است.

مدیریت فوکوس با Enum برای چندین فیلد

برای فرم‌های با چندین فیلد، @FocusState از enum مطابق با پروتکل FocusStateValue (یا Hashable) پشتیبانی می‌کند. هر case enum مربوط به یک فیلد خاص است. این به شما امکان جابجایی فوکوس بین فیلدها را می‌دهد — مثلاً با فشردن دکمه «بعدی» در صفحه کلید به فیلد بعدی بروید.

swift
struct RegistrationForm: View {
    enum Field: Hashable {
        case email
        case password
        case confirmPassword
    }
    
    @State var email = ""
    @State var password = ""
    @State var confirmPassword = ""
    @FocusState var focusedField: Field?
    
    var body: some View {
        Form {
            TextField("Email", text: $email)
                .focused($focusedField, equals: .email)
                .onSubmit { focusedField = .password }
            
            SecureField("Password", text: $password)
                .focused($focusedField, equals: .password)
                .onSubmit { focusedField = .confirmPassword }
            
            SecureField("Confirm", text: $confirmPassword)
                .focused($focusedField, equals: .confirmPassword)
                .onSubmit { submitForm() }
        }
    }
}

به مودیفکاتور .onSubmit توجه کنید — وقتی کاربر روی صفحه کلید «Return» را فشار می‌دهد صدا زده می‌شود. در داخل .onSubmit ما focusedField را به فیلد بعدی تغییر می‌دهیم که به طور خودکار فوکوس را منتقل می‌کند. آخرین فیلد برای ارسال فرم، submitForm() را فراخوان می‌کند.

مخفی کردن و نمایش صفحه کلید

@FocusState راهی ساده برای مخفی کردن صفحه کلید فراهم می‌کند — کافیست ویژگی را بر روی nil (برای enum) یا false (برای Bool) قرار دهید. اما گاهی بیدون پیوند به یک فیلد خاص باید صفحه کلید را مخفی کنید — مثلاً وقتی روی مکان خالی ضربه می‌زنید. در این مورد چند رویکرد وجود دارد.

swift
struct DismissKeyboardView: View {
    @State var text = ""
    @FocusState var isFocused: Bool
    
    var body: some View {
        TextField("Enter text", text: $text)
            .focused($isFocused)
            .toolbar {
                ToolbarItemGroup(placement: .keyboard) {
                    Spacer()
                    Button("تمام") {
                        isFocused = false
                    }
                }
            }
    }
}

مودیفکاتور .toolbar با placement .keyboard یک دکمه بالای صفحه کلید اضافه می‌کند. این یک الگوی استاندارد UX در iOS برای مخفی کردن صفحه کلید است. رویکرد جایگزین — استفاده از .onTapGesture بر روی VStack اصلی برای بازنشستن فوکوس با ضربه روی پس‌زمینه.

فوکوس و اعتبارسنجی فرم

@FocusState به خوبی با اعتبارسنجی فرم ترکیب می‌شود. الگوی تیپیکی: پس از فشردن دکمه «ارسال»، همه فیلدها را بررسی کرده و فوکوس را بر اولین فیلدی که خطا دارد قرار دهید. این تجربه کاربر را بهبود می‌بخشد — کاربر بلافاصله می‌بیند کدام فیلد باید اصلاح شود.

swift
struct ValidatedForm: View {
    enum Field: Hashable { case name; case phone }
    
    @State var name = ""
    @State var phone = ""
    @FocusState var focusedField: Field?
    @State var errors: [String] = []
    
    var body: some View {
        Form {
            TextField("Name", text: $name)
                .focused($focusedField, equals: .name)
            TextField("Phone", text: $phone)
                .focused($focusedField, equals: .phone)
            
            Button("ارسال") { validateAndSubmit() }
        }
    }
    
    func validateAndSubmit() {
        if name.isEmpty {
            focusedField = .name
            return
        }
        if phone.isEmpty {
            focusedField = .phone
            return
        }
        // ارسال فرم
    }
}

در این مثال، وقتی فیلد name خالی است، فوکوس به آن منتقل می‌شود و کاربر بلافاصله می‌بیند خطا کجاست. اگر name پر شده باشد، phone بررسی می‌شود. این رفتار طبیعی برای فرم‌هاست — کاربر فیلدها را از بالا به پایین پر می‌کند و اعتبارسنجی هم همین ترتیب را دنبال می‌کند.

اشتباهات رایج با @FocusState

رایج‌ترین اشتباه — تلاش برای استفاده از @FocusState با یک نوع غیر مطابق با Hashable. @FocusState نیاز دارد که نوع ویژگی Hashable باشد (Bool و enumهای اختیاری قبلاً مطابق هستند). اگر سعی می‌کنید از ساختار سفارشی استفاده کنید، مطمئن شوید که Hashable را پیاده‌سازی می‌کند.

  • مودیفکاتور .focused() را فراموش کرده‌اید — @FocusState به تنهایی فوکوس را مدیریت نمی‌کند. باید حتماً آن را از طریق .focused($property) یا .focused($property, equals: .case) به فیلد متصل کنید.
  • چندین @FocusState در یک View — برای چندین فیلد از یک @FocusState با enum استفاده کنید، نه چند @FocusState. چند ویژگی Bool با یکدیگر همگام نخواهند شد.
  • تغییر @FocusState خارج از main thread — @FocusState فقط در تراباصل اصلی تغییر کرده می‌شود، مانند تمام ویژگی‌های UI در SwiftUI. عملیات‌های ناهمگام باید پیش از تغییر به MainActor سویئچ کنند.
  • بازنشستن فوکوس در بازسازی — اگر View بازسازی شود، @FocusState ممکن است بازنشسته شود. برای شناسایی پایدار View از مودیفکاتور .id() استفاده کنید.
swift
// ❌ اشتباه: دو @FocusState Bool به جای enum
@FocusState var isNameFocused: Bool
@FocusState var isEmailFocused: Bool

// ✅ صحیح: یک enum @FocusState
enum Field: Hashable { case name; case email }
@FocusState var focusedField: Field?

سوالات متداول

@FocusState از چه نسخه‌های iOS در دسترس است؟

@FocusState از iOS 15، iPadOS 15، macOS 12، tvOS 15 و watchOS 8 قابل دسترس است. برای پروژه‌هایی که iOS 14 و پایین‌تر را پشتیبانی می‌کنند، از UIViewRepresentable با UITextField و becomeFirstResponder یا کتابخانه‌های شخص ثالث با پیاده‌سازی سفارشی مدیریت فوکوس استفاده کنید.

آیا می‌توان از @FocusState با UIViewRepresentable سفارشی استفاده کرد؟

بله، برای این کار در UIViewRepresentable سفارشی باید پشتیبانی از FocusState را از طریق پروتکل UIViewRepresentable پیاده کنید. نمایش سفارشی باید becomeFirstResponder و resignFirstResponder داشته باشد. SwiftUI به طور خودکار @FocusState را به این روش‌ها متصل می‌کند اگر مودیفکاتور .focused() را مشخص کنید.

چرا @FocusState با TextField در List کار نمی‌کند؟

در List یا Form، سلول‌ها ممکن است مجدداً استفاده شوند که این ارتباط @FocusState را با فیلد می‌شکند. راه حل: مودیفکاتور .id() را با یک شناسه منحصربهفرد برای هر TextField اضافه کنید. مثال: .id(fieldName). این سبب می‌شود SwiftUI برای هر فیلد یک نمونه View جداگانه ایجاد کند.

چگونه با ضربه روی مکان خالی صفحه کلید را مخفی کنیم؟

.onTapGesture را به کانتینر اصلی (VStack، ZStack) اضافه کرده و فوکوس را بازنشستید: focusedField = nil. اما .onTapGesture ممکن است ضربه روی دکمه‌های داخلی را بلوک کند — از کانتینر با .contentShape(Rectangle()) و .onTapGesture روی آن استفاده کنید یا از UIViewBackgroundView سفارشی.

چگونم می‌توان ظهور صفحه کلید را با @FocusState انیمیشن داد؟

@FocusState API مستقیمی برای انیمیشن صفحه کلید ارائه نمی‌دهد — این رفتار سیستمی iOS است. اما شما می‌توانید با .onChange(of: focusedField) یا .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) به تغییرات فوکوس واکنش نشان دهید و انیمیشن سفارشی محتوا انجام دهید.

خلاصه

  • @FocusState — یک property wrapper ذاتی SwiftUI برای مدیریت فوکوس ورود، قابل دسترس از iOS 15.
  • دو حالت — Bool برای یک فیلد، Hashable enum برای چندین فیلد فرم.
  • مودیفکاتور .focused() — برای اتصال @FocusState به یک فیلد ورودی خاص اجباری است.
  • مدیریت برنامه‌ای — تعیین مقدار nil یا false صفحه کلید را مخفی می‌کند.
  • اعتبارسنجی فرم — @FocusState امکان قرار دادن فوکوس روی اولین فیلد خطادار را پس از بررسی فراهم می‌کند.
  • Enum برای چندین فیلد — یک @FocusState با enum به چند ویژگی Bool ترجیح دارد.
  • iOS 15+ — برای نسخه‌های کهنه از UIViewRepresentable با becomeFirstResponder استفاده کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید