Coloque el punto de anclaje en el centro de la pantalla mientras hace gestos.

Tengo una vista con una image que responde al pellizco, la rotation y los gestos panorámicos. Quiero que el pellizco y la rotation de la image se hagan con respecto al punto de anclaje colocado en el medio de la pantalla, exactamente como se hace con el simulador de iPhone Xcode al presionar la tecla de opciones. ¿Cómo puedo ubicar el punto de anclaje en el medio de la pantalla si el centro de la image puede escalarse y visualizarse en una location diferente?

Aquí están mis funciones de escalar y rotar gestos:

@IBAction func pinchGesture(_ gestureRecognizer: UIPinchGestureRecognizer) { // Move the achor point of the view's layer to the touch point // so that scaling the view and the layer becames simpler. self.adjustAnchorPoint(gestureRecognizer: gestureRecognizer) // Scale the view by the current scale factor. if(gestureRecognizer.state == .began) { // Reset the last scale, necessary if there are multiple objects with different scales lastScale = gestureRecognizer.scale } if (gestureRecognizer.state == .began || gestureRecognizer.state == .changed) { let currentScale = gestureRecognizer.view!.layer.value(forKeyPath:"transform.scale")! as! CGFloat // Constants to adjust the max/min values of zoom let kMaxScale:CGFloat = 15.0 let kMinScale:CGFloat = 1.0 var newScale = 1 - (lastScale - gestureRecognizer.scale) newScale = min(newScale, kMaxScale / currentScale) newScale = max(newScale, kMinScale / currentScale) let transform = (gestureRecognizer.view?.transform)!.scaledBy(x: newScale, y: newScale); gestureRecognizer.view?.transform = transform lastScale = gestureRecognizer.scale // Store the previous scale factor for the next pinch gesture call scale = currentScale // Save current scale for later use } } @IBAction func rotationGesture(_ gestureRecognizer: UIRotationGestureRecognizer) { // Move the achor point of the view's layer to the center of the // user's two fingers. This creates a more natural looking rotation. self.adjustAnchorPoint(gestureRecognizer: gestureRecognizer) // Apply the rotation to the view's transform. if gestureRecognizer.state == .began || gestureRecognizer.state == .changed { gestureRecognizer.view?.transform = (gestureRecognizer.view?.transform.rotated(by: gestureRecognizer.rotation))! // Set the rotation to 0 to avoid compouding the // rotation in the view's transform. angle += gestureRecognizer.rotation // Save rotation angle for later use gestureRecognizer.rotation = 0.0 } } func adjustAnchorPoint(gestureRecognizer : UIGestureRecognizer) { if gestureRecognizer.state == .began { let view = gestureRecognizer.view let locationInView = gestureRecognizer.location(in: view) let locationInSuperview = gestureRecognizer.location(in: view?.superview) // Move the anchor point to the the touch point and change the position of the view view?.layer.anchorPoint = CGPoint(x: (locationInView.x / (view?.bounds.size.width)!), y: (locationInView.y / (view?.bounds.size.height)!)) view?.center = locationInSuperview } } 

EDITAR

Veo que la gente no está ansiosa por entrar en esto. Permítanme ayudar compartiendo algunos progresos que he realizado en los últimos días.

En primer lugar, escribí un centro de funciones centerAnchorPoint que coloca correctamente el punto de anclaje de una image en el centro de la pantalla, independientemente de dónde se haya ubicado anteriormente ese punto de anclaje. Sin embargo, la image no debe escalarse o rotarse para que funcione.

 func setAnchorPoint(_ anchorPoint: CGPoint, forView view: UIView) { var newPoint = CGPoint(x: view.bounds.size.width * anchorPoint.x, y: view.bounds.size.height * anchorPoint.y) var oldPoint = CGPoint(x: view.bounds.size.width * view.layer.anchorPoint.x, y: view.bounds.size.height * view.layer.anchorPoint.y) newPoint = newPoint.applying(view.transform) oldPoint = oldPoint.applying(view.transform) var position = view.layer.position position.x -= oldPoint.x position.x += newPoint.x position.y -= oldPoint.y position.y += newPoint.y view.layer.position = position view.layer.anchorPoint = anchorPoint } func centerAnchorPoint(gestureRecognizer : UIGestureRecognizer) { if gestureRecognizer.state == .ended { view?.layer.anchorPoint = CGPoint(x: (photo.bounds.midX / (view?.bounds.size.width)!), y: (photo.bounds.midY / (view?.bounds.size.height)!)) } } func centerAnchorPoint() { // Position of the current anchor point let currentPosition = photo.layer.anchorPoint self.setAnchorPoint(CGPoint(x: 0.5, y: 0.5), forView: photo) // Center of the image let imageCenter = CGPoint(x: photo.center.x, y: photo.center.y) self.setAnchorPoint(currentPosition, forView: photo) // Center of the screen let screenCenter = CGPoint(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY) // Distance between the centers let distanceX = screenCenter.x - imageCenter.x let distanceY = screenCenter.y - imageCenter.y // Find new anchor point let newAnchorPoint = CGPoint(x: (imageCenter.x+2*distanceX)/(UIScreen.main.bounds.size.width), y: (imageCenter.y+2*distanceY)/(UIScreen.main.bounds.size.height)) //photo.layer.anchorPoint = newAnchorPoint self.setAnchorPoint(newAnchorPoint, forView: photo) let dotPath = UIBezierPath(ovalIn: CGRect(x: photo.layer.position.x-2.5, y: photo.layer.position.y-2.5, width: 5, height: 5)) layer.path = dotPath.cgPath } 

Function setAchorPoint se usa aquí para establecer el punto de anclaje en una nueva position sin mover una image.

Luego actualicé la function panGesture insertando esto al final:

 if gestureRecognizer.state == .ended { self.centerAnchorPoint() } 

EDIT 2

Ok, entonces trataré de simplemente explicar el código anterior.

Lo que estoy haciendo es:

  1. Encontrar la distancia entre el centro de la foto y el centro de la pantalla
  2. Aplica esta fórmula para encontrar la nueva position del punto de anclaje:

newAnchorPointX = (imageCenter.x-distanceX) / screenWidth + distanceX / screenWidth

Luego haz lo mismo para la position y.

  1. Establezca este punto como un nuevo punto de anclaje sin mover la foto con la function setAnchorPoint

Como dije, esto funciona muy bien si la image no está escalada. Si es así, el punto de anclaje no se queda en el centro.

Curiosamente, la distancia X o la distancia Y no depende exactamente del valor de la escala, así que algo como esto no funciona del todo:

newAnchorPointX = (imageCenter.x-distanceX) / screenWidth + distanceX / (scaleValue * screenWidth)

EDIT 3

Me di count de la escala. Parece que el factor de escala correcto tiene que ser:

 scaleFactor = photo.frame.size.width/photo.layer.bounds.size.width 

Utilicé esto en lugar de scaleValue y funcionó espléndidamente.

Por lo tanto, el barrido y el escalado se realizan. Lo único que queda es la rotation, pero parece que es la más difícil.

Lo primero que pensé es aplicar la matriz de rotation a los incrementos en las direcciones X e Y, así:

  let incrementX = (distanceX)/(screenWidth) let incrementY = (distanceY)/(screenHeight) // Applying rotation matrix let incX = incrementX*cos(angle)+incrementY*sin(angle) let incY = -incrementX*sin(angle)+incrementY*cos(angle) // Find new anchor point let newAnchorPoint = CGPoint(x: 0.5+incX, y: 0.5+incY) 

Sin embargo, esto no funciona.

Como la pregunta se responde principalmente en las ediciones, no quiero repetirme demasiado.

En general, lo que cambié del código publicado en la pregunta original:

  1. Llamadas eliminadas para adjustAnchorPoint function adjustAnchorPoint en funciones de gesto de pellizco y rotation.
  2. Colocó esta pieza de código en la function de gesto de pan, para que el punto de anclaje actualice su position después de panear la foto:

    si gestureRecognizer.state == .ended {self.centerAnchorPoint ()}

  3. centerAnchorPoint function centerAnchorPoint actualizada funcionó para la rotation.

Una function centerAnchorPoint totalmente funcional (rotation incluida):

  func centerAnchorPoint() { // Scale factor photo.transform = photo.transform.rotated(by: -angle) let curScale = photo.frame.size.width / photo.layer.bounds.size.width photo.transform = photo.transform.rotated(by: angle) // Position of the current anchor point let currentPosition = photo.layer.anchorPoint self.setAnchorPoint(CGPoint(x: 0.5, y: 0.5), forView: photo) // Center of the image let imageCenter = CGPoint(x: photo.center.x, y: photo.center.y) self.setAnchorPoint(currentPosition, forView: photo) // Center of the screen let screenCenter = CGPoint(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY) // Distance between the centers let distanceX = screenCenter.x - imageCenter.x let distanceY = screenCenter.y - imageCenter.y // Apply rotational matrix to the distances let distX = distanceX*cos(angle)+distanceY*sin(angle) let distY = -distanceX*sin(angle)+distanceY*cos(angle) let incrementX = (distX)/(curScale*UIScreen.main.bounds.size.width) let incrementY = (distY)/(curScale*UIScreen.main.bounds.size.height) // Find new anchor point let newAnchorPoint = CGPoint(x: 0.5+incrementX, y: 0.5+incrementY) self.setAnchorPoint(newAnchorPoint, forView: photo) } 

Lo más importante a tener en count aquí es que la matriz de rotation debe aplicarse a la distancia X y la distancia Y. El factor de escala también se actualiza para permanecer igual durante la rotation.