Sobes.tech
Middle

Πώς καθορίζει το σύστημα σε ποια προβολή έγινε η αφή;

sobes.tech AI

Απάντηση από AI

Το σύστημα χρησιμοποιεί δύο βασικούς μηχανισμούς: το hit-testing (δοκιμή επαφής) και την ιεραρχία των views.

  1. Hit-testing (δοκιμή επαφής): Το πλαίσιο UIKit χρησιμοποιεί τη μέθοδο hitTest(_:with:) του ρίζας view (UIWindow) για να καθορίσει ποιο view βρίσκεται κάτω από το σημείο αφής. Αυτή η μέθοδος διατρέχει αναδρομικά την ιεραρχία των views, ξεκινώντας από το ρίζα.

  2. Ιεραρχία views:

    • Η μέθοδος hitTest(_:with:) ελέγχει πρώτα αν το σημείο αφής βρίσκεται εντός των ορίων του τρέχοντος view. Αν όχι, επιστρέφει nil.
    • Αν ναι, η μέθοδος ελέγχει τα παιδιά views με αντίστροφη σειρά (από το τελευταίο που προστέθηκε στο πρώτο). Για κάθε παιδικό view, καλείται η δική του μέθοδος hitTest(_:with:) με το ίδιο σημείο αφής (μετασχηματισμένο στις συντεταγμένες του παιδικού view).
    • Το πρώτο παιδικό view που η hitTest(_:with:) επιστρέφει αποτέλεσμα διαφορετικό από nil (δηλαδή, αυτό ή κάποιο από τα παιδιά του περιέχει το σημείο) θεωρείται "πιασμένο" και το αποτέλεσμα αυτό επιστρέφεται προς τα πάνω στην ιεραρχία.
    • Αν κανένα παιδικό view δεν περιέχει το σημείο αφής, και το τρέχον view περιέχει το σημείο και δεν έχει userInteractionEnabled = false, το σύστημα θεωρεί ότι η αφή συνέβη στο τρέχον view, και η μέθοδος επιστρέφει self.
    • Αν το userInteractionEnabled του τρέχοντος view είναι false, ή το hidden είναι true, ή το alpha είναι μικρότερο από 0.01, η μέθοδος επιστρέφει nil, ακόμα και αν το σημείο βρίσκεται εντός των ορίων.

Η διαδικασία συνεχίζεται προς τα κάτω στην ιεραρχία μέχρι να βρεθεί το πιο κάτω view που περιέχει το σημείο αφής. Αυτό το view γίνεται hit-test view ή hit-test result, και σε αυτό (ή στον πλησιέστερο προγόνου του) θα ανατεθεί η περαιτέρω διαχείριση του γεγονότος (touchesBegan, touchesMoved κ.λπ.).

Ακολουθεί πώς μπορεί να υπερισταλεί η hitTest ή η point(inside:with:) για προσαρμοσμένη συμπεριφορά hit-testing:

import UIKit

class CustomView: UIView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        // Ελέγχει αν το σημείο βρίσκεται εντός του view.
        if !self.point(inside: point, with: event) {
            return nil
        }

        // Διατρέχει τα παιδιά views με αντίστροφη σειρά.
        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 {
        // Βασική υλοποίηση ελέγχει αν το σημείο βρίσκεται εντός των ορίων και αν το view είναι ενεργό.
        let extendedBounds = bounds.insetBy(dx: -20, dy: -20) // Επεκτείνει τα όρια κατά 20pts
        return extendedBounds.contains(point)
    }
}

Η μέθοδος point(inside:with:) καλείται από το hitTest(_:with:) για γρήγορο έλεγχο αν το σημείο βρίσκεται εντός της περιοχής του view, πριν ελεγχθούν τα παιδιά views.

Τελικά, βρίσκουμε το πιο κάτω interactable view στην ιεραρχία, το οποίο περιέχει το σημείο αφής και έχει userInteractionEnabled true, hidden false, και alpha μεγαλύτερο από 0.01, και του ανατίθεται η διαχείριση του γεγονότος (μέθοδοι touchesBegan, touchesMoved κ.λπ.).