Как я могу проверить слои SubView? - PullRequest
0 голосов
/ 03 декабря 2018

Я столкнулся с проблемой в моей заявке.Я хочу иметь возможность подключиться к подпредставлению и определить, какой из слоев подпредставления был прослушан.Я (наивно?) Решил использовать метод hitTest (: CGPoint) корневого слоя подпредставления.Результаты оказались не такими, как я ожидал.Я создал игровую площадку, чтобы проиллюстрировать сложность.

Игровая площадка (код ниже):

  1. Создает контроллер представления и его корневое представление
  2. Добавляет подпредставление вкорневой вид
  3. Добавляет подслой к корневому слою подпредставления

Вот как это выглядит:

enter image description here

  1. Корневой слой корневого представления черный
  2. Корневой слой подпредставления красный
  3. Подслой подпредставления полупрозрачный серый
  4. Белая линия предназначена дляиллюстративные цели

Когда игровая площадка работает, я начинаю постукивать, начиная с верхней части белой линии и спускаясь вниз.Вот операторы печати, полученные с помощью метода обработки жестов касания:

Root view tapped @ (188.5, 12.5)
Тест попадания возвратил «Root View -> Root Layer» с рамкой (0.0,0.0, 375.0, 668.0)

Повернутый вид root повернут @ (188.5, 49.0)
Тест попадания вернул «Root View -> Root Layer» с рамкой (0.0, 0.0, 375.0, 668.0)

Root view tapped @ (188.5, 88.5)
Тест попадания возвратил "Root View -> Root Layer" с рамкой (0.0, 0.0, 375.0, 668.0)

Sub view tapped @ (140.0, 11.0)

дополнительный вид повернут @ (138.5, 32.5)

дополнительный вид повернут @ (138.5, 55.5)

дополнительный вид повернут @ (138.0, 84.0)

Дополнительный вид коснулся @ (139,0, 113,0)
Тест хита вернул «Дополнительный вид -> Корневой слой» с рамкой (50,0, 100,0, 275,0, 468,0)

Дополнительный вид коснулся @ (138,0, 138,5)
Тест попадания возвратил «Sub View -> Root Layer» с рамкой (50.0, 100.0, 275.0, 468.0)

Подвид повернул @ (140.0, 171.5)
Тест попадания вернулся «Sub View-> Root Layer "с фреймом (50,0, 100,0, 275,0, 468,0)

постукивание по вложенному представлению @ (137,5, 206,5)
Тест попадания вернул «Sub View -> Sub Layer» с рамкой (50,0, 100,0, 175,0, 268,0)

Дополнительный вид коснулся @ (135,5, 224,0)
Тест хита вернул «Дополнительный вид -> Дополнительный слой» с рамкой (50,0, 100,0, 175,0, 268,0)

Вы можете видеть, что, когда я нажимаю в корневом представлении, все «нормально».Как только я вхожу в подпредставление, тест попадания больше не находит слой, пока я не окажусь более чем на 100 пунктов ниже вершины подпредставления;в этот момент встречается корневой слой подпредставления.100 баллов после этого встречается подслой корневого слоя подпредставления.

Что происходит?Ну, очевидно, когда подпредставление было добавлено к корневому представлению, корневой слой подпредставления стал подслоем корневого слоя корневого представления, и его рамка была изменена, чтобы отразить его размещение в корневом слое корневого представления, а не в дополнительном представлении.

Итак, можно задать следующие вопросы:

  1. Правильно ли я понимаю, что, по-видимому, произошло?
  2. Это нормально и ожидаемо?
  3. Как мнеиметь дело с этим?

Спасибо, что нашли время, чтобы рассмотреть мой вопрос.

Вот код игровой площадки:

//: [Previous](@previous)

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {

    class View : UIView {
        override func draw(_ rect: CGRect) {
            let context = UIGraphicsGetCurrentContext()!
            context.setStrokeColor(UIColor.white.cgColor)
            context.setLineWidth(2.0)
            context.move(to: CGPoint(x: rect.midX, y: 0))
            context.addLine(to: CGPoint(x: rect.midX, y: rect.maxY))
            context.strokePath()
        }
    }

    private var subview: View!

    override func loadView() {
        let rootView = View(frame: CGRect.zero)
        rootView.backgroundColor = .black
        rootView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognized(_:))))
        rootView.layer.name = "Root View -> Root Layer"
        self.view = rootView
    }

    override func viewDidLayoutSubviews() {
        subview = View(frame: view.frame.insetBy(dx: 50.0, dy: 100.0))
        subview.backgroundColor = .red
        subview.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognized(_:))))
        subview.layer.name = "Sub View -> Root Layer"

        let sublayer = CALayer()
        sublayer.bounds = subview.layer.bounds.insetBy(dx: 50, dy: 100)
        sublayer.position = CGPoint(x: subview.layer.bounds.midX, y: subview.layer.bounds.midY)
        sublayer.backgroundColor = UIColor.gray.cgColor.copy(alpha: 0.5)
        sublayer.name = "Sub View -> Sub Layer"
        subview.layer.addSublayer(sublayer)

        view.addSubview(subview)
    }

    @objc func tapGestureRecognized(_ sender: UITapGestureRecognizer) {
        guard sender.state == .ended, let tappedView = sender.view else { return }

        var viewName: String
        switch tappedView {
        case view: viewName = "Root"
        case subview: viewName = "Sub"
        default: return
        }

        let location = sender.location(in: tappedView)

        print("\n\(viewName) view tapped @\(location)")

        if let layer = tappedView.layer.hitTest(location) {
            print("Hit test returned <\(layer.name ?? "unknown")> with frame \(layer.frame)")
        }
    }
}

