Hit Testing

๊ทธ๋Ÿผ ํ„ฐ์น˜๊ฐ€ ์–ด๋””์„œ ๋ฐœ์ƒํ–ˆ๋Š”์ง€๋Š” ์–ด๋–ป๊ฒŒ ์•Œ ์ˆ˜ ์žˆ์„๊นŒ? ์ฆ‰, ํ„ฐ์น˜๊ฐ€ ๋ฐœ์ƒํ•œ ์‹œ์ ์—, ์–ด๋–ป๊ฒŒ ๋‚ด๊ฐ€ ๋ˆ„๋ฅธ ๊ฐ€์žฅ ์ƒ์œ„ ๊ฐ์ฒด(์ƒ๋‹จ์€ view ๊ณ„์ธต์—์„œ top์„ ๋งํ•จ)๋ฅผ ํŒ๋‹จํ•  ์ˆ˜ ์žˆ์„๊นŒ? ์—ฌ๊ธฐ์„œ ์ด๋ฒคํŠธ์— ๋ฐ˜์‘ํ•œ ๊ฐ€์žฅ ์ƒ์œ„ ๊ฐ์ฒด๋ฅผ first Responder๋ผ ํ•œ๋‹ค.

Hit Test ์‹คํ–‰ ์‹œ์ 

๊ทธ๋ฆผ์ด ์–ด๋ ค์šธ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•ด๋ณด๊ฒ ๋‹ค. ์œ„์˜ ๊ทธ๋ฆผ์„ ๋ณด๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • View ์š”์†Œ๋ฅผ ์„ ํƒํ•˜๊ณ , ์ด๋™ํ•œ ๋’ค, ๋–ผ๋Š” ์ž‘์—…์„ ํ•˜๊ณ  ์žˆ๋‹ค. ์ด ๊ฐ๊ฐ์˜ ๋‹จ๊ณ„์—์„œ ์•„๋ž˜์—๋Š” ์–ด๋–ค ๋™์ž‘๋“ค์ด ์ด๋ฃจ์–ด์ง€๋Š”์ง€ ์ ํ˜€์žˆ๋‹ค. Hit test๋Š” ์ด ์‹œ์ ์—์„œ, ๊ฐ€์žฅ ์ฒ˜์Œ ์‹คํ–‰๋œ๋‹ค. ์ฆ‰, ์‚ฌ์šฉ์ž๊ฐ€ ํ„ฐ์น˜๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์ˆœ๊ฐ„, Hit test๋ฅผ ํ†ตํ•ด first responder๊ฐ€ ์–ด๋–ค ๋…€์„์ธ์ง€ ํ™•์ธํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  drag๋ฅผ ํ•˜๋ฉด์„œ ์ผ์ • ์‹œ๊ฐ„์„ ๊ธฐ์ค€์œผ๋กœ ์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ๊ฐ€ ๊ณ„์† ๋ฐœ์ƒํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ํ™”์‚ดํ‘œ๊ฐ€ 3๊ฐœ์ด๋‹ค. ์ด ์ด๋ฒคํŠธ๋“ค์€ ์ฒซ๋ฒˆ์งธ hit test์˜ ๊ฒฐ๊ณผ์ธ first responder์—๊ฒŒ ์ „๋‹ฌ๋œ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ํ„ฐ์น˜๋ฅผ ๋—€ ๊ฒฝ์šฐ, ๋—€ event๊ฐ€ first responder๋กœ ์ „๋‹ฌ๋œ๋‹ค. ์–ด๋–„์šฉ ์‰ฝ์ฃ ?

Hit Test ์ž‘๋™ ๋ฐฉ์‹

func hitTest(point: CGPoint) -> UIResponder? {
    guard isPoint(inside: point) else { return nil }
    guard subViews.count > 0 else { return self }
 
    let pointDiff = CGPoint(x: point.x = origin.x, y: point.y - origin.y)
 
    for subView in subViews.reversed() {
        let hitView = subView.hitTest(point: pointDiff)
        if hitView != nil {
            return hitView
        }
    }
    return self
}

ํ•ด๋‹น ์ฝ”๋“œ๋Š” ๊ฐ„๋žตํ•˜๊ฒŒ ๋™์ž‘์„ ๋‚˜ํƒ€๋‚ด ๋ณธ ๊ฒƒ์ด๋‹ค. ์ฐธ๊ณ ๋กœ subview๋“ค์€ ํ•˜์œ„์— ์žˆ์„ ์ˆ˜๋ก array์— ์•„๋ž˜์— ์žˆ๋‹ค. (stack ๊ตฌ์กฐ์ด๋‹ค.) ์•Œ๊ณ ๋ฆฌ์ฆ˜์—์„œ ์ƒ๊ฐํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋ถ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ํ•ด๋‹น ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ UIApplication์—์„œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์•„ ํ•˜์œ„๋กœ ์ „๋‹ฌํ•œ๋‹ค.
    • UIApplication์€ first responder๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋‚ด๋ถ€์— ๋“ค์–ด์žˆ์ง€ ์•Š์œผ๋ฉด pass ํ•œ๋‹ค.
  • ๋‚ด๋ถ€์— ๋“ค์–ด์žˆ๋Š”๋ฐ ํ•˜์œ„ view๊ฐ€ ์—†๋‹ค๋ฉด (๋‚ด๊ฐ€ ๊ฐ€์žฅ ์ƒ์œ„ ๋ทฐ) ์ž์‹ ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ํ•˜์œ„ view๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ, ๋‚ด๊ฐ€ ๊ฐ€์žฅ ์œ„์— ์žˆ์ง€ ์•Š์„์ˆ˜๋„ ์žˆ์œผ๋ฏ€๋กœ ํ•˜์œ„ View๋กœ Hit Test๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

์ด๋Ÿฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ Reverse Pre-order depth-first traversal algorithm ์ด๋ผ ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์‚ฌ์‹ค DFS์™€ ํฌ๊ฒŒ ๋‹ค๋ฅธ์ ์ด ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ด์„œ ๊นŠ๊ฒŒ ์ž‘์„ฑํ•˜์ง€๋Š” ์•Š์•˜๋‹ค. ๋˜ ๋ช‡๊ฐ€์ง€ ๋” ๊ณ ๋ คํ•˜๋Š” ์‚ฌํ•ญ์ด ์žˆ๊ธดํ•œ๋ฐ, ์ค‘์š”ํ•˜์ง€ ์•Š์•„์„œ ์ œ๊ฑฐํ–ˆ๋‹ค. ๊ณ ๋ ค ์‚ฌํ•ญ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • View๊ฐ€ hidden์ธ๊ฐ€?
  • UserInteraction์ด Enable์ธ๊ฐ€?
  • Alpha ์ˆ˜์ค€์ด ์ผ์ • ์ด์ƒ (0.01 up) ์ธ๊ฐ€?
  • ํ•ด๋‹น ์ด๋ฒคํŠธ๊ฐ€ ํฌํ•จ๋˜๋Š”๊ฐ€?

Reference