16 ธ.ค. 2568·อ่าน 2 นาที

การตรวจสอบฟอร์ม SwiftUI ให้ความรู้สึกแบบเนทีฟ: การจัดการโฟกัสและข้อผิดพลาด

การตรวจสอบฟอร์มใน SwiftUI ที่ให้ความรู้สึกแบบเนทีฟ: จัดการโฟกัส แสดงข้อผิดพลาดแบบอินไลน์ในเวลาที่เหมาะสม และแสดงข้อความจากเซิร์ฟเวอร์อย่างชัดเจนโดยไม่รบกวนผู้ใช้

การตรวจสอบฟอร์ม SwiftUI ให้ความรู้สึกแบบเนทีฟ: การจัดการโฟกัสและข้อผิดพลาด

การตรวจสอบที่ให้ความรู้สึกแบบเนทีฟใน SwiftUI

ฟอร์ม iOS ที่ให้ความรู้สึกแบบเนทีฟจะมีความสงบ ไม่เถียงกับผู้ใช้ระหว่างพิมพ์ ให้ข้อเสนอแนะชัดเมื่อจำเป็น และไม่ทำให้ผู้ใช้ต้องค้นหาว่าเกิดอะไรผิดพลาด

ความคาดหวังหลักคือความคาดเดาได้ การกระทำแบบเดียวกันควรให้ผลลัพธ์แบบเดียวกันเสมอ ถ้าฟิลด์ไม่ถูกต้อง ฟอร์มควรแสดงในจุดที่สม่ำเสมอ มีน้ำเสียงสม่ำเสมอ และบอกขั้นตอนต่อไปอย่างชัดเจน

ฟอร์มส่วนใหญ่ต้องการกฎอยู่สามประเภท:

  • กฎฟิลด์เดี่ยว: ค่าตัวนี้ถูกต้องหรือไม่ (ว่าง รูปแบบ ความยาว)?
  • กฎข้ามฟิลด์: ค่าต่าง ๆ ตรงกันหรือต้องพึ่งพากัน (รหัสผ่านกับยืนยันรหัสผ่าน)?
  • กฎจากเซิร์ฟเวอร์: ฝั่งแบ็กเอนด์ยอมรับหรือไม่ (อีเมลถูกใช้แล้ว ต้องมีคำเชิญ)?

จังหวะเวลา (timing) สำคัญกว่าคำสวย ๆ การตรวจสอบที่ดีรอเวลาที่เหมาะสม แล้วพูดครั้งเดียวอย่างชัดเจน จังหวะที่ใช้งานได้จริงเป็นดังนี้:

  • เงียบขณะผู้ใช้กำลังพิมพ์ โดยเฉพาะกฎเกี่ยวกับรูปแบบ
  • แสดงข้อเสนอแนะหลังจากออกจากฟิลด์ หรือหลังผู้ใช้แตะส่ง
  • เก็บข้อผิดพลาดให้มองเห็นจนกว่าจะแก้ แล้วลบทันทีเมื่อแก้แล้ว

การตรวจสอบควรเงียบขณะผู้ใช้กำลังตั้งคำตอบ เช่น พิมพ์อีเมลหรือรหัสผ่าน การแสดงข้อผิดพลาดเมื่อพิมพ์ตัวแรกทำให้รู้สึกเหมือนตะคอก ถึงแม้จะถูกตามตรรกะก็ตาม

การตรวจสอบควรปรากฏเมื่อผู้ใช้ส่งสัญญาณว่าทำเสร็จ: โฟกัสย้ายไปที่อื่น หรือพยายามส่ง สิ่งนั้นคือช่วงเวลาที่พวกเขาต้องการคำแนะนำ และคุณสามารถช่วยให้พวกเขาไปยังฟิลด์ที่ต้องแก้ไขได้อย่างแม่นยำ

ปรับจังหวะเวลาให้ถูก แล้วทุกอย่างจะง่ายขึ้น ข้อความอินไลน์จะสั้น โฟกัสที่ย้ายจะรู้สึกช่วยได้ และข้อผิดพลาดจากเซิร์ฟเวอร์จะเหมือนคำแนะนำ ไม่ใช่การลงโทษ

ตั้งค่ารูปแบบสถานะการตรวจสอบอย่างง่าย

ฟอร์มที่ให้ความรู้สึกแบบเนทีฟเริ่มจากการแยกให้ชัด: ข้อความที่ผู้ใช้พิมพ์ไม่ใช่สิ่งเดียวกับความเห็นของแอปเกี่ยวกับข้อความนั้น หากผสมกัน คุณจะโชว์ข้อผิดพลาดเร็วเกินไปหรือสูญเสียข้อความจากเซิร์ฟเวอร์เมื่อ UI รีเฟรช

วิธีง่ายคือให้แต่ละฟิลด์มีสถานะของตัวเองสี่ส่วน: ค่าปัจจุบัน, ว่าผู้ใช้เคยโต้ตอบหรือไม่, ข้อผิดพลาดภายในเครื่อง (local), และข้อผิดพลาดจากเซิร์ฟเวอร์ (ถ้ามี) แล้ว UI จะตัดสินใจแสดงตาม touched และ submitted แทนที่จะตอบสนองต่อทุกปุ่มกด

struct FieldState {
    var value: String = ""
    var touched: Bool = false
    var localError: String? = nil
    var serverError: String? = nil

    // One source of truth for what the UI displays
    func displayedError(submitted: Bool) -> String? {
        guard touched || submitted else { return nil }
        return localError ?? serverError
    }
}

struct FormState {
    var submitted: Bool = false
    var email = FieldState()
    var password = FieldState()
}

กฎเล็ก ๆ บางอย่างช่วยให้คาดเดาได้:

  • แยกข้อผิดพลาดภายในเครื่องและจากเซิร์ฟเวอร์ให้ชัด ข้อผิดพลาดภายใน (เช่น “จำเป็นต้องกรอก” หรือ “อีเมลไม่ถูกต้อง”) ไม่ควรเขียนทับข้อความจากเซิร์ฟเวอร์ เช่น “อีเมลถูกใช้แล้ว”
  • เคลียร์ serverError เมื่อผู้ใช้แก้ไขฟิลด์นั้นอีก เพื่อไม่ให้เขาเผชิญกับข้อความเก่า
  • ตั้ง touched = true เมื่อผู้ใช้ออกจากฟิลด์ (หรือเมื่อคุณตัดสินใจว่าพวกเขาพยายามโต้ตอบ) ไม่ใช่เมื่อพิมพ์ตัวแรก

ด้วยโครงสร้างนี้ view ของคุณสามารถผูกกับ value ได้อย่างอิสระ การตรวจสอบจะอัปเดต localError และเลเยอร์ API จะตั้ง serverError โดยไม่ให้ทั้งสองต่อสู้กัน

การจัดการโฟกัสที่ชี้ทาง ไม่ใช่ตะโกน

การตรวจสอบที่ดีใน SwiftUI ควรรู้สึกว่าแป้นพิมพ์ช่วยผู้ใช้ทำงานให้เสร็จ ไม่ใช่การดุด่า โฟกัสเป็นส่วนสำคัญของเรื่องนี้

แพตเทิร์นคร่าว ๆ คือถือโฟกัสเป็นแหล่งความจริงเดียวโดยใช้ @FocusState กำหนด enum สำหรับฟิลด์ ผูกแต่ละฟิลด์กับมัน แล้วเลื่อนไปข้างหน้าเมื่อผู้ใช้แตะปุ่มบนคีย์บอร์ด

enum Field: Hashable { case email, password, confirm }

@FocusState private var focused: Field?

TextField("Email", text: $email)
  .textContentType(.emailAddress)
  .keyboardType(.emailAddress)
  .textInputAutocapitalization(.never)
  .submitLabel(.next)
  .focused($focused, equals: .email)
  .onSubmit { focused = .password }

SecureField("Password", text: $password)
  .submitLabel(.next)
  .focused($focused, equals: .password)
  .onSubmit { focused = .confirm }

สิ่งที่ทำให้รู้สึกเป็นธรรมชาติคือความยับยั้งชั่งใจ ย้ายโฟกัสเฉพาะเมื่อมีการกระทำที่ชัดเจนของผู้ใช้: แตะ Next, Done หรือปุ่มหลัก เมื่อส่ง ให้โฟกัสไปยังฟิลด์ที่ไม่ผ่านการตรวจสอบตัวแรก (และเลื่อนไปหามันถ้าจำเป็น) อย่าขโมยโฟกัสในขณะที่ผู้ใช้กำลังพิมพ์ แม้ว่าค่าจะไม่ถูกต้องก็ตาม และรักษาความสอดคล้องกับป้ายปุ่มคีย์บอร์ด: Next สำหรับฟิลด์ระหว่างทาง, Done สำหรับฟิลด์สุดท้าย

