我正在尝试使用UIGestureRecognizers旋转,平移和缩放UIView。识别器被添加到超级视图中,而旋转,缩放等应用于子视图(_baseImage),这使我能够在将来接收手势事件时将其他事物覆盖在子视图之上。
这个想法是UIView应该在两个触摸点下方的子视图上的“锚点”周围缩放/旋转,因为这似乎是最自然的。我遇到的问题是设置anchorPoint后子视图的位置,以及缩放和旋转似乎没有使用set anchorPoint。我对重叠坐标系统/ CGAffine变换缺乏了解可能会让我陷入困境。代码来自各种示例。
这是我现在的代码:
-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
{
CGPoint oldOrigin = view.frame.origin;
view.layer.anchorPoint = anchorPoint;
CGPoint newOrigin = view.frame.origin;
CGPoint transition;
transition.x = newOrigin.x - oldOrigin.x;
transition.y = newOrigin.y - oldOrigin.y;
view.center = CGPointMake (view.center.x - transition.x, view.center.y - transition.y);
}
- (void) updateTransformWithOffset: (CGPoint) translation
{
// Create a blended transform representing translation,
// rotation, and scaling
_baseImage.transform = CGAffineTransformMakeTranslation(translation.x + tx, translation.y + ty);
_baseImage.transform = CGAffineTransformRotate(_baseImage.transform, theta);
_baseImage.transform = CGAffineTransformScale(_baseImage.transform, scale, scale);
}
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)uigr {
if (uigr.state == UIGestureRecognizerStateBegan) {
UIView *piece = self.view;
CGPoint locationInView = [uigr locationInView:_baseImage];
myFrame = _baseImage.frame;
CGPoint newAnchor = CGPointMake( (locationInView.x / piece.bounds.size.width), (locationInView.y / piece.bounds.size.height ));
[self setAnchorPoint:newAnchor forView:_baseImage];
}
}
- (void) handlePinch: (UIPinchGestureRecognizer *) uigr
{
[self adjustAnchorPointForGestureRecognizer:uigr];
if (uigr.state == UIGestureRecognizerStateBegan) {
initScale = scale;
}
scale = initScale*uigr.scale;
[self updateTransformWithOffset:CGPointZero];
}
答案 0 :(得分:6)
我找到了解决我对anchorPoint的具体问题的解决方案。我试图将变换应用于在Interface Builder中添加的UIView,这会导致锚点问题并设置新的中心点,删除然后重新添加子视图似乎可以修复它。如果有人有类似的问题,我在视图控制器上使用的最终代码,它使用触摸位置作为旋转和缩放的中心,在UIView上进行缩放,旋转和平移:
#import "ViewController.h"
@interface ViewController (){
CGFloat tx; // x translation
CGFloat ty; // y translation
CGFloat scale; // zoom scale
CGFloat theta; // rotation angle
CGFloat initScale ;
CGFloat initTheta ;
}
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIRotationGestureRecognizer *rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleRotation:)];
[rotationGesture setDelegate:self];
[self.view addGestureRecognizer:rotationGesture];
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinch:)];
[pinchGesture setDelegate:self];
[self.view addGestureRecognizer:pinchGesture];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[panGesture setDelegate:self];
[panGesture setMinimumNumberOfTouches:1];
[panGesture setMaximumNumberOfTouches:1];
[self.view addGestureRecognizer:panGesture];
_baseImage.transform = CGAffineTransformIdentity;
tx = 0.0f; ty = 0.0f; scale = 1.0f; theta = 0.0f;
scale = 1.0;
//removing and adding back to the view seems to fix problems with anchor point I was having, I suspect because of IB layout/scaling and constraints etc
UIView *mySuperView =_baseImage.superview;
[_baseImage removeFromSuperview];
[mySuperView addSubview:_baseImage];
}
-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)myview
{
CGPoint oldOrigin = myview.frame.origin;
myview.layer.anchorPoint = anchorPoint;
CGPoint newOrigin = myview.frame.origin;
CGPoint transition;
transition.x = (newOrigin.x - oldOrigin.x);
transition.y = (newOrigin.y - oldOrigin.y);
CGPoint myNewCenter = CGPointMake (myview.center.x - transition.x, myview.center.y - transition.y);
myview.center = myNewCenter;
}
- (void) updateTransformWithOffset: (CGPoint) translation
{
// Create a blended transform representing translation,
// rotation, and scaling
_baseImage.transform = CGAffineTransformMakeTranslation(translation.x + tx, translation.y + ty);
_baseImage.transform = CGAffineTransformRotate(_baseImage.transform, theta);
_baseImage.transform = CGAffineTransformScale(_baseImage.transform, scale, scale);
}
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)uigr {
if (uigr.state == UIGestureRecognizerStateBegan) {
tx =_baseImage.transform.tx;
ty =_baseImage.transform.ty;
CGPoint locationInView = [uigr locationInView:_baseImage];
CGPoint newAnchor = CGPointMake( (locationInView.x / _baseImage.bounds.size.width), (locationInView.y / _baseImage.bounds.size.height ));
[self setAnchorPoint:newAnchor forView:_baseImage];
}
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
// if the gesture recognizers are on different views, don't allow simultaneous recognition
if (gestureRecognizer.view != otherGestureRecognizer.view)
return NO;
if (![gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] && ![otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
return YES;
}
return NO;
}
- (void) handleRotation: (UIRotationGestureRecognizer *) uigr
{
if (uigr.state == UIGestureRecognizerStateBegan) {
initTheta = theta;
}
theta = initTheta+uigr.rotation;
[self adjustAnchorPointForGestureRecognizer:uigr];
[self updateTransformWithOffset:CGPointZero];
}
- (void) handlePinch: (UIPinchGestureRecognizer *) uigr
{
if (uigr.state == UIGestureRecognizerStateBegan) {
initScale = scale;
}
scale = initScale*uigr.scale;
[self adjustAnchorPointForGestureRecognizer:uigr];
[self updateTransformWithOffset:CGPointZero];
}
- (void) handlePan: (UIPanGestureRecognizer *) uigr
{
CGPoint translation = [uigr translationInView:_baseImage.superview];
[self adjustAnchorPointForGestureRecognizer:uigr];
[self updateTransformWithOffset:translation];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
答案 1 :(得分:0)
OP的原始解决方案已转换为Swift 2.0。我省略了解决方法,因为它似乎不再是一个问题了。
class ViewController: UIViewController {
var tx:CGFloat = 0.0 // x translation
var ty:CGFloat = 0.0 // y translation
var scale:CGFloat = 1.0 // zoom scale
var theta:CGFloat = 0.0 // rotation angle
var initScale:CGFloat = 1.0
var initTheta:CGFloat = 0.0
var transformedView: UIView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
transformedView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
transformedView.backgroundColor = UIColor.blueColor()
view.addSubview(transformedView)
let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(OHPlastyViewController.handleRotation(_:)))
rotationGesture.delegate = self
view.addGestureRecognizer(rotationGesture)
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(OHPlastyViewController.handlePinch(_:)))
pinchGesture.delegate = self
view.addGestureRecognizer(pinchGesture)
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(OHPlastyViewController.handlePan(_:)))
panGesture.delegate = self
panGesture.minimumNumberOfTouches = 1
panGesture.maximumNumberOfTouches = 1
view.addGestureRecognizer(panGesture)
// The workaround wasn't translated to Swift because it doesn't seem to be needed anymore
/*
_baseImage.transform = CGAffineTransformIdentity;
//removing and adding back to the view seems to fix problems with anchor point I was having, I suspect because of IB layout/scaling and constraints etc
UIView *mySuperView =_baseImage.superview;
[_baseImage removeFromSuperview];
[mySuperView addSubview:_baseImage];*/
}
func setAnchorPoint(anchorPoint: CGPoint, forView myView: UIView) {
let oldOrigin: CGPoint = myView.frame.origin
myView.layer.anchorPoint = anchorPoint
let newOrigin = myView.frame.origin
let transition = CGPoint(x: newOrigin.x - oldOrigin.x, y: newOrigin.y - oldOrigin.y)
let myNewCenter = CGPoint(x: myView.center.x - transition.x, y: myView.center.y - transition.y)
myView.center = myNewCenter
}
func updateTransformWithOffset(translation: CGPoint) {
transformedView.transform = CGAffineTransformMakeTranslation(translation.x + tx, translation.y + ty)
transformedView.transform = CGAffineTransformRotate(transformedView.transform, theta)
transformedView.transform = CGAffineTransformScale(transformedView.transform, scale, scale)
}
func adjustAnchorPointForGestureRecognizer(recognizer: UIGestureRecognizer) {
if (recognizer.state == .Began) {
tx = transformedView.transform.tx
ty = transformedView.transform.ty
let locationInView = recognizer.locationInView(transformedView)
let newAnchor = CGPoint(x: (locationInView.x / transformedView.bounds.size.width), y: (locationInView.y / transformedView.bounds.size.height))
setAnchorPoint(newAnchor, forView: transformedView)
}
}
func handleRotation(recognizer: UIRotationGestureRecognizer) {
if recognizer.state == .Began {
initTheta = theta
}
theta = initTheta + recognizer.rotation
adjustAnchorPointForGestureRecognizer(recognizer)
updateTransformWithOffset(CGPointZero)
}
func handlePinch(recognizer: UIPinchGestureRecognizer) {
if recognizer.state == .Began {
initScale = scale
}
scale = initScale * recognizer.scale
adjustAnchorPointForGestureRecognizer(recognizer)
updateTransformWithOffset(CGPointZero)
}
func handlePan(recognizer: UIPanGestureRecognizer) {
let translation = recognizer.translationInView(transformedView.superview)
adjustAnchorPointForGestureRecognizer(recognizer)
updateTransformWithOffset(translation)
}
}
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer.view != otherGestureRecognizer.view {
return false
}
if !gestureRecognizer.isKindOfClass(UITapGestureRecognizer.self) && !otherGestureRecognizer.isKindOfClass(UITapGestureRecognizer.self) {
return true
}
return false
}
}