Swipe: มันคืออะไร การจัดการการปัดในการพัฒนาแอปมือถือ

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-02-27 เวลาอ่าน: 8 นาที
Swipe (การปัด) เป็นท่าทางแบบไม่ต่อเนื่องที่ผู้ใช้ใช้นิ้วปัดบนหน้าจอในทิศทางเดียว — ขวา ซ้าย บน หรือล่าง ต่างจากการลาก (drag) การปัดจะไม่ติดตามการเคลื่อนไหวต่อเนื่อง: มันจะถูกรู้จักเป็นการกระทำที่เสร็จสมบูรณ์หลังจากนิ้วผ่านเกณฑ์ขั้นต่ำและยกขึ้นจากหน้าจอ จากการศึกษาด้าน UX ของ Nielsen Norman Group (2024) การปัด เป็นท่าทางที่พบบ่อยเป็นอันดับสองในแอปมือถือรองจากการแตะ โดยใช้ใน 23% ของการโต้ตอบของผู้ใช้

ประเด็นสำคัญ

  • การปัดเป็นท่าทางแบบไม่ต่อเนื่อง: ถูกรู้จักเป็นการกระทำที่เสร็จสมบูรณ์หลังจากผ่านเกณฑ์และยกนิ้ว
  • ใน iOS การจัดการการปัดทำผ่าน UISwipeGestureRecognizer พร้อมการตั้งค่าทิศทางและจำนวนนิ้ว
  • ใน Android ใช้ GestureDetector กับ onFling(MotionEvent) หรือ OnSwipeTouchListener สำหรับ API ที่เรียบง่าย
  • เกณฑ์การปัดคือ ~20px บน iOS (viewConfiguration) และกำหนดค่าได้ผ่าน VelocityTracker ใน Android
  • สำหรับรายการ (UITableView/RecyclerView) ใช้กลไกในตัว — UISwipeActionsConfiguration และ ItemTouchHelper — แทนการรู้จำด้วยตนเอง

การปัดคืออะไร?

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

ระบบปฏิบัติการมือถือตรวจจับการปัดตามพารามิเตอร์สามตัว: ระยะทางขั้นต่ำ (เกณฑ์) ความเร็วขั้นต่ำ (velocity) และทิศทาง หากนิ้วเคลื่อนที่ช้าหรือระยะทางไม่เพียงพอ ระบบจะแปลความหมายท่าทางเป็นการเลื่อนหรือการแตะ ค่าเกณฑ์แตกต่างกันระหว่างแพลตฟอร์ม: iOS ใช้ค่าคงที่ใน UISwipeGestureRecognizer พร้อมเกณฑ์คงที่ Android ใช้ ViewConfiguration.getScaledTouchSlop() ซึ่งส่งคืน ~8–16 dp ขึ้นอยู่กับความหนาแน่นของหน้าจอ

ตาม เอกสารสำหรับนักพัฒนา Android onFling (การปัด) ใน GestureDetector จะทำงานที่ความเร็วเกิน 100 px/s และระยะทางตั้งแต่ 50px ที่ IT Sectr เราใช้การปัดเพื่อการนำทาง (แกลเลอรี อินเทอร์เฟซการ์ด การเริ่มต้นใช้งาน) — ท่าทางนี้ใช้งานง่ายสำหรับผู้ใช้ทุกวัย

การปัด vs การเลื่อน: ต่างกันอย่างไร?

การปัดและการเลื่อน (pan/drag) เป็นท่าทางคนละประเภท แม้ว่าทั้งสองจะเกี่ยวข้องกับการขยับนิ้วบนหน้าจอ การปัดเป็นแบบไม่ต่อเนื่องและหมายถึง "ดำเนินการหนึ่งอย่าง" ในขณะที่การเลื่อนเป็นแบบต่อเนื่องและหมายถึง "เคลื่อนย้ายวัตถุตราบใดที่นิ้วยังอยู่บนหน้าจอ"

พารามิเตอร์ การปัด (Swipe) การเลื่อน/ลาก (Pan/Drag)
ประเภท ไม่ต่อเนื่อง ต่อเนื่อง
เหตุการณ์ เดี่ยว (รู้จัก/ไม่รู้จัก) หลายรายการ (.began → .changed → .ended)
ความเร็ว สูง (เคลื่อนที่เร็ว) ใดก็ได้ (เคลื่อนที่ช้า)
ตัวอย่าง iOS UISwipeGestureRecognizer UIPanGestureRecognizer
ตัวอย่าง Android GestureDetector.onFling() GestureDetector.onScroll()
สถานการณ์ ลบอีเมล พลิกหน้า เคลื่อนย้ายแผนที่ ลากไฟล์
การติดตามพิกัด ไม่ (เฉพาะทิศทาง) ใช่ (อัปเดตพิกัดต่อเนื่อง)

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

การปัดใน iOS: UISwipeGestureRecognizer

UISwipeGestureRecognizer เป็นคลาสย่อยของ UIGestureRecognizer ที่รู้จักการปัดแบบไม่ต่อเนื่องในหนึ่งในสี่ทิศทาง ต่างจาก UIPanGestureRecognizer ตรงที่ไม่สร้างเหตุการณ์ .changed ระหว่างกลาง: เมื่อรู้จักแล้ว การกระทำจะถูกเรียกเพียงครั้งเดียว

คุณสมบัติ direction (UISwipeGestureRecognizer.Direction) กำหนดทิศทางที่ติดตาม — .left, .right, .up, .down สามารถรวมทิศทางผ่านมาสก์: [.left, .right] สำหรับการปัดแนวนอน คุณสมบัติ numberOfTouchesRequired กำหนดจำนวนนิ้ว — ค่าเริ่มต้นคือ 1 UISwipeGestureRecognizer ไม่มีเกณฑ์ระยะทางที่กำหนดค่าได้ — iOS ใช้ค่าในตัวประมาณ 20px ซึ่งเพียงพอสำหรับการรู้จำที่เชื่อถือได้

เนื่องจาก UISwipeGestureRecognizer เป็นตัวรู้จำแบบไม่ต่อเนื่อง มันจึงไม่ขัดแย้งกับ UIPanGestureRecognizer: การเคลื่อนที่ช้าจะเรียก pan การเคลื่อนที่เร็วจะเรียก swipe อย่างไรก็ตาม เมื่ออยู่ร่วมกับ UITapGestureRecognizer ในวิวเดียวกัน อาจต้องใช้ require(toFail:) เพื่อป้องกันการรู้จำการแตะที่ผิดพลาด

การปัดใน Android: GestureDetector

ใน Android API หลักสำหรับการรู้จำการปัดคือ GestureDetector พร้อม callback OnGestureListener โดยเฉพาะเมธอด onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) GestureDetector วิเคราะห์ตำแหน่งเริ่มต้นและสุดท้ายของนิ้ว ความเร็วการเคลื่อนที่ตามแกน และระยะทาง จากนั้นเรียก onFling เมื่อเกินเกณฑ์

เกณฑ์ขั้นต่ำกำหนดค่าได้ผ่าน ViewConfiguration: VelocityTracker.getMinVelocity() (~100 px/s) และ ViewConfiguration.getScaledTouchSlop() (~8–16 dp) OnSwipeTouchListener แบบกำหนดเองเป็นรูปแบบที่พบได้ทั่วไปซึ่งทำให้ API ง่ายขึ้น: นักพัฒนาไม่ต้องคำนวณทิศทางและความเร็วด้วยตนเอง Android Support Library ไม่มี "SwipeDetector" ในตัว ดังนั้น wrapper แบบกำหนดเองจึงเป็นวิธีแก้ปัญหามาตรฐาน

สำหรับการปัดในรายการ Google แนะนำให้ใช้ ItemTouchHelper.SimpleCallback จาก RecyclerView ซึ่งสรุปการตรวจจับท่าทาง แอนิเมชัน และ callback onSwiped() ที่ IT Sectr เราใช้ SwipeListener แบบกำหนดเองสำหรับ ViewPager2 และอินเทอร์เฟซการ์ด — ซึ่งให้การควบคุมความไวในการปัดโดยไม่ลดประสิทธิภาพ

การปัดในรายการ: SwipeActions และ ItemTouchHelper

สำหรับรายการ (UITableView, RecyclerView) Apple และ Google มี API เฉพาะทาง ที่แทนที่การเพิ่ม UISwipeGestureRecognizer ด้วยตนเอง API เหล่านี้รองรับปุ่มการกระทำ แอนิเมชันแบบกำหนดเอง และท่าทาง "ปัดเพื่อลบ" — รูปแบบมาตรฐานในแอปพลิเคชันมือถือ

ใน iOS (11+) ใช้ UISwipeActionsConfiguration: เมธอด tableView(_:leadingSwipeActionsConfigurationForRowAt:) ส่งคืนอาร์เรย์ของ UIContextualAction — ปุ่มที่มีชื่อเรื่อง รูปแบบ (destructive, normal) และตัวจัดการ ตัวอย่างเช่น "ลบ" — .destructive พื้นหลังสีแดง "เก็บถาวร" — .normal พื้นหลังสีเทา ระบบจะสร้างแอนิเมชันการปรากฏของปุ่มและเรียกบล็อก completion หลังจากแตะ

ใน Android ItemTouchHelper.SimpleCallback แนบกับ RecyclerView ผ่าน ItemTouchHelper.attachToRecyclerView() พารามิเตอร์รวมถึงธงทิศทาง (start, end, up, down) และ callback onSwiped(viewHolder, direction) ภายใน onSwiped จะเรียก adapter.notifyItemRemoved(position) เพื่อลบรายการด้วยแอนิเมชัน ItemTouchHelper จัดการท่าทาง การขัดแย้งกับการเลื่อน และการคืนรายการโดยอัตโนมัติหากการปัดไม่สมบูรณ์ ตาม Google I/O 2024 ItemTouchHelper ถูกใช้ใน 70% ของแอป Android ที่มีการปัดในรายการ

ตัวอย่างโค้ดใน Swift และ Kotlin

ตัวอย่าง 1: ปัดซ้ายบน iOS (Swift)

เพิ่ม UISwipeGestureRecognizer ให้กับวิวเพื่อกลับไปยังหน้าจอก่อนหน้า โค้ดแสดงการกำหนดค่าตัวรู้จำขั้นต่ำด้วยทิศทาง .left

swift
import UIKit

class CardViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let swipeLeft = UISwipeGestureRecognizer(
            target: self,
            action: #selector(handleSwipeLeft(_:))
        )
        swipeLeft.direction = .left
        swipeLeft.numberOfTouchesRequired = 1
        view.addGestureRecognizer(swipeLeft)
    }

    @objc private func handleSwipeLeft(_: UISwipeGestureRecognizer) {
        guard let nav = navigationController else { return }
        if nav.viewControllers.count > 1 {
            nav.popViewController(animated: true)
        } else {
            nav.dismiss(animated: true)
        }
    }
}

ตัวรู้จำแนบกับวิวรากของคอนโทรลเลอร์ การตรวจสอบ navigationController.viewControllers.count ทำให้แน่ใจว่า pop จะดำเนินการก็ต่อเมื่อมีหน้าจอก่อนหน้าในสแตก สำหรับคอนโทรลเลอร์แบบ modal ใช้ dismiss โค้ดไม่ต้องการการตรวจสอบสถานะ — UISwipeGestureRecognizer เป็นแบบไม่ต่อเนื่องและเรียกการกระทำเมื่อรู้จำสำเร็จเท่านั้น

ตัวอย่าง 2: การปัดใน Android (Kotlin) ด้วย OnSwipeTouchListener แบบกำหนดเอง

ใช้ wrapper GestureDetector อย่างง่ายที่กำหนดทิศทางการปัดและเรียก callback onSwipeLeft/Right

kotlin
import android.content.Context
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View

open class OnSwipeTouchListener(context: Context) : View.OnTouchListener {

    private val gestureDetector = GestureDetector(context, GestureListener())

    override fun onTouch(v: View, event: MotionEvent): Boolean {
        return gestureDetector.onTouchEvent(event)
    }

    private inner class GestureListener : GestureDetector.SimpleOnGestureListener() {

        companion object {
            private const val SWIPE_THRESHOLD = 100
            private const val SWIPE_VELOCITY_THRESHOLD = 100
        }

        override fun onFling(
            e1: MotionEvent?,
            e2: MotionEvent,
            velocityX: Float,
            velocityY: Float
        ): Boolean {
            val diffX = e2.x - e1!!.x
            val diffY = e2.y - e1.y

            if (Math.abs(diffX) > Math.abs(diffY)) {
                if (Math.abs(diffX) > SWIPE_THRESHOLD
                    && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD
                ) {
                    if (diffX > 0) onSwipeRight()
                    else onSwipeLeft()
                    return true
                }
            } else {
                if (Math.abs(diffY) > SWIPE_THRESHOLD
                    && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD
                ) {
                    if (diffY > 0) onSwipeDown()
                    else onSwipeUp()
                    return true
                }
            }
            return false
        }
    }

    open fun onSwipeLeft() {}
    open fun onSwipeRight() {}
    open fun onSwipeUp() {}
    open fun onSwipeDown() {}
}

OnSwipeTouchListener ขยาย SimpleOnGestureListener โดยใช้ onFling diffX/diffY กำหนดทิศทาง SWIPE_THRESHOLD (100px) และ SWIPE_VELOCITY_THRESHOLD (100 px/s) คือเกณฑ์ การใช้งาน: view.setOnTouchListener(OnSwipeTouchListener(context).apply { onSwipeLeft = { dismiss() } })

ตัวอย่าง 3: SwipeActions ใน iOS (UITableView)

เพิ่มปุ่มลบเมื่อปัดซ้ายบนเซลล์ตาราง ใช้ API ในตัวของ iOS 11+ โดยไม่ต้องรู้จำท่าทางด้วยตนเอง

swift
func tableView(
    _ tableView: UITableView,
    trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath
) -> UISwipeActionsConfiguration? {
    let deleteAction = UIContextualAction(
        style: .destructive,
        title: "ลบ"
    ) { _, _, completionHandler in
        items.remove(at: indexPath.row)
        tableView.deleteRows(at: [indexPath], with: .automatic)
        completionHandler(true)
    }
    deleteAction.backgroundColor = .systemRed

    let config = UISwipeActionsConfiguration(actions: [deleteAction])
    config.performsFirstActionWithFullSwipe = true
    return config
}

เมธอดส่งคืน UISwipeActionsConfiguration พร้อมอาร์เรย์ของ UIContextualAction style: .destructive กำหนดพื้นหลังสีแดงโดยอัตโนมัติ performsFirstActionWithFullSwipe = true อนุญาตให้ดำเนินการด้วยการปัดเต็มโดยไม่ต้องแตะเพิ่มเติม ที่ IT Sectr เราใช้ API นี้สำหรับทุกรายการที่มีการกระทำ — เป็นมาตรฐาน รองรับ VoiceOver และไม่ต้องใช้ GestureRecognizer แบบกำหนดเอง

คำถามที่พบบ่อย

จะแยกการปัดออกจากการเลื่อนได้อย่างไร?

การปัดเป็นท่าทาง ไม่ต่อเนื่อง ที่มีการเคลื่อนที่เร็วและยกนิ้ว การเลื่อนคือการลากต่อเนื่องที่มีการติดตามพิกัด UISwipeGestureRecognizer ทำงานครั้งเดียว UIPanGestureRecognizer สร้าง .began → .changed → .ended การเลือกขึ้นอยู่กับสถานการณ์: การปัดสำหรับการกระทำ (ลบ พลิก) pan สำหรับการเคลื่อนที่ (แผนที่ ตัวเลื่อน)

การปัดทำงานเหมือนกันบน iOS และ Android หรือไม่?

ตรรกะของท่าทางเหมือนกัน แต่ API ต่างกัน iOS ใช้ UISwipeGestureRecognizer กับ direction Android ใช้ GestureDetector.onFling() กับเกณฑ์แบบกำหนดเอง ผลลัพธ์คือ UX เดียวกันกับการใช้งานที่ต่างกัน เฟรมเวิร์กข้ามแพลตฟอร์ม (Flutter, React Native) สรุปความแตกต่างผ่าน API การปัดแบบรวม

จะจัดการการปัดในรายการอย่างไร?

ใน iOS (11+) ใช้ UISwipeActionsConfiguration ใน UITableViewDelegate ใน Android ใช้ ItemTouchHelper.SimpleCallback สำหรับ RecyclerView API เหล่านี้แทนที่ GestureRecognizer แบบกำหนดเอง รองรับปุ่มการกระทำและแอนิเมชัน จัดการการขัดแย้งกับการเลื่อนอย่างถูกต้อง และปรับให้เข้ากับ HIG ของแพลตฟอร์ม

สรุป

  • การปัดเป็นท่าทางการเลื่อนนิ้วเร็วแบบไม่ต่อเนื่อง พบบ่อยเป็นอันดับสองรองจากการแตะ (23% ของการโต้ตอบ)
  • ใน iOS UISwipeGestureRecognizer จัดการการปัดในสี่ทิศทางด้วยเกณฑ์คงที่ ~20px
  • ใน Android ใช้ GestureDetector.onFling() กับเกณฑ์ความเร็วและระยะทางแบบกำหนดเอง
  • สำหรับรายการ ใช้ UISwipeActionsConfiguration (iOS) และ ItemTouchHelper (Android) แทนตัวรู้จำแบบกำหนดเอง
  • การปัดแตกต่างจากการเลื่อนในเรื่องความไม่ต่อเนื่องและความเร็วสูง — อย่าสับสนท่าทางเหล่านี้เมื่อออกแบบ
  • OnSwipeTouchListener แบบกำหนดเองใน Android ทำให้ GestureDetector API ง่ายขึ้นโดยซ่อนการคำนวณทิศทาง
  • ที่ IT Sectr เราใช้การปัดสำหรับการนำทาง การลบรายการ และการเริ่มต้นใช้งาน — ท่าทางใช้งานง่ายและไม่ต้องเรียนรู้

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

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

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

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