CGAffineTransform
でビューを回転します
[view setTransform:newTransform];
フレーム値は変換が適用された後も同じままですが、このフレームの「回転」または変換された値を見つけるにはどうすればよいですか?
(ソース: informit.com )
回転したフレームエッジの正確な座標、つまりa、b、c、dポイントが必要です。
留意すべきことの1つは、変換によって座標系が変更されるため、親の「ビュー」と子の(変換された)ビューの間で変換できる必要があるということです。また、変換では、変換されたオブジェクトの中心は保持されますが、他の座標は保持されません。したがって、中心を基準にして物事を計算する必要があります。そして、あなたが必要とするいくつかのヘルパーがあります。 (私はエリカサドゥンの本Core iOS Developer's Cookbookから以下のアプローチのほとんどを得ました)。
私は通常、これらをUIViewのカテゴリとして追加します。
子の座標を親の座標に変換するには、次のようなものが必要です。
// helper to get pre transform frame
-(CGRect)originalFrame {
CGAffineTransform currentTransform = self.transform;
self.transform = CGAffineTransformIdentity;
CGRect originalFrame = self.frame;
self.transform = currentTransform;
return originalFrame;
}
// helper to get point offset from center
-(CGPoint)centerOffset:(CGPoint)thePoint {
return CGPointMake(thePoint.x - self.center.x, thePoint.y - self.center.y);
}
// helper to get point back relative to center
-(CGPoint)pointRelativeToCenter:(CGPoint)thePoint {
return CGPointMake(thePoint.x + self.center.x, thePoint.y + self.center.y);
}
// helper to get point relative to transformed coords
-(CGPoint)newPointInView:(CGPoint)thePoint {
// get offset from center
CGPoint offset = [self centerOffset:thePoint];
// get transformed point
CGPoint transformedPoint = CGPointApplyAffineTransform(offset, self.transform);
// make relative to center
return [self pointRelativeToCenter:transformedPoint];
}
// now get your corners
-(CGPoint)newTopLeft {
CGRect frame = [self originalFrame];
return [self newPointInView:frame.Origin];
}
-(CGPoint)newTopRight {
CGRect frame = [self originalFrame];
CGPoint point = frame.Origin;
point.x += frame.size.width;
return [self newPointInView:point];
}
-(CGPoint)newBottomLeft {
CGRect frame = [self originalFrame];
CGPoint point = frame.Origin;
point.y += frame.size.height;
return [self newPointInView:point];
}
-(CGPoint)newBottomRight {
CGRect frame = [self originalFrame];
CGPoint point = frame.Origin;
point.x += frame.size.width;
point.y += frame.size.height;
return [self newPointInView:point];
}
Swift 4
extension UIView {
/// Helper to get pre transform frame
var originalFrame: CGRect {
let currentTransform = transform
transform = .identity
let originalFrame = frame
transform = currentTransform
return originalFrame
}
/// Helper to get point offset from center
func centerOffset(_ point: CGPoint) -> CGPoint {
return CGPoint(x: point.x - center.x, y: point.y - center.y)
}
/// Helper to get point back relative to center
func pointRelativeToCenter(_ point: CGPoint) -> CGPoint {
return CGPoint(x: point.x + center.x, y: point.y + center.y)
}
/// Helper to get point relative to transformed coords
func newPointInView(_ point: CGPoint) -> CGPoint {
// get offset from center
let offset = centerOffset(point)
// get transformed point
let transformedPoint = offset.applying(transform)
// make relative to center
return pointRelativeToCenter(transformedPoint)
}
var newTopLeft: CGPoint {
return newPointInView(originalFrame.Origin)
}
var newTopRight: CGPoint {
var point = originalFrame.Origin
point.x += originalFrame.width
return newPointInView(point)
}
var newBottomLeft: CGPoint {
var point = originalFrame.Origin
point.y += originalFrame.height
return newPointInView(point)
}
var newBottomRight: CGPoint {
var point = originalFrame.Origin
point.x += originalFrame.width
point.y += originalFrame.height
return newPointInView(point)
}
}
基本的な三角法を使用して、回転したビューの座標を確認できます。これを行う方法は次のとおりです。
最初のステップは、ビューの幅と高さを知ることです。それらを2で割ると、三角形の隣接する側と反対側(それぞれシアンと緑)になります。上記の例では、幅= 300、高さ= 300です。したがって、隣接サイド= 150および反対側サイド= 150です。
斜辺を見つけます(赤)。これには、式__h^2 = a^2 + b^2
_を使用します。この式を適用すると、斜辺= 212.13になります。
シータを見つけます。これは、隣接サイド(シアン)と斜辺(赤)の間の角度です。これには、数式cos(theta) = (h^2 + a^2 - o^2)/2*h*o
を使用します。この式を適用した後、theta = 0.785(RADIANS)であることがわかります。これを度に変換するには、式_degrees = radians * 180 / PI
_ = 45(度)を適用します。これは、斜辺の初期(オフセット)角度です。これは、理解することが非常に重要です。ビューのビューの回転がIS ZERO THE HYPOTENUSEのオフセット角度が45(DEGREES)の場合。すぐにシータが必要になります。
斜辺(赤)がわかったので、rotationAngleが必要です。この例では、UIRotationGestureRecognizer
を使用して正方形のビューを回転させました。このクラスには、ビューがどれだけ回転したかを示す「回転」プロパティがあります。この値はラジアンです。上記の例では、回転は0.3597ラジアンです。度に変換するには、式_degrees = radians * PI / 180
_を使用します。式を適用すると、回転角度は20.61度になります。
最終的にオフセットの幅(オレンジ)と高さ(紫)を見つけることができます。幅については式width = cos(rotationAngle - theta) * hypotenuse
を使用し、高さについては式height = sen(rotationAngle - theta)
を使用します。 WE THETAを(RADIANSで)回転角度から(RADIANSで)減算する必要があるため、THETAは初期オフセットであったため、このように表示します。斜辺の角度は45 )回転角度がゼロの場合。式を適用した後、幅= 193.20、高さ= -87.60であることがわかります。
最後に、これらの値(幅と高さ)を正方形の中心に追加して、青い点の座標を見つけることができます。
-例-
_// Get the center point
CGPoint squareCenter = self.squareView.center;
// Get the blue point coordinates
CGPoint bluePointCoordinates = CGPointMake(squareCenter.x + width, squareCenter.y + height);
_
青い点の座標は(963.20、272.40)です
式をよりよく理解するには、次のリンクを参照してください。
また、私が作成したテストプロジェクト(画像の中にあるもの)を試してみたい場合は、- 次のリンク から自由にダウンロードしてください。
[〜#〜]更新[〜#〜]
これは、探している右上のオフセットポイント(青)を計算する圧縮メソッドです。
_/* Params:
/ viewCenter: The center point (in superView coordinates) of your view
/ width: The total width of your view
/ height: The total height of your view
/ angleOfRotation: The rotation angle of your view. Can be either DEGREES or RADIANS
/ degrees: A boolean flag indicating whether 'angleOfRotation' is degrees
/ or radians. E.g.: If 'angleOfRotation' is expressed in degrees
/ this parameter must be 'YES'
*/
-(CGPoint)calculateTopRightCoordinatesFromViewCenter:(CGPoint)viewCenter viewWidth:(CGFloat)viewWidth viewHeight:(CGFloat)viewHeight angleOfRotation:(CGFloat)angleOfRotation degrees:(BOOL)degrees {
CGFloat adjacent = viewWidth/2.0;
CGFloat opposite = viewHeight/2.0;
CGFloat hipotenuse = sqrtf(powf(adjacent, 2.0) + pow(opposite, 2.0));
CGFloat thetaRad = acosf((powf(hipotenuse, 2.0) + powf(adjacent, 2.0) - pow(opposite, 2.0)) / (2 * hipotenuse * adjacent));
CGFloat angleRad = 0.0;
if (degrees) {
angleRad = angleOfRotation*M_PI/180.0;
} else {
angleRad = angleOfRotation;
}
CGFloat widthOffset = cosf(angleRad - thetaRad) * hipotenuse;
CGFloat heightOffset = sinf(angleRad - thetaRad) * hipotenuse;
CGPoint offsetPoint = CGPointMake(viewCenter.x + widthOffset, viewCenter.y + heightOffset);
return offsetPoint;
}
_
お役に立てれば!
あなたは使うべきです:
CGPoint CGPointApplyAffineTransform (
CGPoint point,
CGAffineTransform t
);
特定のポイントを取得するには、ビューの境界と中心を使用し、ビューの変換を適用して、変換後に新しいポイントを取得します。これは、変換だけでなくチェーンもサポートできるため、回転変換専用のコードを追加するよりも優れています。
なぜ混乱と大騒ぎ?単純にする? xが変換前にあった場合、アンカーの周りの他のすべての点と同じように、qラジアン/度になります。
それをすべて説明するつもりでしたが、この投稿のこの章ではさらに短いコンテキストで説明しました:
CGFloat radians = atan2f(yourView.transform.b, yourView.transform.a);
CGFloat degrees = radians * (180 / M_PI);
このコードを試してください
CGPoint localBeforeTransform = CGPointMake( view.bounds.size.width/2.0f, view.bounds.size.height/2.0f ); // lower left corner
CGPoint localAfterTransform = CGPointApplyAffineTransform(localBeforeTransform, transform);
CGPoint globalAfterTransform = CGPointMake(localAfterTransform.x + view.center.x, localAfterTransform.y + view.center.y);
私は私たちを助けることができるこのクラスを書きました:
TransformedViewFrameCalculator.h
#import <Foundation/Foundation.h>
@interface TransformedViewFrameCalculator : NSObject
@property (nonatomic, strong) UIView *viewToProcess;
- (void)calculateTransformedCornersWithTranslation:(CGPoint)translation
scale:(CGFloat)scale
rotation:(CGFloat)rotation;
@property (nonatomic, readonly) CGPoint transformedTopLeftCorner;
@property (nonatomic, readonly) CGPoint transformedTopRightCorner;
@property (nonatomic, readonly) CGPoint transformedBottomLeftCorner;
@property (nonatomic, readonly) CGPoint transformedBottomRightCorner;
@end
TransformedViewFrameCalculator.m:
#import "TransformedViewFrameCalculator.h"
@interface TransformedViewFrameCalculator ()
@property (nonatomic, assign) CGRect viewToProcessNotTransformedFrame;
@property (nonatomic, assign) CGPoint viewToProcessNotTransformedCenter;
@end
@implementation TransformedViewFrameCalculator
- (void)setViewToProcess:(UIView *)viewToProcess {
_viewToProcess = viewToProcess;
CGAffineTransform t = _viewToProcess.transform;
_viewToProcess.transform = CGAffineTransformIdentity;
_viewToProcessNotTransformedFrame = _viewToProcess.frame;
_viewToProcessNotTransformedCenter = _viewToProcess.center;
_viewToProcess.transform = t;
}
- (void)calculateTransformedCornersWithTranslation:(CGPoint)translation
scale:(CGFloat)scale
rotation:(CGFloat)rotation {
double viewWidth = _viewToProcessNotTransformedFrame.size.width * scale;
double viewHeight = _viewToProcessNotTransformedFrame.size.height * scale;
CGPoint viewCenter = CGPointMake(_viewToProcessNotTransformedCenter.x + translation.x,
_viewToProcessNotTransformedCenter.y + translation.y);
_transformedTopLeftCorner = [self calculateCoordinatesForViewPoint:CGPointMake(0, 0)
fromViewCenter:viewCenter
viewWidth:viewWidth
viewHeight:viewHeight
angleOfRotation:rotation];
_transformedTopRightCorner = [self calculateCoordinatesForViewPoint:CGPointMake(0, viewHeight)
fromViewCenter:viewCenter
viewWidth:viewWidth
viewHeight:viewHeight
angleOfRotation:rotation];
_transformedBottomLeftCorner = [self calculateCoordinatesForViewPoint:CGPointMake(viewWidth, 0)
fromViewCenter:viewCenter
viewWidth:viewWidth
viewHeight:viewHeight
angleOfRotation:rotation];
_transformedBottomRightCorner = [self calculateCoordinatesForViewPoint:CGPointMake(viewWidth, viewHeight)
fromViewCenter:viewCenter
viewWidth:viewWidth
viewHeight:viewHeight
angleOfRotation:rotation];
}
- (CGPoint)calculateCoordinatesForViewPoint:(CGPoint)viewPoint
fromViewCenter:(CGPoint)viewCenter
viewWidth:(CGFloat)viewWidth
viewHeight:(CGFloat)viewHeight
angleOfRotation:(CGFloat)angleOfRotation {
CGPoint centeredViewPoint = CGPointMake(viewPoint.x - viewWidth/2.0, viewPoint.y - viewHeight/2.0);
CGPoint rotatedCenteredViewPoint = CGPointApplyAffineTransform(centeredViewPoint, CGAffineTransformMakeRotation(angleOfRotation));
CGPoint rotatedViewPoint = CGPointMake(rotatedCenteredViewPoint.x + viewCenter.x, rotatedCenteredViewPoint.y + viewCenter.y);
return rotatedViewPoint;
}
たとえば、次の方法でコンテナビュー内のステッカーの移動/スケール/回転を制限するために使用します。
@property (nonatomic, strong) TransformedViewFrameCalculator *transformedFrameCalculator;
...
self.transformedFrameCalculator = [TransformedViewFrameCalculator new];
self.transformedFrameCalculator.viewToProcess = someView;
...
- (BOOL)transformedView:(UIView *)view
withTranslation:(CGPoint)translation
scale:(double)scale
rotation:(double)rotation
isFullyInsideValidFrame:(CGRect)validFrame {
[self.transformedFrameCalculator calculateTransformedCornersWithTranslation:translation
scale:scale
BOOL topRightIsInsideValidFrame = CGRectContainsPoint(validFrame, self.transformedFrameCalculator.transformedTopRightCorner);
BOOL topLeftIsInsideValidFrame = CGRectContainsPoint(validFrame, self.transformedFrameCalculator.transformedTopLeftCorner);
BOOL bottomRightIsInsideValidFrame = CGRectContainsPoint(validFrame, self.transformedFrameCalculator.transformedBottomRightCorner);
BOOL bottomLeftIsInsideValidFrame = CGRectContainsPoint(validFrame, self.transformedFrameCalculator.transformedBottomLeftCorner);
return topRightIsInsideValidFrame && topLeftIsInsideValidFrame && bottomRightIsInsideValidFrame && bottomLeftIsInsideValidFrame;
}