Sobes.tech
Middle

Cum determină sistemul în ce vizualizare a avut loc atingerea?

sobes.tech AI

Răspuns de la AI

Sistemul utilizează două mecanisme cheie: testarea de atingere (hit-testing) și ierarhia vizualizărilor.

  1. Testarea de atingere (Hit-testing): Framework-ul UIKit folosește metoda hitTest(_:with:) a viziunii rădăcină (UIWindow) pentru a determina care vizualizare se află sub punctul de atingere. Această metodă parcurge recursiv ierarhia vizualizărilor, începând cu rădăcina.

  2. Ierarhia vizualizărilor:

    • Metoda hitTest(_:with:) verifică mai întâi dacă punctul de atingere se află în interiorul limitei vizualizării curente. Dacă nu, returnează nil.
    • Dacă punctul este în interior, metoda examinează vizualizările copil în ordine inversă (de la ultima adăugată la prima). Pentru fiecare vizualizare copil, se apelează propria metodă hitTest(_:with:) cu același punct de atingere (convertit în coordonatele vizualizării copil).
    • Prima vizualizare copil pentru care metoda hitTest(_:with:) returnează un rezultat diferit de nil (adică ea sau unul dintre descendenții săi conține punctul) este considerată "atinsă" și acest rezultat este returnat în sus pe ierarhie.
    • Dacă nici o vizualizare copil nu conține punctul de atingere, iar vizualizarea curentă conține punctul și nu are userInteractionEnabled = false, sistemul consideră că atingerea a avut loc în vizualizarea curentă, și metoda returnează self.
    • Dacă userInteractionEnabled al vizualizării curente este false, sau hidden este true, sau alpha este mai mic decât 0.01, metoda returnează nil, chiar dacă punctul se află în interiorul limitei.

Procesul continuă în jos pe ierarhie până se găsește cea mai joasă vizualizare care conține punctul de atingere. Această vizualizare devine hit-test view sau hit-test result, și anume acesteia (sau părintelui său cel mai apropiat) i se va delega gestionarea ulterioară a evenimentului (metodele touchesBegan, touchesMoved etc.).

Iată cum se poate suprascrie hitTest sau point(inside:with:) pentru comportament personalizat de hit-testing:

import UIKit

class CustomView: UIView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        // Verificăm dacă punctul se află în interiorul vizualizării noastre.
        // Dacă nu, returnăm nil.
        if !self.point(inside: point, with: event) {
            return nil
        }

        // Parcurgem vizualizările copil în ordine inversă adăugării.
        for subview in subviews.reversed() {
            // Convertim punctul din coordonatele părintelui în cele ale vizualizării copil.
            let convertedPoint = subview.convert(point, from: self)

            // Apelăm recursiv hitTest pentru vizualizarea copil.
            if let hitTestView = subview.hitTest(convertedPoint, with: event) {
                // Dacă vizualizarea copil sau descendenții săi conține punctul, returnăm rezultatul.
                return hitTestView
            }
        }

        // Dacă nimeni dintre copiii vizualizării nu a gestionat atingerea,
        // iar vizualizarea curentă este interactivă și conține punctul, returnăm self.
        return self
    }

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        // Implementarea de bază verifică dacă punctul se află în limitele vizualizării
        // și dacă vizualizarea este activă (userInteractionEnabled, alpha > 0.01, hidden == false).
        // return super.point(inside: point, with: event)

        // Poți suprascrie pentru forme personalizate sau margini, de exemplu, pentru a mări zona de atingere
        let extendedBounds = bounds.insetBy(dx: -20, dy: -20) // Mărim zona cu 20pts în toate direcțiile
        return extendedBounds.contains(point)
    }
}

Metoda point(inside:with:) este apelată de hitTest(_:with:) pentru verificarea rapidă dacă punctul se află în interiorul zonei vizualizării, înainte de a verifica vizualizările copil.

În final, se găsește cea mai joasă vizualizare interactivă din ierarhie, a cărei geometrie conține punctul de atingere și care are userInteractionEnabled setat la true, hidden la false și alpha mai mare decât 0.01.