PlaygroundPage.current.liveView = ViewController()
PlaygroundPage.current.needsIndefiniteExecution = true

//: [Next](@next)

Обновление:Решенная проблема

Под руководством всегда знающего Мэтта Нюбурга (и его самой превосходной книги) я изменил метод обработчика жестов касания следующим образом:

@objc func tapGestureRecognized(_ sender: UITapGestureRecognizer) {
    guard sender.state == .ended, let tappedView = sender.view else { return }

    var viewName: String
    let hitTestLocation: CGPoint

    switch tappedView {
    case view:
        viewName = "Root"
        hitTestLocation = sender.location(in: tappedView)
    case subview:
        viewName = "Sub"
        hitTestLocation = sender.location(in: tappedView.superview!)
   default: return
    }

    print("\n\(viewName) view tapped @\(sender.location(in: tappedView))")

    if let layer = tappedView.layer.hitTest(hitTestLocation) {
        print("Hit test returned <\(layer.name ?? "unknown")> with frame \(layer.frame)")
    }
}

Теперь консольвывод выглядит хорошо:

Root View повернут @ (186.0, 19.0)
Тест попадания вернул «Root View -> Root Layer» с рамкой (0.0, 0.0, 375.0, 668.0)

Root view tapped @ (187.0, 45.0)
Проверка тестаповернул «Root View -> Root Layer» с рамкой (0.0, 0.0, 375.0, 668.0)

Root View повернул @ (187.0, 84.5)
Тест хита вернул «Root View -> Root Layer» скадр (0,0, 0,0, 375,0, 668,0)

Sub view tapped @ (138.0, 9.0)
Тест попадания вернул «Sub View -> Root Layer» с рамкой (50.0, 100.0, 275.0, 468.0)

Sub view tapped @ (137.5,43.5)
Тест попадания возвратил «Sub View -> Root Layer» с рамкой (50.0, 100.0, 275.0, 468.0)

Подвид коснулся @ (138.5, 77.5)
Возвращен тест попадания «SubВид -> Root Layer "с рамкой (50.0, 100.0, 275.0, 468.0)

Подвид повернутым @ (138.0, 111.0)
Тест попадания вернул" Sub View -> Sub Layer 1 "с рамкой (50,0, 100,0, 175,0, 268,0)

При просмотре вспомогательного вида коснулся @ (138,5, 140,5)
Тест попадания возвратил «Подвид -> Подуровень 1» с рамкой (50,0, 100,0, 175,0, 268,0)

Подвид, коснувшийся @ (139.5, 174.5)
Тест попадания вернул «Подвид -> Подуровень 1» с рамкой (50.0, 100.0, 175.0, 268.0)

ВторойОбновление - более четкий код

Я возвращаюсь назад с кодом, который в итоге использовал для обнаружения касаний подуровней.Надеюсь, он более понятен, чем приведенный выше код игровой площадки.

private struct LayerTouched : CustomStringConvertible {

    let view: UIView // The touched view
    var layer: CALayer // The touched layer
    var location: CGPoint // The touch location expressed in the touched layer's coordinate system

    init(by recognizer: UIGestureRecognizer) {
        view = recognizer.view!
        let gestureLocation = recognizer.location(in: view)

        // AFAIK - Any touchable layer will have a super layer. Hence the forced unwrap is okay.
        let hitTestLocation = view.layer.superlayer!.convert(gestureLocation, from: view.layer)
        layer = view.layer.hitTest(hitTestLocation)!

        location = layer.convert(gestureLocation, from: view.layer)
    }

    var description: String {
        return """
        Touched \(layer)
        of \(view)
        at \(location).
        """
    }
}

1 Ответ

0 голосов
/ 03 декабря 2018

метод hitTest(:CGPoint) корневого слоя подпредставления

Но вы забыли, что тестирование попаданий слоя работает особым образом.Предоставляемая вами CGPoint должна находиться в системе координат суперслоя того слоя, который вы тестируете.

...