在做手势时将锚点放在屏幕的中心

Kas*_*245 9 xcode calayer ios swift

我有一个图像,可以响应捏,旋转和平移手势.我希望图像的捏合和旋转将相对于放置在屏幕中间的锚点完成,就像使用Xcode iPhone模拟器按下选项键一样.如果图像的中心可能会缩放并平移到其他位置,如何将锚点放在屏幕中间?

这是我的缩放和旋转手势功能:

@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
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑

我看到人们并不急于进入这个领域.让我帮助分享一下过去几天我取得的一些进展.

首先,我编写了一个函数centerAnchorPoint,无论该锚点之前的位置如何,都能正确地将图像的锚点放置在屏幕的中心.但是,图像不得缩放或旋转才能工作.

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
}
Run Code Online (Sandbox Code Playgroud)

setAchorPoint此处使用函数将锚点设置为新位置而不移动图像.

然后我panGesture通过在它的末尾插入更新函数:

if gestureRecognizer.state == .ended {
        self.centerAnchorPoint()
    }
Run Code Online (Sandbox Code Playgroud)

编辑2

好的,所以我将尝试简单地解释上面的代码.

我在做的是:

  1. 找到照片中心和屏幕中心之间的距离
  2. 应用此公式以查找锚点的新位置:

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

然后为y位置做同样的事情.

  1. 将此点设置为新锚点,而不使用setAnchorPoint函数移动照片

正如我所说,如果图像没有缩放,这种方法很有效.如果是,那么锚点不会停留在中心.

奇怪的是,distanceX或distanceY并不完全取决于比例值,所以这样的事情并不常用:

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

编辑3

我想出了缩放比例.似乎正确的比例因子必须是:

scaleFactor = photo.frame.size.width/photo.layer.bounds.size.width
Run Code Online (Sandbox Code Playgroud)

我使用它而不是scaleValue它,它的工作非常出色.

因此完成了平移和缩放.唯一剩下的就是轮换,但看起来它是最难的.

我首先想到的是将旋转矩阵应用于X和Y方向的增量,如下所示:

    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)
Run Code Online (Sandbox Code Playgroud)

但这不起作用.

Kas*_*245 2

由于问题大部分已在编辑中得到回答,因此我不想重复太多。

总的来说,我对原始问题中发布的代码进行了更改:

  1. 删除了对adjustAnchorPoint捏合和旋转手势函数中函数的调用。
  2. 将这段代码放在平移手势函数中,以便在平移照片后锚点会更新其位置:

    如果gestureRecognizer.state == .ending { self.centerAnchorPoint() }

  3. 更新了centerAnchorPoint用于旋转的功能。

完整的工作centerAnchorPoint功能(包括旋转):

    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)
}
Run Code Online (Sandbox Code Playgroud)

这里需要注意的关键是旋转矩阵必须应用于 distanceX 和 distanceY。比例因子也会更新以在整个旋转过程中保持不变。