ตัวอย่างที่พบบ่อยคือ Sign Up ผู้ใช้แตะ Create Account คุณตรวจสอบครั้งหนึ่ง แสดงข้อผิดพลาด แล้วตั้งโฟกัสที่ฟิลด์ที่ผิดพลาดครั้งแรก (มักเป็น Email) ถ้าพวกเขาอยู่ในฟิลด์ Password และยังพิมพ์อยู่ อย่าโยนพวกเขากลับไปที่ Email กลางทาง นั่นคือรายละเอียดเล็ก ๆ ที่มักเป็นตัวแยกระหว่าง “ฟอร์ม iOS ที่ปราณีต” กับ “ฟอร์มที่น่ารำคาญ”

ข้อความอินไลน์ที่ปรากฏในเวลาที่เหมาะสม

ข้อผิดพลาดอินไลน์ควรรู้สึกเหมือนคำแนะนำเงียบ ๆ ไม่ใช่การดุด่า ความแตกต่างใหญ่สุดระหว่าง “แบบเนทีฟ” กับ “น่ารำคาญ” คือเวลาที่คุณแสดงข้อความ

กฎด้านเวลา

ถ้าแสดงข้อผิดพลาดตอนที่คนเพิ่งเริ่มพิมพ์ มันทำให้ขัดจังหวะ กฎที่ดีกว่าคือ: รอจนกว่าผู้ใช้จะมีโอกาสพิมพ์ให้เสร็จ

ช่วงเวลาที่เหมาะสมในการเปิดเผยข้อผิดพลาดอินไลน์:

  • หลังจากฟิลด์สูญเสียโฟกัส
  • หลังผู้ใช้แตะส่ง
  • หลังหยุดพิมพ์ชั่วครู่ (เฉพาะสำหรับการตรวจสอบชัดเจน เช่น รูปแบบอีเมล)

แนวทางที่เชื่อถือได้คือแสดงข้อความเมื่อฟิลด์ถูก touched หรือเมื่อมีการลอง submit แบบฟอร์มใหม่ ฟอร์มใหม่จะเงียบ แต่ผู้ใช้จะได้รับคำแนะนำเมื่อโต้ตอบ

เลย์เอาต์และสไตล์

สิ่งที่ไม่ให้ความรู้สึก iOS เลยคือเลย์เอาต์กระโดดเมื่อแสดงข้อผิดพลาด สำรองที่ว่างสำหรับข้อความ หรือแอนิเมตการปรากฏเพื่อไม่ให้ผลักฟิลด์ถัดไปลงอย่างฉับพลัน

เก็บข้อความข้อผิดพลาดสั้นและเฉพาะเจาะจง มีวิธีแก้ไขเดียวต่อข้อความ เช่น “รหัสผ่านต้องมีอย่างน้อย 8 ตัวอักษร” จะช่วยได้ “ข้อมูลไม่ถูกต้อง” จะไม่ช่วย

ในการสไตล์ เน้นความละเอียดและสม่ำเสมอ ใช้ฟอนต์เล็กใต้ฟิลด์ (เช่น footnote) สีข้อผิดพลาดที่สอดคล้องกัน และไฮไลต์ฟิลด์อย่างอ่อนโยน โดยปกติจะอ่านได้ดีกว่าพื้นหลังหนัก ๆ ลบข้อความเมื่อค่ากลับมาเป็นค่าที่ถูกต้อง

ตัวอย่างสมจริง: ในฟอร์มสมัคร อย่าแสดง “Email is invalid” ขณะที่ผู้ใช้พิมพ์ name@ แสดงมันหลังจากออกจากฟิลด์หรือหลังหยุดพิมพ์ชั่วครู่ และลบทันทีเมื่ออีเมลถูกต้อง

การไหลของการตรวจสอบภายในเครื่อง: พิมพ์ ออกจากฟิลด์ ส่ง

ลดงานย้อนทำเมื่อต้องเปลี่ยน
ลดงานแก้ซ้ำเมื่อความต้องการเปลี่ยนและแอปถูกสร้างซ้ำโดยอัตโนมัติ
เริ่มเลย

การไหลภายในเครื่องที่ดีมีสามความเร็ว: บอกเป็นนัยเบา ๆ ขณะพิมพ์, ตรวจสอบเข้มขึ้นเมื่อออกจากฟิลด์, และกฎเต็มรูปแบบเมื่อส่ง นี่คือจังหวะที่ทำให้การตรวจสอบรู้สึกเป็นธรรมชาติ

