Sobes.tech
Middle

Ako systém určuje, na ktorú zobrazenie sa dotyk vzťahuje?

sobes.tech AI

Odpoveď od AI

Systém využíva dva kľúčové mechanizmy: hit-testovanie (testovanie zásahu) a hierarchiu pohľadov.

  1. Hit-testovanie (Hit-testing): Framework UIKit používa metódu hitTest(_:with:) koreňového pohľadu (UIWindow) na určenie, ktorý pohľad je pod bodom dotyku. Táto metóda rekurzívne prechádza hierarchiu pohľadov, začínajúc od koreňa.

  2. Hierarchia pohľadov:

    • Metóda hitTest(_:with:) najskôr skontroluje, či je bod dotyku vo vnútri hraníc aktuálneho pohľadu. Ak nie, vráti nil.
    • Ak áno, metóda prehľadáva deti pohľadu v opačnom poradí (od posledného pridaného k prvému). Pre každé dieťa sa volá jeho vlastná hitTest(_:with:) metóda so rovnakým bodom dotyku (prevedeným do súradníc dieťaťa).
    • Prvé dieťa, ktoré hitTest(_:with:) vráti výsledok odlišný od nil (t.j. obsahuje bod), sa považuje za "zasiahnuté" a tento výsledok sa vracia nahor v hierarchii.
    • Ak žiadne dieťa neobsahuje bod, a aktuálny pohľad ho obsahuje a nemá userInteractionEnabled = false, systém považuje, že dotyk sa stal v aktuálnom pohľade, a metóda vracia self.
    • Ak je userInteractionEnabled aktuálneho pohľadu false, alebo hidden je true, alebo alpha je menšie ako 0.01, metóda vracia nil, aj keď je bod vo vnútri hraníc.

Proces pokračuje nadol hierarchiou, kým sa nenájde najnižší pohľad obsahujúci bod dotyku. Tento pohľad sa stáva hit-test view alebo hit-test result, a jemu (alebo jeho najbližšiemu predkovi) bude delegované ďalšie riadenie udalosti (touchesBegan, touchesMoved atď.).

Tu je, ako možno prepísať hitTest alebo point(inside:with:) pre vlastné správanie hit-testovania:

import UIKit

class CustomView: UIView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        // Kontrolujeme, či je bod vo vnútri nášho pohľadu.
        if !self.point(inside: point, with: event) {
            return nil
        }

        // Prechádzame deti v opačnom poradí.
        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 {
        // Základná implementácia kontroluje, či je bod vo vnútri bounds a či je pohľad interaktívny.
        let extendedBounds = bounds.insetBy(dx: -20, dy: -20) // Rozširuje hranice o 20pt
        return extendedBounds.contains(point)
    }
}