I want to allow users to add a yellow square UIView to a UIScrollView and then both drag and drop and zoom the yellow squares that the user has created.
I understand that to enable this I need to create a secondary view, say yellowSquareContainerView that I then set up as follows:
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return yellowSquareContainerView
}
I add the yellow squares to this container view.
However the main problem with this approach is that the UIPanGestureRecognizer added to the yellow UIView doesn't work if the user moves the square outside of the bounds of the yellowSquareContainerView (which is a completely valid use case).
How can I get the UIPanGestureRecognizer to work when the yellow squares are outside of the bounds of the container view?
After hacking around, I figured out an approach to panning a view outside the bounds of its parent view. It involves overriding the parent view's point
function.
Internally, a view uses its point function to determine if an event (like touch or pan) should be allowed. By default, point checks if the event starts in the view's bounds. That's why you can pan a view outside of its parent, but you can't pan it after that. By overriding point to always return true, the gesture is always allowed.
Here are the steps:
point(inside:with:)
to always return trueHere is an example subclass:
class ContainerView: UIView {
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return true
}
}
And here is some example code to demonstrate:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let containerView = ContainerView(frame: CGRect(x: 100, y: 300, width: 200, height: 200))
containerView.backgroundColor = .green
view.addSubview(containerView)
let yellowSquareView = UIView(frame: CGRect(x: 40, y: 40, width: 80, height: 80))
yellowSquareView.backgroundColor = .yellow
containerView.addSubview(yellowSquareView)
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
yellowSquareView.addGestureRecognizer(pan)
}
@objc private func handlePan(recognizer: UIPanGestureRecognizer) {
if let pannedView = recognizer.view {
let translation = recognizer.translation(in: view)
pannedView.center = CGPoint(x: pannedView.center.x + translation.x,
y: pannedView.center.y + translation.y)
recognizer.setTranslation(.zero, in: view)
}
}
}