ขณะผู้ใช้พิมพ์ ให้การตรวจสอบเบา ๆ และเงียบ คิดว่า “เป็นไปไม่ได้ชัดเจนหรือไม่?” ไม่ใช่ “สมบูรณ์แบบหรือไม่?” สำหรับฟิลด์อีเมล คุณอาจเช็คแค่ว่ามี @ และไม่มีช่องว่าง สำหรับรหัสผ่าน คุณอาจแสดงตัวช่วยเล็ก ๆ เช่น “8+ ตัวอักษร” เมื่อเริ่มพิมพ์ แต่หลีกเลี่ยงการแสดงข้อผิดพลาดสีแดงเมื่อพิมพ์ตัวแรก

เมื่อผู้ใช้ออกจากฟิลด์ ให้รันกฎฟิลด์เดี่ยวที่เข้มขึ้นและแสดงข้อผิดพลาดอินไลน์หากจำเป็น นี่คือที่ที่คำว่า “จำเป็น” และ “รูปแบบไม่ถูกต้อง” ควรปรากฏ เป็นช่วงเวลาที่ดีในการตัดช่องว่างหัวท้ายและปรับรูปแบบ เช่น แปลงอีเมลเป็นตัวพิมพ์เล็ก เพื่อให้ผู้ใช้เห็นสิ่งที่จะถูกส่งจริง

เมื่อส่ง ให้ตรวจสอบทุกอย่างอีกครั้ง รวมถึงกฎข้ามฟิลด์ที่ไม่สามารถตัดสินได้ก่อนหน้านั้น ตัวอย่างคลาสสิกคือรหัสผ่านกับยืนยันรหัสผ่าน ถ้าล้มเหลว ให้ย้ายโฟกัสไปยังฟิลด์ที่ต้องแก้และแสดงข้อความชัดเจนใกล้ ๆ

ใช้ปุ่มส่งอย่างระมัดระวัง ให้ปุ่มเปิดใช้งานขณะผู้ใช้กรอกฟอร์ม อย่าปิดใช้งานจนกว่าการแตะจะไม่มีผล (เช่น ขณะกำลังส่ง) ถ้าคุณปิดใช้งานเพราะข้อมูลไม่ถูกต้อง ให้แสดงว่าต้องแก้อะไรบริเวณใกล้ ๆ หรืออนุญาตให้ส่งแล้วค่อยชี้แนะข้อผิดพลาด

ระหว่างการส่ง ให้แสดงสถานะกำลังโหลดชัดเจน เปลี่ยนป้ายปุ่มเป็น ProgressView ป้องกันการแตะซ้ำ และเก็บฟอร์มไว้เพื่อให้ผู้ใช้เข้าใจสิ่งที่เกิดขึ้น ถ้าคำขอใช้เวลามากกว่าหนึ่งวินาที ให้ป้ายสั้น ๆ เช่น “กำลังสร้างบัญชี...” เพื่อลดความวิตกโดยไม่เพิ่มเสียงรบกวน

การตรวจสอบฝั่งเซิร์ฟเวอร์โดยไม่ทำให้ผู้ใช้หงุดหงิด

การตรวจสอบจากเซิร์ฟเวอร์คือแหล่งความจริงสุดท้าย แม้กฎภายในเครื่องจะแข็งแรง รหัสผ่านอาจผ่านกฎของคุณแต่ล้มเพราะมันทั่วไปเกินไป หรืออีเมลอาจซ้ำกัน

ประโยชน์ด้าน UX ที่ใหญ่ที่สุดคือแยก “ข้อมูลของคุณไม่ยอมรับได้” ออกจาก “เราไม่สามารถติดต่อเซิร์ฟเวอร์ได้” หากคำขอล้มเหลวหรือผู้ใช้ออฟไลน์ อย่าทำให้ฟิลด์เป็นค่าผิด ให้แสดงแถบหรือแจ้งเตือนอย่างใจเย็นเช่น “ไม่สามารถเชื่อมต่อได้ ลองอีกครั้ง” และเก็บฟอร์มไว้เหมือนเดิม

เมื่อเซิร์ฟเวอร์แจ้งว่าการตรวจสอบล้มเหลว ให้เก็บข้อมูลที่ผู้ใช้พิมพ์ไว้และชี้ไปยังฟิลด์ที่เกี่ยวข้อง การล้างฟอร์ม ล้างรหัสผ่าน หรือย้ายโฟกัสทำให้ผู้ใช้รู้สึกถูกลงโทษเมื่อพยายาม

แพตเทิร์นง่ายคือแปลงการตอบกลับข้อผิดพลาดที่มีโครงสร้างเป็นสองถัง: ข้อผิดพลาดตามฟิลด์และข้อผิดพลาดระดับฟอร์ม แล้วอัปเดตสถานะ UI โดยไม่เปลี่ยนการผูกข้อความ

struct ServerValidation: Decodable {
  var fieldErrors: [String: String]
  var formError: String?
}
// Map keys like "email" or "password" to your local field IDs.

สิ่งที่มักให้ความรู้สึกเป็นธรรมชาติ:

  • วางข้อความฟิลด์อินไลน์ ใต้ฟิลด์ โดยใช้คำจากเซิร์ฟเวอร์เมื่อชัดเจน
  • ย้ายโฟกัสไปยังฟิลด์ที่มีข้อผิดพลาดเพียงหลังจากพยายามส่ง ไม่ใช่ระหว่างพิมพ์
  • หากเซิร์ฟเวอร์ส่งปัญหาหลายอย่าง ให้แสดงข้อแรกต่อฟิลด์เพื่อให้อ่านง่าย
  • หากมีรายละเอียดของฟิลด์ อย่าใช้ข้อความหม้อนว่า "เกิดข้อผิดพลาด" เสมอ

ตัวอย่าง: ผู้ใช้ส่งฟอร์มสมัครและเซิร์ฟเวอร์ตอบว่า “email already in use” ให้เก็บอีเมลที่พิมพ์ไว้ แสดงข้อความใต้ Email และโฟกัสที่ฟิลด์นั้น หากเซิร์ฟเวอร์ล่ม ให้แสดงข้อความรีทไรและปล่อยข้อมูลทุกอย่างไว้เหมือนเดิม

จัดวางข้อความจากเซิร์ฟเวอร์ในตำแหน่งที่ถูกต้อง

ออกแบบหน้าจอสไตล์ SwiftUI
ออกแบบหน้าจอสไตล์ SwiftUI ในตัวสร้าง UI และรักษาลำดับโฟกัสให้คาดเดาได้
ดูตัวช่วยสร้าง

ข้อผิดพลาดจากเซิร์ฟเวอร์จะรู้สึกว่า “ไม่ยุติธรรม” เมื่อปรากฏในแบนเนอร์แบบสุ่ม วางข้อความแต่ละอันให้ใกล้กับฟิลด์ที่ทำให้เกิดมันที่สุด ใช้ข้อความรวมเฉพาะเมื่อไม่สามารถผูกกับอินพุตเดียวได้จริง ๆ

เริ่มจากแปลง payload ข้อผิดพลาดของเซิร์ฟเวอร์ให้เป็นตัวระบุฟิลด์ของ SwiftUI UI อาจได้คีย์เช่น email, password, หรือ profile.phone ขณะที่ UI ของคุณใช้ enum เช่น Field.email และ Field.password ทำการแมปครั้งเดียวหลังรับผล เพื่อที่ส่วนที่เหลือของ view จะคงสม่ำเสมอ

วิธีที่ยืดหยุ่นคือเก็บ serverFieldErrors: [Field: [String]] และ serverFormErrors: [String] เก็บเป็นอาร์เรย์แม้โดยปกติจะแสดงข้อความเดียว เมื่อแสดงข้อผิดพลาดอินไลน์ ให้เลือกข้อความที่ช่วยได้มากที่สุด เช่น “Email already in use” มีประโยชน์กว่า “Invalid email” ถ้าทั้งสองปรากฏ

ข้อผิดพลาดหลายข้อต่อฟิลด์เป็นเรื่องปกติ แต่การแสดงทั้งหมดทำให้ว่อน โดยมากแล้วแสดงเพียงข้อความแรกอินไลน์และเก็บที่เหลือไว้ในมุมมองรายละเอียดหากจำเป็น

สำหรับข้อผิดพลาดที่ไม่ผูกกับฟิลด์ (เซสชันหมดอายุ, จำกัดความถี่, "ลองอีกครั้งภายหลัง") วางไว้ใกล้ปุ่มส่งเพื่อให้ผู้ใช้เห็นเมื่อกระทำ นอกจากนี้ต้องแน่ใจว่าเคลียร์ข้อผิดพลาดเก่าเมื่อสำเร็จเพื่อไม่ให้ UI ดู "ติด"

สุดท้าย ให้เคลียร์ข้อผิดพลาดจากเซิร์ฟเวอร์เมื่อลูกค้าเปลี่ยนฟิลด์ที่เกี่ยวข้อง ในการใช้งานจริง onChange สำหรับ email ควรลบ serverFieldErrors[.email] เพื่อให้ UI แสดงทันทีว่า "กำลังแก้ไข"

การรองรับการเข้าถึงและน้ำเสียง: ตัวเลือกเล็ก ๆ ที่ให้ความรู้สึกเนทีฟ

เพิ่มการตรวจสอบข้ามฟิลด์
สร้างการตรวจสอบฟิลด์และข้ามฟิลด์ด้วยการลากแล้ววางตรรกะธุรกิจ
สร้างฟอร์ม

การตรวจสอบที่ดีไม่ใช่แค่ตรรกะ แต่ยังเกี่ยวกับการอ่าน เสียง และพฤติกรรมกับ Dynamic Type, VoiceOver และหลายภาษา

ทำให้ข้อผิดพลาดอ่านง่าย (ไม่ใช่แค่ใช้สี)

สมมติว่าข้อความอาจขยายใหญ่ ใช้สไตล์ที่รองรับ Dynamic Type (เช่น .font(.footnote) หรือ .font(.caption) โดยไม่ตั้งขนาดตายตัว) และให้ป้ายข้อผิดพลาดห่อข้อความได้ เก็บระยะห่างให้สม่ำเสมอเพื่อไม่ให้เลย์เอาต์กระโดดมากเมื่อข้อผิดพลาดปรากฏ

อย่าใช้สีแดงอย่างเดียว เพิ่มไอคอนชัดเจน หรือตัวพรีฟิกซ์อย่าง "Error:" หรือทั้งสอง วิธีนี้ช่วยผู้ที่มีปัญหาการมองเห็นสีและช่วยให้สแกนได้เร็วขึ้น

ชุดเช็คลิสต์รวดเร็วที่มักใช้ได้ดี:

  • ใช้สไตล์ข้อความที่อ่านง่ายและปรับตาม Dynamic Type
  • อนุญาตการห่อข้อความและหลีกเลี่ยงการตัดคำสำหรับข้อความผิดพลาด
  • เพิ่มไอคอนหรือคำว่า “Error:” ร่วมกับสี
  • รักษาคอนทราสต์สูงทั้งในโหมดสว่างและมืด

ทำให้ VoiceOver อ่านสิ่งที่ถูกต้อง

เมื่อฟิลด์ไม่ถูกต้อง VoiceOver ควรอ่านป้ายชื่อ ค่าปัจจุบัน และข้อผิดพลาดรวมกัน หากข้อผิดพลาดเป็น Text แยกใต้ฟิลด์ มันอาจถูกข้ามหรืออ่านนอกบริบท

สองรูปแบบที่ช่วยได้:

  • รวมฟิลด์และข้อผิดพลาดเป็นองค์ประกอบการเข้าถึงเดียว เพื่อให้ข้อผิดพลาดประกาศเมื่อผู้ใช้โฟกัสฟิลด์
  • ตั้ง accessibility hint หรือ value ที่รวมข้อความข้อผิดพลาด (เช่น "Password, required, must be at least 8 characters")

โทนของข้อความสำคัญด้วย เขียนข้อความที่ชัดเจนและแปลได้ง่าย หลีกเลี่ยงสแลง มุกตลก และประโยคคลุมเครือเช่น "Oops" ใช้คำแนะนำเฉพาะเช่น "กรอกอีเมล" หรือ "รหัสผ่านต้องมีตัวเลข"

ตัวอย่าง: ฟอร์มสมัครที่มีทั้งกฎภายในและจากเซิร์ฟเวอร์

สมมติฟอร์มสมัครมีสามฟิลด์: Email, Password และ Confirm Password เป้าหมายคือฟอร์มที่เงียบขณะผู้ใช้พิมพ์ แล้วค่อยให้ความช่วยเหลือเมื่อพวกเขาพยายามไปต่อ

ลำดับโฟกัส (Return ทำอะไร)

ด้วย SwiftUI FocusState แต่ละการกด Return ควรรู้สึกเป็นขั้นตอนธรรมชาติ

  • Return บน Email: ย้ายโฟกัสไป Password
  • Return บน Password: ย้ายโฟกัสไป Confirm Password
  • Return บน Confirm Password: ซ่อนคีย์บอร์ดและพยายามส่ง
  • หากการส่งล้มเหลว: ย้ายโฟกัสไปยังฟิลด์แรกที่ต้องแก้

ขั้นตอนสุดท้ายนั้นสำคัญ หากอีเมลไม่ถูกต้อง โฟกัสควรกลับไปที่ Email ไม่ใช่แค่มีข้อความสีแดงอยู่ที่ไหนสักแห่ง

