如何添加拖动以将函数移动到转换后的视图?

时间:2016-11-08 01:54:44

标签: ios swift uiview uitapgesturerecognizer cgaffinetransform

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

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

我尝试了几种方法,但没有一种方法属性(视图在组合旋转和调整大小动作时只是奇怪的行为)。

这样做的正确方法是什么?

根据这个答案引用苹果公司的文件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 stackoverflow.

    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
}
}

enter image description here enter image description here


更新

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

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
        }

    }
}

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

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

这种情况下,我可以完全分离旋转功能和移动功能,使其真正易于处理。

  • ViewA。移动功能已实施。它在下面可见,使其易于理解,但其颜色应清晰。我只在ViewB(子视图)上启用了移动功能触摸。

  • ViewB。实现了旋转功能。它是ViewA的子视图 enter image description here