@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 ที่เชื่อมโยงสถานะโฟกัสกับช่องป้อนข้อมูลที่เฉพาะเจาะจงหรือองค์ประกอบที่สามารถโฟกัสได้อื่นๆ ใน SwiftUI แตกต่างจาก UIKit ที่การจัดการโฟกัสเกิดขึ้นผ่านเมธอด becomeFirstResponder และ resignFirstResponder SwiftUI ใช้แนวทางแบบประกาศ: คุณประกาศสถานะ (@FocusState) และเชื่อมโยงกับองค์ประกอบผ่านตัวปรับแต่ง .focused() การเปลี่ยนสถานะจะเปลี่ยนโฟกัสโดยอัตโนมัติ

ก่อนที่ @FocusState จะถูกนำเสนอใน iOS 15 นักพัฒนาต้องสร้าง wrapper UIViewRepresentable รอบ UITextField หรือใช้ไลบรารีของบุคคลที่สาม @FocusState ถูกผสานรวมโดยตรงใน SwiftUI และทำงานกับ TextField, TextEditor, SecureField และ SearchField ทำให้โค้ดสะอาดขึ้น ลดจำนวนสะพาน UIKit และปรับปรุงความสามารถในการทดสอบ

ตาม WWDC Session 10136 — What's new in SwiftUI (2024) @FocusState ใช้ระบบ preference keys ของ 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 แบบทางเลือกสอดคล้องอยู่แล้ว) หากคุณกำลังพยายามใช้โครงสร้างที่กำหนดเอง ตรวจสอบให้แน่ใจว่ามัน implement Hashable

  • ลืมตัวปรับแต่ง .focused() — @FocusState เพียงอย่างเดียวไม่สามารถจัดการโฟกัสได้ คุณต้องผูกมันกับช่องผ่าน .focused($property) หรือ .focused($property, equals: .case)
  • หลาย @FocusState ใน View เดียว — สำหรับหลายช่อง ให้ใช้ @FocusState เดียวกับ enum ไม่ใช่หลายคุณสมบัติ @FocusState คุณสมบัติ Bool หลายตัวจะไม่ซิงโครไนซ์กัน
  • การเปลี่ยน @FocusState นอกเธรดหลัก — @FocusState ควรเปลี่ยนบนเธรดหลักเท่านั้น เช่นเดียวกับคุณสมบัติ UI ทั้งหมดใน SwiftUI การดำเนินการแบบอะซิงโครนัสต้องสลับไปยัง MainActor ก่อนที่จะเปลี่ยน
  • การรีเซ็ตโฟกัสเมื่อสร้าง View ใหม่ — หาก View ถูกสร้างใหม่ @FocusState อาจถูกรีเซ็ต ใช้ตัวปรับแต่ง .id() สำหรับการระบุ View ที่เสถียร
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 ที่กำหนดเองได้หรือไม่

ได้ คุณต้อง implement การรองรับ FocusState ใน UIViewRepresentable ที่กำหนดเองผ่านโปรโตคอล UIViewRepresentable View ที่กำหนดเองต้องมี 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 บนนั้น หรือ UIKitBackgroundView ที่กำหนดเอง

จะทำให้แป้นพิมพ์ปรากฏขึ้นพร้อมแอนิเมชันด้วย @FocusState ได้อย่างไร

@FocusState ไม่มี API โดยตรงสำหรับแอนิเมชันแป้นพิมพ์ — นี่คือพฤติกรรมของระบบ iOS อย่างไรก็ตาม คุณสามารถตอบสนองต่อการเปลี่ยนแปลงโฟกัสด้วย .onChange(of: focusedField) หรือ .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) สำหรับแอนิเมชันเนื้อหาแบบกำหนดเอง

สรุป

  • @FocusState — property wrapper ดั้งเดิมของ SwiftUI สำหรับการจัดการโฟกัสการป้อนข้อมูล พร้อมใช้งานตั้งแต่ iOS 15
  • สองโหมด — Bool สำหรับช่องเดียว, enum Hashable สำหรับหลายช่องฟอร์ม
  • ตัวปรับแต่ง .focused() — จำเป็นสำหรับการเชื่อมโยง @FocusState กับช่องป้อนข้อมูลที่เฉพาะเจาะจง
  • การควบคุมโดยทางโปรแกรม — การตั้งค่าเป็น nil หรือ false จะซ่อนแป้นพิมพ์
  • การตรวจสอบฟอร์ม — @FocusState อนุญาตให้ตั้งค่าโฟกัสไปยังช่องแรกที่มีข้อผิดพลาดหลังการตรวจสอบ
  • Enum สำหรับหลายช่อง — @FocusState เดียวกับ enum ดีกว่าคุณสมบัติ Bool หลายตัว
  • iOS 15+ — สำหรับเวอร์ชันเก่า ให้ใช้ UIViewRepresentable กับ becomeFirstResponder

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม