Sobes.tech
Middle

Hogyan határozza meg a rendszer, hogy melyik nézetben történt az érintés?

sobes.tech MI

Válasz az MI-től

A rendszer két kulcsfontosságú mechanizmust használ: a hit-tesztelést (érintés tesztelés) és a nézethierarchiát.

  1. Hit-tesztelés (Hit-testing): A UIKit keretrendszer a hitTest(_:with:) metódust használja az ablak (UIWindow) gyökérnézetén, hogy meghatározza, melyik nézet található az érintés pontja alatt. Ez a metódus rekurzívan végigjárja a nézethierarchiát, a gyökértől kezdve.

  2. Nézethierarchia:

    • A hitTest(_:with:) elsőként ellenőrzi, hogy az érintés pontja a jelenlegi nézet határain belül van-e. Ha nem, akkor nil-t ad vissza.
    • Ha igen, akkor a nézet gyermekeit fordított sorrendben vizsgálja (az utoljára hozzáadottól az elsőig). Minden gyermekre meghívja a saját hitTest(_:with:) metódusát ugyanazzal a ponttal (átalakítva a gyermek koordinátáira).
    • Az első gyermek, amelyik hitTest(_:with:) eredménye nem nil, az "elkapott" nézetnek számít, és ez az eredmény felfelé kerül visszaadásra.
    • Ha egyik gyermek sem tartalmazza a pontot, és a jelenlegi nézet tartalmazza, valamint userInteractionEnabled értéke true, akkor a rendszer úgy tekinti, hogy az érintés a jelenlegi nézetben történt, és a self értéket adja vissza.
    • Ha a jelenlegi nézet userInteractionEnabled értéke false, vagy hidden true, vagy alpha kisebb, mint 0.01, akkor nil-t ad vissza, még akkor is, ha a pont a határain belül van.

A folyamat lefelé halad a hierarchiában, amíg meg nem találja a legalsó nézetet, amely tartalmazza az érintés pontját. Ez a nézet lesz a hit-test view vagy hit-test eredmény, és neki (vagy legközelebbi szülőjének) delegálódik a további eseménykezelés (touchesBegan, touchesMoved stb.).

Íme, hogyan lehet felülírni a hitTest vagy a point(inside:with:) metódust egyedi viselkedéshez:

import UIKit

class CustomView: UIView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        // Ellenőrizzük, hogy a pont a view belsejében van-e.
        if !self.point(inside: point, with: event) {
            return nil
        }

        // Végigmegyünk a gyermek nézeteken fordított sorrendben.
        for subview in subviews.reversed() {
            let convertedPoint = subview.convert(point, from: self)
            if let hitTestView = subview.hitTest(convertedPoint, with: event) {
                return hitTestView
            }
        }
        return self
    }

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        // Alapértelmezett implementáció ellenőrzi, hogy a pont a határokon belül van-e, és az view interaktív-e.
        let extendedBounds = bounds.insetBy(dx: -20, dy: -20) // Növeli a területet 20 ponttal minden irányban
        return extendedBounds.contains(point)
    }
}

A point(inside:with:) metódus a hitTest(_:with:) által hívódik, hogy gyorsan ellenőrizze, a pont egyáltalán a view területén belül van-e, mielőtt a gyermek nézeteket ellenőrizné.

Végül, megtaláljuk a legalsó, interaktív nézetet a hierarchiában, amely tartalmazza a pontot, és userInteractionEnabled értéke true, hidden false, és alpha nagyobb, mint 0.01, és neki delegáljuk az esemény további kezelését (touchesBegan, touchesMoved stb.).