如何将拖动移动功能添加到转换后的视图?

How to add drag to move function to a transformed view?

我制作了一个自定义视图,可以通过拖动角圆按钮来调整大小和旋转。

我需要视图能够通过拖动来改变它的位置。

我尝试了几种方法,但 none 奏效了 属性(在组合旋转和调整大小操作时,视图表现得很奇怪)。

执行此操作的正确方法是什么?

根据这个引用苹果文档的答案,What to use to move UIView self.frame or self.transform property? 我不应该将 frame 属性 与转换一起使用,所以我假设我需要用户 CGAffineTransform 来更改视图的位置? (我试过了,但效果不佳)

import Foundation
import UIKit

class ResizeRotateView: UIImageView, Resizable {

private let buttonWidth: CGFloat = 40
private var themeColor: UIColor = UIColor.magentaColor()


// Rotation Resize
var radian: CGFloat = 0

private var tempAnglePanStart  : Float = 0
private var tempAngleLastPanEnd: Float = 0


// UI Subviews
lazy var cornerButton: UIView = {
    let b = UIView()
    
    b.layer.cornerRadius = self.buttonWidth / 2
    b.layer.borderWidth  = 1
    b.layer.borderColor  = self.themeColor.CGColor
    
    let panGesture = UIPanGestureRecognizer(target: self, action: #selector(buttonTouchMoved) )
    b.addGestureRecognizer(panGesture)
    
    return b
}()




// Init

override init(frame: CGRect) {
    super.init(frame: frame)
    
    setupSuviews()
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}


func setupSuviews() {
    addSubview( cornerButton )
    addConstraintsWithFormat("H:[v0(\(buttonWidth))]", views: cornerButton)
    addConstraintsWithFormat("V:[v0(\(buttonWidth))]", views: cornerButton)
    addConstraint(NSLayoutConstraint(item: cornerButton, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .Right, multiplier: 1, constant: 0))
    addConstraint(NSLayoutConstraint(item: cornerButton, attribute: .CenterY, relatedBy: .Equal, toItem: self, attribute: .Top,   multiplier: 1, constant: 0))
}


override func hitTest(point: CGPoint, withEvent e: UIEvent?) -> UIView? { // This will enable gesture out of self.view's boundary. From Whosebug.

    if let result = super.hitTest(point, withEvent:e) {
        return result
    }
    
    for sub in self.subviews.reverse() {
        let pt = self.convertPoint(point, toView:sub)
        if let result = sub.hitTest(pt, withEvent:e) {
            return result
        }
    }
    return nil
}







////// Event

func buttonTouchMoved(gestureRecognizer: UIPanGestureRecognizer) {
 
    resize(gestureRecognizer)
    rotate(gestureRecognizer)
}
 







////// Action

// Resize
func resize(gestureRecognizer: UIPanGestureRecognizer) {
 
    // Temporary set aglet to 0
    self.transform = CGAffineTransformMakeRotation( 0 )

    // Resize
    let locationInSelf = gestureRecognizer.locationInView(self)

    resizeByKeepingAspectRatioByDistanceFromCenter(toLocation: locationInSelf)
    layoutSubviews()

    // Recover angle
    self.transform = CGAffineTransformMakeRotation( radian )
    layoutSubviews()
    
}



// Rotation
func rotate(gestureRecognizer: UIPanGestureRecognizer) {
    
    guard let superView = self.superview else {
        print("#Error: Super View not found")
        return
    }
    
    let angle = self.angle(byLocation: gestureRecognizer.locationInView( superView ) )
    
    
    if gestureRecognizer.state == .Began {
        print("Pan begin:\(angle)")
        tempAnglePanStart = angle - tempAngleLastPanEnd
    }
    
    
    let destinationAngle = angle - tempAnglePanStart
    self.transform = CGAffineTransformMakeRotation( destinationAngle.degreesToRadians )
    
    
    if gestureRecognizer.state == .Ended {
        print("Pan ended")
        tempAngleLastPanEnd = destinationAngle
    }
}













////// Helper

func angle( firstPoint: CGPoint, secondPoint: CGPoint ) -> Float {
    
    let dx = firstPoint.x - secondPoint.x
    let dy = firstPoint.y - secondPoint.y
    let angle = atan2(dy, dx).radiansToDegrees
    let angleInFloat = Float( angle.double )
    
    let formattedAngle = angleInFloat < 0 ? angleInFloat + 360.0 : angleInFloat
    
    return formattedAngle
}


func angle(byLocation location: CGPoint) -> Float {
    
    let targetViewCnter = self.center
    return self.angle(targetViewCnter, secondPoint: location)
}

func setAngle(angle: Float) {
    let radian = angle.degreesToRadians
    self.transform = CGAffineTransformMakeRotation( radian )
    self.radian = radian
}
}


更新

我非常接近。我可以通过保持旋转角度和视图大小来移动。剩下的问题是当我开始拖动时视图跳了一点。

import Foundation
import UIKit

class ResizeRorateMovableView: ResizeRotateView {

    var startCenter: CGPoint = CGPointZero
    
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setupSuviews()
        
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureEvent))
        addGestureRecognizer( panGesture )
        userInteractionEnabled = true
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    
    func panGestureEvent(panGesture: UIPanGestureRecognizer) {
        
        if panGesture.state == .Began {
            self.startCenter = self.center
            return
        }
        
        if panGesture.state == .Changed {
            
            let location = panGesture.translationInView(self)
            
            // Rotate first, then move
            var transfrom  = CGAffineTransformMakeRotation( self.radian )
            transfrom      = CGAffineTransformTranslate(transfrom, location.x, location.y)
            self.transform = transfrom

            return
        }
        
    }
}

它可能会跳动,因为用户不一定要触摸中心,但您将中心设置为他们拖动的位置。如果您开始靠近中心拖动,它不会像您开始靠近边缘拖动那样跳跃。要修复,当手势开始时,获取从触摸到当前中心的距离,并在进行平移时使用该偏移量。

我只需要添加一个父视图来实现移动功能。

这种情况下,我可以将旋转功能和移动功能完全分开,非常好办。

  • 视图A。实现移动功能。它在下面可见,以便于理解,但它的颜色应该清晰。我触摸了仅在 ViewB(子视图)上启用的移动功能。

  • 视图B。实现旋转功能。它是 ViewA 的子视图