Sobes.tech
Middle

Ինչպես է համակարգը որոշում, թե որ տեսարանն է հպում:

sobes.tech AI

Պատասխան AI-ից

Սիստեմը օգտագործում է երկու հիմնական մեխանիզմ՝ հիթ-տեստավորում (hit-testing) և դիտման հիերարխիա:

  1. Հիթ-տեստավորում (Hit-testing): UIKit ֆրեյմվորկը օգտագործում է hitTest(_:with:) մեթոդը UIWindow-ի հիմնական դիտման համար՝ որոշելու, թե որ դիտը գտնվում է հպման կետում: Այս մեթոդը ռեկուրսիվ անցնում է դիտների հիերարխիան՝ սկսած հիմնականից:

  2. Դիտների հիերարխիա:

    • hitTest(_:with:) մեթոդը առաջին հերթին ստուգում է, արդյոք հպման կետը գտնվում է ներկայիս դիտի սահմաններում: Եթե ոչ, վերադարձնում է nil:
    • Եթե հպման կետը ներսում է, այն անցնում է երեխաների դիտների վրա հակառակ հերթականությամբ (վերջին ավելացվածից առաջինին): Յուրաքանչյուր երեխայի համար կանչում է իր hitTest(_:with:) մեթոդը նույն հպման կետով (այն փոխակերպելով երեխայի կոորդինատների համակարգին):
    • Առաջին երեխա, որի hitTest(_:with:) մեթոդը վերադարձնում է ոչ-nil արդյունք, համարվում է "հարվածված" և այդ արդյունքը վերադառնում է վերև:
    • Եթե ոչ մի երեխա չի պարունակում հպման կետը, իսկ ներկայիս դիտը պարունակում է այն և userInteractionEnabled հավասար է true, ապա համակարգը համարում է, որ հպումը տեղի է ունեցել ներկայիս դիտում, և մեթոդը վերադարձնում է self:
    • Եթե userInteractionEnabled ներկայիս դիտում հավասար է false, կամ hidden հավասար է true, կամ alpha փոքր է 0.01-ից, մեթոդը վերադարձնում է nil, նույնիսկ եթե կետը ներսում է:

Ընթացքը շարունակվում է հիերարխիայով ներքև՝ մինչև գտնվի ամենաթույլ դիտը, որը պարունակում է հպման կետը: Այս դիտը դառնում է hit-test view կամ hit-test result, և հենց նրան (կամ նրա ամենամոտ համապատասխան նախորդին) կհանձնվի հետագա կառավարման միջոցառումը (մեթոդներ touchesBegan, touchesMoved և այլն):

Ինչպես կարելի է վերագրել hitTest կամ point(inside:with:) հարմարեցված վարքագծի համար՝

import UIKit

class CustomView: UIView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        // Ստուգում ենք, արդյոք կետը գտնվում է մեր դիտում ներսում:
        if !self.point(inside: point, with: event) {
            return nil
        }

        // Դիտում ենք երեխաներին հակառակ հերթականությամբ:
        for subview in subviews.reversed() {
            let convertedPoint = subview.convert(point, from: self)
            if let hitTestView = subview.hitTest(convertedPoint, with: event) {
                return hitTestView
            }
        }
        // Եթե ոչ մեկը չի պարունակում կետը, վերադարձնում ենք self:
        return self
    }

    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        // Ստանդարտը ստուգում է, արդյոք կետը ներսում է bounds-ի և ակտիվ է:
        // return super.point(inside: point, with: event)

        // Կարող ենք վերագրել հարմարեցված ձևով՝
        let extendedBounds = bounds.insetBy(dx: -20, dy: -20) // 20 պիքսել ընդլայնում
        return extendedBounds.contains(point)
    }
}

point(inside:with:) մեթոդը կանչվում է hitTest(_:with:)-ից՝ արագ ստուգելու համար, արդյոք կետը בכלל գտնվում է դիտի տարածքում՝ նախքան երեխաներին ստուգելը:

Վերջում գտնում ենք ամենաթույլ ինտերակտիվ դիտը, որի գեոմետրիան պարունակում է հպման կետը և որի userInteractionEnabledtrue, hiddenfalse, և alpha-ն ավելի մեծ է 0.01-ից: