【问题标题】:How Do I Use a PanGestureRecognizer to Reposition a Sublayer?如何使用 PanGestureRecognizer 重新定位子图层?
【发布时间】:2019-05-11 19:53:36
【问题描述】:

我想通过单击和拖动来重新定位子图层。我遇到了困难,并整理了一个简单的演示来说明问题。请参阅下面的代码和/或 this 可运行项目,其中包括 GitHub 上的游乐场。

演示的 UI(见下图)由三个元素组成:主视图(黑色)、子视图(红色)和子视图子层(灰色)。这两个视图都有一个向控制台报告的轻击手势识别器。此外,子视图有一个平移手势识别器,允许重新定位子视图和/或其子图层,以及向控制台报告。

第一个问题 - 子层滞后于手势。

  1. 在我的设备和 Playground 中:如果我点击并拖动红色子视图,它会很好地跟踪手指/光标;灰色子层随之移动。如果我点击并拖动灰色子图层,则它会滞后:我必须缓慢移动或暂停并等待它赶上。
  2. 在模拟器上:红色子视图或灰色子图层都不跟踪光标。它们保持静止,直到我停下来,然后它们迅速移动到新位置。

第二个问题 - 我无法抓住靠近边缘的灰色子层。

如果我点击靠近其边缘的灰色子图层,那么控制台消息会正确报告灰色子图层已被点击。但是,如果我试图抓住同一个点并移动它,那么红色子视图就会移动。

感谢您抽出宝贵时间考虑这一点。任何建议将不胜感激。

import UIKit

public class ViewController: UIViewController {

    override public func loadView() {
        let mainView = UIView(frame: CGRect.zero)
        mainView.backgroundColor = .black
        mainView.layer.name = "Main View's Layer"
        mainView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognized(_:))))
        self.view = mainView
    }

    override public func viewDidLayoutSubviews() {
        let subview = UIView(frame: view.bounds.insetBy(dx: 50.0, dy: 100.0))
        subview.backgroundColor = .red
        subview.layer.name = "Sub View's Layer"
        subview.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognized(_:))))
        subview.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(panGestureRecognized(_:))))

        let sublayer = CALayer()
        sublayer.bounds = subview.layer.bounds.insetBy(dx: 50, dy: 100)
        sublayer.name = "Sub View's Sublayer"
        sublayer.position = CGPoint(x: subview.layer.bounds.midX, y: subview.layer.bounds.midY)
        sublayer.backgroundColor = UIColor.gray.cgColor
        subview.layer.addSublayer(sublayer)

        view.addSubview(subview)
    }

    // **********************************************************************************************************************

    private struct TouchedLayer : CustomStringConvertible {

        var layer: CALayer
        let startingPosition: CGPoint

        init(recognizer: UIGestureRecognizer) {
            let view = recognizer.view!
            let touchLocation = recognizer.location(in: view)
            let hitTestLocation = view.layer.superlayer!.convert(touchLocation, from: view.layer)

            layer = view.layer.hitTest(hitTestLocation)!
            startingPosition = layer.position
        }

        mutating func updateLayerPosition(translation: CGPoint) {
            layer.position = CGPoint(x: startingPosition.x + translation.x, y: startingPosition.y + translation.y)
        }

        var description: String {
            return "\(layer.name ?? "<Unknown>"): position = \(layer.position)"
        }
    }

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

        let tappedLayer = TouchedLayer(recognizer: sender)
        print("\nTapped", tappedLayer)
    }

    private var panningLayer: TouchedLayer!
    @objc func panGestureRecognized(_ sender: UIPanGestureRecognizer) {
        switch sender.state {
        case .began:
            panningLayer = TouchedLayer(recognizer: sender)
            print("\nPanning Begun - \(panningLayer!)")
        case .changed:
            panningLayer.updateLayerPosition(translation: sender.translation(in: sender.view))
            //print("\tPanning - \(panningLayer!)")
        default:
            print("Panning Ended - \(panningLayer!)")
        }
    }
}

【问题讨论】:

    标签: xcode10 ios12 swift4.2


    【解决方案1】:

    我想通过点击和拖动来重新定位子图层

    我建议你不要再这样做了。图层没有可触摸性,而视图有;事实上,明智的说法是视图一个层加上可触摸性。如果您使用子视图而不是子图层,则可以直接触摸子视图,在其上安装手势识别器,以正常方式点击和平移,等等。通过使用层而不是视图,您只是让自己的生活变得不必要地困难,(据我所知)没有明显的收获。

    【讨论】:

    • 哦,好吧 - 这是我可以接受的建议。谢谢,马特。
    • 是的,如果您使用 Cocoa 框架而不是反对它,确实会容易得多。
    • 有人曾经对我说:“如果你和框架抗衡,它就会和你抗衡”。我说:该死,是的,我不想被擦伤和殴打。但诀窍是区分“我在战斗”和“我不明白这应该如何工作”。我很感谢像你这样的人!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-10
    相关资源
    最近更新 更多