เมื่อข้อความปรากฏ

กฎง่าย ๆ ช่วยให้ UI สงบ: แสดงข้อความหลังจากฟิลด์ถูก touched (ผู้ใช้ออกจากฟิลด์) หรือหลังจากลองส่ง

  • Email: แสดง “กรอกอีเมลให้ถูกต้อง” หลังออกจากฟิลด์ หรือเมื่อส่ง
  • Password: แสดงกฎ (เช่น ความยาวขั้นต่ำ) หลังออกจากฟิลด์ หรือเมื่อส่ง
  • Confirm Password: แสดง “รหัสผ่านไม่ตรงกัน” หลังออกจากฟิลด์ หรือเมื่อส่ง

ตอนนี้ฝั่งเซิร์ฟเวอร์ สมมติผู้ใช้ส่งและ API ตอบกลับเช่น:

{
  "errors": {
    "email": "That email is already in use.",
    "password": "Password is too weak. Try 10+ characters."
  }
}

สิ่งที่ผู้ใช้เห็น: ข้อความของ Email แสดงใต้ฟิลด์ Email และ Password แสดงใต้ Password Confirm Password เงียบยกเว้นมีปัญหาเฉพาะจากการตรวจสอบภายใน

ผู้ใช้ทำอย่างไรต่อ: โฟกัสไปที่ Email (ข้อผิดพลาดจากเซิร์ฟเวอร์ตัวแรก) พวกเขาแก้ไขอีเมล กด Return ไปที่ Password ปรับรหัสผ่าน แล้วส่งอีกครั้ง เพราะข้อความเป็นอินไลน์และโฟกัสเคลื่อนตามเจตนา ฟอร์มจึงรู้สึกร่วมมือ ไม่ใช่ดุด่า

ข้อผิดพลาดทั่วไปที่ทำให้การตรวจสอบดู “ไม่ใช่ iOS”

สร้างฟอร์มสมัครที่นิ่งและเป็นมิตร
สร้างฟลว์การสมัครด้วย SwiftUI ที่มีการตรวจสอบฝั่งไคลเอนต์และเซิร์ฟเวอร์ รองรับ API จริง
ลอง AppMaster

ฟอร์มอาจถูกต้องตามตรรกะแต่ยังรู้สึกผิด หลายปัญหา "ไม่ใช่ iOS" มาจากจังหวะเวลา: เมื่อคุณแสดงข้อผิดพลาด เมื่อคุณย้ายโฟกัส และวิธีที่คุณตอบสนองต่อเซิร์ฟเวอร์

ข้อผิดพลาดทั่วไปคือพูดก่อนเวลา ถ้าแสดงข้อผิดพลาดเมื่อพิมพ์ตัวแรก ผู้ใช้จะรู้สึกว่าถูกดุด่าในขณะพิมพ์ การรอจนกว่าฟิลด์ถูก touched (ออกจากฟิลด์หรือพยายามส่ง) มักแก้ปัญหานี้ได้

การตอบกลับแบบอะซิงก์จากเซิร์ฟเวอร์อาจทำลายการไหล ถ้าการตอบกลับกลับมาแล้วคุณกระโดดโฟกัสไปฟิลด์อื่น มันจะรู้สึกสุ่ม เก็บโฟกัสไว้ที่ที่ผู้ใช้สุดท้ายอยู่ และย้ายเฉพาะเมื่อพวกเขาแตะ Next หรือเมื่อจัดการการส่ง

กับดักอีกอย่างคือการล้างทุกอย่างเมื่อมีการแก้ไข หากล้างข้อผิดพลาดทั้งหมดทันทีเมื่อมีการพิมพ์ จะซ่อนปัญหาจริง โดยเฉพาะข้อความจากเซิร์ฟเวอร์ ลบเฉพาะข้อผิดพลาดของฟิลด์ที่แก้ และเก็บที่เหลือจนกว่าจะถูกแก้จริง

หลีกเลี่ยงปุ่มส่งที่ "ล้มเหลวเงียบ" ปิดใช้งาน Submit ตลอดไปโดยไม่อธิบายว่าจะต้องแก้อะไร บังคับให้ผู้ใช้เดา หากปิดใช้งาน ให้จับคู่กับคำแนะนำเฉพาะ หรือต้องอนุญาตให้ส่งแล้วค่อยชี้แนะจุดแรกที่ผิด

คำขอช้าและการแตะซ้ำง่ายที่จะพลาด หากไม่แสดงสถานะกำลังโหลดและป้องกันการส่งซ้ำ ผู้ใช้จะกดสองครั้ง ได้คำตอบสองครั้ง และเจอข้อผิดพลาดสับสน

