Como é que o sistema determina em qual vista ocorreu o toque?
sobes.tech IA
Resposta da IA
O sistema utiliza dois mecanismos principais: o teste de toque (hit-testing) e a hierarquia de vistas.
-
Teste de toque (Hit-testing): O framework UIKit usa o método
hitTest(_:with:)da vista raiz da janela (UIWindow) para determinar qual vista está sob o ponto de contacto. Este método percorre recursivamente a hierarquia de vistas, começando pela raiz. -
Hierarquia de vistas:
- O método
hitTest(_:with:)primeiro verifica se o ponto de contacto está dentro dos limites da vista atual. Se não estiver, retornanil. - Se o ponto estiver dentro dos limites, o método revisa as vistas filhas em ordem inversa (da última adicionada à primeira). Para cada vista filha, chama seu próprio método
hitTest(_:with:)com o mesmo ponto de contacto (convertido para as coordenadas da vista filha). - A primeira vista filha cujo método
hitTest(_:with:)retorna um resultado não-nil(ou seja, ela ou uma de suas vistas filhas contém o ponto) é considerada "tocada" e esse resultado é retornado para cima na hierarquia. - Se nenhuma vista filha contiver o ponto de contacto, e a vista atual contiver o ponto e não tiver
userInteractionEnabled = false, o sistema considera que o toque ocorreu na vista atual, e o método retornaself. - Se
userInteractionEnabledda vista atual forfalse, ouhiddenfortrue, oualphafor menor que 0.01, o método retornanil, mesmo que o ponto esteja dentro dos limites.
- O método
O processo continua descendo pela hierarquia até encontrar a vista mais baixa na hierarquia que contenha o ponto de contacto. Essa vista torna-se a hit-test view ou hit-test result, e a ela (ou ao seu antecessor mais próximo adequado) será delegado o gerenciamento posterior do evento (métodos touchesBegan, touchesMoved, etc.).
Aqui está como sobrescrever hitTest ou point(inside:with:) para comportamento personalizado de hit-testing:
import UIKit
class CustomView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// Verifica se o ponto está dentro desta vista.
// Se não, retorna nil.
if !self.point(inside: point, with: event) {
return nil
}
// Percorre as vistas filhas em ordem inversa.
for subview in subviews.reversed() {
// Converte o ponto para as coordenadas da vista filha.
let convertedPoint = subview.convert(point, from: self)
// Chama recursivamente hitTest na vista filha.
if let hitTestView = subview.hitTest(convertedPoint, with: event) {
// Se a vista filha ou seus descendentes contêm o ponto, retorna esse resultado.
return hitTestView
}
}
// Se nenhuma vista filha processou o toque,
// e esta vista é interativa e contém o ponto, retorna self.
return self
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
// A implementação base verifica se o ponto está dentro dos limites e se a vista é interativa.
// return super.point(inside: point, with: event)
// Pode ser sobrescrita para formas personalizadas ou margens, por exemplo, aumentar a área de toque.
let extendedBounds = bounds.insetBy(dx: -20, dy: -20) // Aumenta a área em 20pts em todas as direções
return extendedBounds.contains(point)
}
}
O método point(inside:with:) é chamado de hitTest(_:with:) para verificar rapidamente se o ponto está dentro da área da vista antes de verificar as vistas filhas.
No final, encontra-se a vista mais baixa na hierarquia que contenha o ponto de contacto e que tenha userInteractionEnabled como true, hidden como false e alpha maior que 0.01.