Я работаю над приложением для iPad, которое будет использовать UIGestureRecognizer, чтобы позволить пользователю панорамировать, масштабировать и вращать объекты (подкласс UIView) на экране.
Я понимаю, что свойство [UIView frame]недопустимо после выполнения преобразования, поэтому я пытаюсь взять значения моих UIGestureRecognizer и сохранить «фрейм» сам.
Вот код, который я использую для попытки этого (вы можете распознатьмного кода из примера проекта Apple, SimpleGestureRecognizers):
// Shape.h (partial)
@interface Shape : UIView <UIGestureRecognizerDelegate> {
CGFloat centerX;
CGFloat centerY;
CGFloat rotatation;
CGFloat xScale;
CGFloat yScale;
}
// Shape.m (partial)
- (void)panPiece:(UIPanGestureRecognizer *)gestureRecognizer
{
UIView *piece = [gestureRecognizer view];
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
CGPoint translation = [gestureRecognizer translationInView:[piece superview]];
self.centerX += translation.x;
self.centerY += translation.y;
[piece setCenter:CGPointMake([piece center].x + translation.x, [piece center].y + translation.y)];
for ( HandleView *h in [self handles] ) {
[h setCenter:CGPointMake([h center].x + translation.x, [h center].y + translation.y)];
[h setNeedsDisplay];
}
[gestureRecognizer setTranslation:CGPointZero inView:[piece superview]];
NSLog(@"(%.0f, %.0f, %.0f, %.0f) %.2f˚, (%.2fx, %.2fx)", [self frame].origin.x, [self frame].origin.y, [self frame].size.width, [self frame].size.height, [self rotation], [self xScale], [self yScale]);
}
}
// rotate the piece by the current rotation
// reset the gesture recognizer's rotation to 0 after applying so the next callback is a delta from the current rotation
- (void)rotatePiece:(UIRotationGestureRecognizer *)gestureRecognizer
{
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
CGFloat rot = [self normalizeRotation:[gestureRecognizer rotation]];
self.rotation += rot * 180.0 / M_PI;
NSLog(@"Rotation: %.12f", [gestureRecognizer rotation]);
[gestureRecognizer view].transform = CGAffineTransformRotate([[gestureRecognizer view] transform], [gestureRecognizer rotation]);
[gestureRecognizer setRotation:0];
NSLog(@"(%.0f, %.0f, %.0f, %.0f) %.2f˚, (%.2fx, %.2fx)", [self frame].origin.x, [self frame].origin.y, [self frame].size.width, [self frame].size.height, [self rotation], [self xScale], [self yScale]);
}
}
// scale the piece by the current scale
// reset the gesture recognizer's rotation to 0 after applying so the next callback is a delta from the current scale
- (void)scalePiece:(UIPinchGestureRecognizer *)gestureRecognizer
{
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) {
self.xScale *= [gestureRecognizer scale];
self.yScale *= [gestureRecognizer scale];
[gestureRecognizer view].transform = CGAffineTransformScale([[gestureRecognizer view] transform], [gestureRecognizer scale], [gestureRecognizer scale]);
[gestureRecognizer setScale:1];
NSLog(@"(%.0f, %.0f, %.0f, %.0f) %.2f˚, (%.2fx, %.2fx)", [self frame].origin.x, [self frame].origin.y, [self frame].size.width, [self frame].size.height, [self rotation], [self xScale], [self yScale]);
}
}
Из-за некоторых странностей, которые я заметил при поворотах, я реализовал следующий метод, чтобы помочь сохранить точное значение вращения:
- (CGFloat) normalizeRotation:(CGFloat)rot
{
if (abs(rot) > 0.05) {
if (rot > 0) {
rot -= M_PI;
} else {
rot += M_PI;
}
return [self normalizeRotation:rot];
} else {
return rot;
}
}
В любом случае, форма на экране панорамирует, масштабируется и вращается нормально.Все так, как вы ожидаете, и производительность хорошая.
Проблема в том, что после того, как пользователь перемещает, изменяет размеры и поворачивает UIView, я хочу позволить ему коснуться его и дать ему «ручки», которые позволяютизменение размера, отличное от «квадратного» изменения, которое дает сжатие (т. е. когда вы используете жест сжатия, вы увеличиваете или уменьшаете размер в одинаковом соотношении для x и y).Теперь, даже с кодом выше, сохраненные значения не всегда точны.
«Ручки», которые я использую, представляют собой просто 10x10 точек, которые должны идти в каждом углу и на полпути вниз по каждой «стороне» рамки / прямоугольника UIView.Когда я впервые ставлю квадрат и нажимаю на него, чтобы получить ручки, прежде чем делать что-либо еще, маркеры появляются в соответствующем месте.Когда я перемещаю, изменяю размеры и поворачиваю объект, затем нажимаю на него, все ручки смещаются от формы на некоторое количество.Обычно он составляет около 20 пикселей.
Являются ли значения в объектах UIGestureRecognizer просто недостаточно точными?Похоже, что это не так, потому что эти значения используются для изменения объекта на экране, и это точно.
Я почти уверен, что у меня нет возможности получить реальное представление офрейм UIView после того, как он так много возился, но где же недостаток в моем пользовательском коде, который дает мне плохие значения?Я теряю точность при преобразовании между градусами и радианами?
На заметке, связанной с этим: у Apple, очевидно, есть внутренний код, который отслеживает, как нарисовать представление, которое было переведено / преобразовано.Рамка сплошного цвета, которую я сейчас использую, перемещается, масштабируется и поворачивается правильно.Итак, есть ли способ получить доступ к значениям, которые они используют для отображения переведенного / преобразованного UIView?
Спасибо!