เช็คลิสต์ย่อ:

  • หน่วงการแสดงข้อผิดพลาดจนกว่าจะ blur หรือ submit ไม่ใช่ตัวอักษรแรก
  • อย่าย้ายโฟกัสหลังการตอบจากเซิร์ฟเวอร์เว้นแต่ผู้ใช้เป็นคนขอ
  • เคลียร์ข้อผิดพลาดแยกตามฟิลด์ ไม่ใช่ทั้งหมดทีเดียว
  • อธิบายว่าทำไมส่งถูกบล็อก (หรืออนุญาตให้ส่งพร้อมคำแนะนำ)
  • แสดงการโหลดและเพิกเฉยการแตะเพิ่มขณะที่รอ

ตัวอย่าง: หากเซิร์ฟเวอร์บอกว่า “email already in use” ให้เก็บข้อความใต้ Email ปล่อย Password ไว้ไม่แตะต้อง และให้ผู้ใช้แก้ไข Email โดยไม่ต้องเริ่มฟอร์มใหม่

เช็คลิสต์ด่วนและก้าวต่อไป

ประสบการณ์การตรวจสอบที่ให้ความรู้สึกแบบเนทีฟส่วนใหญ่เกี่ยวกับจังหวะเวลาและความยับยั้งชั่งใจ คุณสามารถมีกฎเข้มงวดและทำให้หน้าจอรู้สึกสงบได้

ก่อนส่งออก ตรวจสอบเหล่านี้:

  • ตรวจสอบในเวลาที่เหมาะสม อย่าแสดงข้อผิดพลาดเมื่อพิมพ์ตัวแรกเว้นแต่ช่วยได้ชัดเจน
  • ย้ายโฟกัสอย่างมีจุดประสงค์ เมื่อส่ง ให้กระโดดไปฟิลด์ที่ผิดและทำให้ชัดว่าต้องแก้อะไร
  • ใช้คำสั้นและชัดเจน บอกว่าต้องทำอะไรต่อ แทนที่จะบอกว่าทำผิดอย่างไร
  • ให้ความสำคัญกับการโหลดและการรีเทรย์ ปิดปุ่มส่งขณะส่ง และเก็บค่าที่พิมพ์ไว้หากคำขอล้มเหลว
  • ถือว่าข้อผิดพลาดจากเซิร์ฟเวอร์เป็นคำติชมของฟิลด์เมื่อเป็นไปได้ แม็ปโค้ดจากเซิร์ฟเวอร์ให้เป็นฟิลด์ และใช้ข้อความบนสุดสำหรับปัญหาระดับกว้างจริง ๆ

จากนั้นทดสอบเหมือนคนจริง ถือโทรศัพท์ด้วยมือเดียวและลองกรอกฟอร์มด้วยนิ้วหัวแม่มือ หลังจากนั้นเปิด VoiceOver และตรวจสอบลำดับโฟกัส การประกาศข้อผิดพลาด และป้ายปุ่มยังใช้ง่ายหรือไม่

เพื่อการดีบักและซัพพอร์ต ช่วยให้บันทึกโค้ดการตรวจสอบจากเซิร์ฟเวอร์ (ไม่ใช่ข้อความดิบ) พร้อมหน้าจอและชื่อฟิลด์ เมื่อผู้ใช้บอกว่า “สมัครไม่ได้” คุณจะรู้ทันทีว่ามันเป็น email_taken, weak_password, หรือ timeout

เพื่อรักษาความสม่ำเสมอทั่วแอป ให้ทำมาตรฐานโมเดลฟิลด์ของคุณ (value, touched, local error, server error), ตำแหน่งข้อผิดพลาด และกฎโฟกัส หากคุณต้องการสร้างฟอร์ม iOS แบบเนทีฟให้เร็วขึ้นโดยไม่ต้องเขียนมือทุกหน้าจอ AppMaster (appmaster.io) สามารถสร้างแอป SwiftUI พร้อมแบ็กเอนด์ ซึ่งช่วยให้กฎการตรวจสอบฝั่งไคลเอนต์และเซิร์ฟเวอร์สอดคล้องกันได้ง่ายขึ้น

ง่ายต่อการเริ่มต้น
สร้างบางสิ่งที่ น่าทึ่ง

ทดลองกับ AppMaster ด้วยแผนฟรี
เมื่อคุณพร้อม คุณสามารถเลือกการสมัครที่เหมาะสมได้

เริ่ม