如何使用 PanGestureRecognizer 重新定位子层?
How Do I Use a PanGestureRecognizer to Reposition a Sublayer?
我想通过单击和拖动来重新定位子图层。我遇到了困难,并整理了一个简单的演示来说明问题。在 GitHub.
上查看 and/or this 可运行项目的代码,其中包括一个游乐场
演示的UI(见下图)由三个元素组成:主视图(黑色)、子视图(红色)和子视图子层(灰色)。这两个视图都有一个向控制台报告的点击手势识别器。此外,子视图有一个平移手势识别器,允许子视图 and/or 重新定位其子层,并向控制台报告。
第一个问题 - sublayer滞后于手势。
- 在我的设备上和 Playground 中:如果我点击并拖动红色子视图,那么它会很好地跟踪 finger/cursor;灰色子层随之移动。如果我点击并拖动灰色子图层,它就会滞后:我必须缓慢移动或暂停并等待它赶上。
- 在模拟器上:红色子视图和灰色子层都不跟踪光标。他们保持静止直到我停下来,这时他们迅速移动到他们的新位置。
第二个问题 - 我无法抓住靠近其边缘的灰色子层。
如果我点击靠近其边缘的灰色子层,控制台消息会正确报告灰色子层已被点击。但是,如果我尝试抓住同一个点并移动它,那么红色子视图就会移动。
感谢您花时间考虑这个问题。任何建议将不胜感激。
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!)")
}
}
}
I would like to reposition sub layers by clicking and dragging
我建议你不要再那样做了。层没有可触摸性,而视图有;事实上,有人明智地说视图 是 一层加上可触摸性。如果您使用子视图而不是子层,则可以直接触摸子视图,在其上使用手势识别器,以正常方式点击和平移,等等。你只是让自己的生活变得不必要的困难,(据我所知)没有明显的收获,通过使用图层而不是视图。
我想通过单击和拖动来重新定位子图层。我遇到了困难,并整理了一个简单的演示来说明问题。在 GitHub.
上查看 and/or this 可运行项目的代码,其中包括一个游乐场演示的UI(见下图)由三个元素组成:主视图(黑色)、子视图(红色)和子视图子层(灰色)。这两个视图都有一个向控制台报告的点击手势识别器。此外,子视图有一个平移手势识别器,允许子视图 and/or 重新定位其子层,并向控制台报告。
第一个问题 - sublayer滞后于手势。
- 在我的设备上和 Playground 中:如果我点击并拖动红色子视图,那么它会很好地跟踪 finger/cursor;灰色子层随之移动。如果我点击并拖动灰色子图层,它就会滞后:我必须缓慢移动或暂停并等待它赶上。
- 在模拟器上:红色子视图和灰色子层都不跟踪光标。他们保持静止直到我停下来,这时他们迅速移动到他们的新位置。
第二个问题 - 我无法抓住靠近其边缘的灰色子层。
如果我点击靠近其边缘的灰色子层,控制台消息会正确报告灰色子层已被点击。但是,如果我尝试抓住同一个点并移动它,那么红色子视图就会移动。
感谢您花时间考虑这个问题。任何建议将不胜感激。
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!)")
}
}
}
I would like to reposition sub layers by clicking and dragging
我建议你不要再那样做了。层没有可触摸性,而视图有;事实上,有人明智地说视图 是 一层加上可触摸性。如果您使用子视图而不是子层,则可以直接触摸子视图,在其上使用手势识别器,以正常方式点击和平移,等等。你只是让自己的生活变得不必要的困难,(据我所知)没有明显的收获,通过使用图层而不是视图。