UIPinchGestureRecognizer中的捏放大的最大/最小比例 – iPhone iOS

我怎样才能将UIPinchGestureRecognizer的规模限制在最低和最高水平? 下面的比例属性似乎是相对于最后一个已知的规模(从上一个国家的增量),我不知道如何设置对被缩放的对象的大小/高度的限制。

-(void)scale:(id)sender { [self.view bringSubviewToFront:[(UIPinchGestureRecognizer*)sender view]]; if([(UIPinchGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded) { lastScale = 1.0; return; } CGFloat pinchscale = [(UIPinchGestureRecognizer*)sender scale]; CGFloat scale = 1.0 - (lastScale - pinchscale); CGAffineTransform currentTransform = [(UIPinchGestureRecognizer*)sender view].transform; CGAffineTransform holderTransform = holderView.transform; CGAffineTransform newTransform = CGAffineTransformScale(currentTransform, scale, scale); [[(UIPinchGestureRecognizer*)sender view] setTransform:newTransform]; lastScale = [(UIPinchGestureRecognizer*)sender scale]; 

}

没有办法限制UIPinchGestureRecognizer的缩放比例。 为了限制代码中的高度,你应该可以这样做:

 CGFloat scale = 1.0 - (lastScale - pinchscale); CGRect bounds = [(UIPinchGestureRecognizer*)sender view].bounds; scale = MIN(scale, maximumHeight / CGRectGetHeight(bounds)); scale = MAX(scale, minimumHeight / CGRectGetHeight(bounds)); 

要限制宽度,请在最后两行中将“高度”更改为“宽度”。

以Anomie的答案为出发点,我想到了这个解决scheme。

 - (void)handlePinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer { if([gestureRecognizer state] == UIGestureRecognizerStateBegan) { // Reset the last scale, necessary if there are multiple objects with different scales lastScale = [gestureRecognizer scale]; } if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) { CGFloat currentScale = [[[gestureRecognizer view].layer valueForKeyPath:@"transform.scale"] floatValue]; // Constants to adjust the max/min values of zoom const CGFloat kMaxScale = 2.0; const CGFloat kMinScale = 1.0; CGFloat newScale = 1 - (lastScale - [gestureRecognizer scale]); newScale = MIN(newScale, kMaxScale / currentScale); newScale = MAX(newScale, kMinScale / currentScale); CGAffineTransform transform = CGAffineTransformScale([[gestureRecognizer view] transform], newScale, newScale); [gestureRecognizer view].transform = transform; lastScale = [gestureRecognizer scale]; // Store the previous scale factor for the next pinch gesture call } } 

我从Paul Solt和Anoime的答案中收集了一些信息,并且添加了一个我已经为UIViewController创build的现有类,使得任何UIView都可以拖动,现在可以使用手势和变换使其变为可捏合的。

注意:这使您可拖动/可捏的视图的标签属性变脏。 所以,如果你需要标签的东西,你可以考虑把这个值放在这个技术使用的NSMutableDictionary中。 这是可用的[self dictForView:theView]

在你的项目中实现:

您可以在视图控制器“视图”中的任何子视图可拖动或pinchable(或两者)放置在您的viewDidLoad一行代码(例如:)

 [self makeView:mySubView draggable:YES pinchable:YES minPinchScale:0.75 maxPinchScale:1.0]; 

closuresviewDidUnload(发布游客和字典):

 [self makeView:mySubView draggable:NO pinchable:NO minPinchScale:1.0 maxPinchScale:1.0]; 

DragAndPinchScale.h文件

 #import <UIKit/UIKit.h> @interface UIViewController (DragAndPinchScale) -(void) makeView:(UIView*)aView draggable:(BOOL)draggable pinchable:(BOOL)pinchable minPinchScale:(CGFloat)minPinchScale maxPinchScale:(CGFloat)maxPinchScale; -(NSMutableDictionary *) dictForView:(UIView *)theView; -(NSMutableDictionary *) dictForViewGuestures:(UIGestureRecognizer *)guesture; @end 

DragAndPinchScale.m文件

 #import "DragAndPinchScale.h" @implementation UIViewController (DragAndPinchScale) -(NSMutableDictionary *) dictForView:(UIView *)theView{ NSMutableDictionary *dict = (NSMutableDictionary*) (void*) theView.tag; if (!dict) { dict = [[NSMutableDictionary dictionary ] retain]; theView.tag = (NSInteger) (void *) dict; } return dict; } -(NSMutableDictionary *) dictForViewGuestures:(UIGestureRecognizer *)guesture { return [self dictForView:guesture.view]; } - (IBAction)fingersDidPinchInPinchableView:(UIPinchGestureRecognizer *)fingers { NSMutableDictionary *dict = [self dictForViewGuestures:fingers]; UIView *viewToZoom = fingers.view; CGFloat lastScale; if([fingers state] == UIGestureRecognizerStateBegan) { // Reset the last scale, necessary if there are multiple objects with different scales lastScale = [fingers scale]; } else { lastScale = [[dict objectForKey:@"lastScale"] floatValue]; } if ([fingers state] == UIGestureRecognizerStateBegan || [fingers state] == UIGestureRecognizerStateChanged) { CGFloat currentScale = [[[fingers view].layer valueForKeyPath:@"transform.scale"] floatValue]; // limits to adjust the max/min values of zoom CGFloat maxScale = [[dict objectForKey:@"maxScale"] floatValue]; CGFloat minScale = [[dict objectForKey:@"minScale"] floatValue]; CGFloat newScale = 1 - (lastScale - [fingers scale]); newScale = MIN(newScale, maxScale / currentScale); newScale = MAX(newScale, minScale / currentScale); CGAffineTransform transform = CGAffineTransformScale([[fingers view] transform], newScale, newScale); viewToZoom.transform = transform; lastScale = [fingers scale]; // Store the previous scale factor for the next pinch gesture call } [dict setObject:[NSNumber numberWithFloat:lastScale] forKey:@"lastScale"]; } - (void)fingerDidMoveInDraggableView:(UIPanGestureRecognizer *)finger { NSMutableDictionary *dict = [self dictForViewGuestures:finger]; UIView *viewToDrag = finger.view; if (finger.state == UIGestureRecognizerStateBegan) { [dict setObject:[NSValue valueWithCGPoint:viewToDrag.frame.origin] forKey:@"startDragOffset"]; [dict setObject:[NSValue valueWithCGPoint:[finger locationInView:self.view]] forKey:@"startDragLocation"]; } else if (finger.state == UIGestureRecognizerStateChanged) { NSMutableDictionary *dict = (NSMutableDictionary*) (void*) viewToDrag.tag; CGPoint stopLocation = [finger locationInView:self.view]; CGPoint startDragLocation = [[dict valueForKey:@"startDragLocation"] CGPointValue]; CGPoint startDragOffset = [[dict valueForKey:@"startDragOffset"] CGPointValue]; CGFloat dx = stopLocation.x - startDragLocation.x; CGFloat dy = stopLocation.y - startDragLocation.y; // CGFloat distance = sqrt(dx*dx + dy*dy ); CGRect dragFrame = viewToDrag.frame; CGSize selfViewSize = self.view.frame.size; if (!UIDeviceOrientationIsPortrait(self.interfaceOrientation)) { selfViewSize = CGSizeMake(selfViewSize.height,selfViewSize.width); } selfViewSize.width -= dragFrame.size.width; selfViewSize.height -= dragFrame.size.height; dragFrame.origin.x = MIN(selfViewSize.width, MAX(0,startDragOffset.x+dx)); dragFrame.origin.y = MIN(selfViewSize.height,MAX(0,startDragOffset.y+dy)); viewToDrag.frame = dragFrame; } else if (finger.state == UIGestureRecognizerStateEnded) { [dict removeObjectForKey:@"startDragLocation"]; [dict removeObjectForKey:@"startDragOffset"]; } } -(void) makeView:(UIView*)aView draggable:(BOOL)draggable pinchable:(BOOL)pinchable minPinchScale:(CGFloat)minPinchScale maxPinchScale:(CGFloat)maxPinchScale{ NSMutableDictionary *dict = (NSMutableDictionary*) (void*) aView.tag; if (!(pinchable || draggable)) { if (dict){ [dict release]; aView.tag = 0; } return; } if (dict) { UIPanGestureRecognizer *pan =[dict objectForKey:@"UIPanGestureRecognizer"]; if(pan){ if ([aView.gestureRecognizers indexOfObject:pan]!=NSNotFound) { [aView removeGestureRecognizer:pan]; } [dict removeObjectForKey:@"UIPanGestureRecognizer"]; } UIPinchGestureRecognizer *pinch =[dict objectForKey:@"UIPinchGestureRecognizer"]; if(pinch){ if ([aView.gestureRecognizers indexOfObject:pinch]!=NSNotFound) { [aView removeGestureRecognizer:pinch]; } [dict removeObjectForKey:@"UIPinchGestureRecognizer"]; } [dict removeObjectForKey:@"startDragLocation"]; [dict removeObjectForKey:@"startDragOffset"]; [dict removeObjectForKey:@"lastScale"]; [dict removeObjectForKey:@"minScale"]; [dict removeObjectForKey:@"maxScale"]; } if (draggable) { UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(fingerDidMoveInDraggableView:)]; pan.minimumNumberOfTouches = 1; pan.maximumNumberOfTouches = 1; [aView addGestureRecognizer:pan]; [pan release]; dict = [self dictForViewGuestures:pan]; [dict setObject:pan forKey:@"UIPanGestureRecognizer"]; } if (pinchable) { CGAffineTransform initialTramsform = CGAffineTransformMakeScale(1.0, 1.0); aView.transform = initialTramsform; UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(fingersDidPinchInPinchableView:)]; [aView addGestureRecognizer:pinch]; [pinch release]; dict = [self dictForViewGuestures:pinch]; [dict setObject:pinch forKey:@"UIPinchGestureRecognizer"]; [dict setObject:[NSNumber numberWithFloat:minPinchScale] forKey:@"minScale"]; [dict setObject:[NSNumber numberWithFloat:maxPinchScale] forKey:@"maxScale"]; } } @end 

大多数其他答案的问题是,他们试图将比例作为一个线性值来处理,实际上它是非线性的,因为UIPinchGestureRecognizer根据触摸距离计算其比例属性。 当没有考虑到这一点时,用户必须使用或多或less的夹点距离来“撤消”前一个捏合手势施加的缩放。

考虑:假设transform.scale = 1.0 ,我把我的手指放在屏幕上6厘米,然后向内掐3厘米 – 得到的gestureRecognizer.scalegestureRecognizer.scale-0.5 ,所以transform.scale将变成1.0+(-0.5) = 0.5 。 现在,我举起我的手指,把它们放回距离3厘米,并向外掐6厘米。 得到的gestureRecognizer.scale将是2.0-1.01.0 ,所以transform.scale将变成0.5+1.0 = 1.5 。 不是我想要发生的事情。

解决的办法是计算三angular洲箍缩比例作为其以前的价值比例。 我把我的手指放下6厘米,并向内掐3厘米,所以gestureRecognizer.scale0.50.5/1.00.5 ,所以我的新transform.scale1.0*0.5 = 0.5 。 接下来,我的手指放下3厘米,并向外捏6厘米。 gestureRecognizer.scale然后是gestureRecognizer.scale 2.0/1.02.0 ,所以我新的transform.scale0.5*2.0 = 1.0 ,这正是我想要发生的。

这里是代码:

-(void)viewDidLoad

 self.zoomGestureCurrentZoom = 1.0f; 

in -(void)onZoomGesture:(UIPinchGestureRecognizer*)gestureRecognizer

 if ( gestureRecognizer.state == UIGestureRecognizerStateBegan ) { self.zoomGestureLastScale = gestureRecognizer.scale; } else if ( gestureRecognizer.state == UIGestureRecognizerStateChanged ) { // we have to jump through some hoops to clamp the scale in a way that makes the UX intuitive float scaleDeltaFactor = gestureRecognizer.scale/self.zoomGestureLastScale; float currentZoom = self.zoomGestureCurrentZoom; float newZoom = currentZoom * scaleDeltaFactor; // clamp float kMaxZoom = 4.0f; float kMinZoom = 0.5f; newZoom = MAX(kMinZoom,MIN(newZoom,kMaxZoom)); self.view.transform = CGAffineTransformScale([[gestureRecognizer view] transform], newZoom, newZoom); // store for next time self.zoomGestureCurrentZoom = newZoom; self.zoomGestureLastScale = gestureRecognizer.scale; } 

谢谢,真正有用的代码片段夹紧到最小和最大的规模。

我发现,当我首先使用以下方式翻转视图时:

 CGAffineTransformScale(gestureRecognizer.view.transform, -1.0, 1.0); 

缩放视图时会导致闪烁。

让我知道你的想法,但我的解决scheme是更新上面的代码示例,如果视图已翻转(通过属性设置标志),然后反转比例值:

 if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) { CGFloat currentScale = [[[gestureRecognizer view].layer valueForKeyPath:@"transform.scale"] floatValue]; if(self.isFlipped) // (inverting) { currentScale *= -1; } CGFloat newScale = 1 - (self.lastScale - [gestureRecognizer scale]); newScale = MIN(newScale, self.maximumScaleFactor / currentScale); newScale = MAX(newScale, self.minimumScaleFactor / currentScale); CGAffineTransform transform = CGAffineTransformScale([[gestureRecognizer view] transform], newScale, newScale); gestureRecognizer.view.transform = transform; self.lastScale = [gestureRecognizer scale]; // Store the previous scale factor for the next pinch gesture call 

这里提到的其他方法并不适用于我,但从以前的答案和(在我看来)简化了一些东西,我已经得到了这个为我工作。 effectiveScale是在viewDidLoad设置为1.0的一个ivar。

 -(void)zoomScale:(UIPinchGestureRecognizer *)recognizer { if([recognizer state] == UIGestureRecognizerStateEnded) { // Reset last scale lastScale = 1.0; return; } if ([recognizer state] == UIGestureRecognizerStateBegan || [recognizer state] == UIGestureRecognizerStateChanged) { CGFloat pinchscale = [recognizer scale]; CGFloat scaleDiff = pinchscale - lastScale; if (scaleDiff < 0) scaleDiff *= 2; // speed up zoom-out else scaleDiff *= 0.7; // slow down zoom-in effectiveScale += scaleDiff; // Limit scale between 1 and 2 effectiveScale = effectiveScale < 1 ? 1 : effectiveScale; effectiveScale = effectiveScale > 2 ? 2 : effectiveScale; // Handle transform in separate method using new effectiveScale [self makeAndApplyAffineTransform]; lastScale = pinchscale; } } 
 - (void)handlePinch:(UIPinchGestureRecognizer *)recognizer{ //recognizer.scale=1; CGFloat pinchScale = recognizer.scale; pinchScale = round(pinchScale * 1000) / 1000.0; NSLog(@"%lf",pinchScale); if (pinchScale < 1) { currentLabel.font = [UIFont fontWithName:currentLabel.font.fontName size: (currentLabel.font.pointSize - pinchScale)]; recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale); [currentLabel sizeToFit]; recognizer.scale=1; } else { currentLabel.font = [UIFont fontWithName:currentLabel.font.fontName size:(currentLabel.font.pointSize + pinchScale)]; recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale); [currentLabel sizeToFit]; recognizer.scale=1; } //currentLabel.adjustsFontSizeToFitWidth = YES; // [currentLabel sizeToFit]; NSLog(@"Font :%@",label.font); } 
 - (void)pinchToZoom:(UIPinchGestureRecognizer*)gesture { switch (gesture.state) { case UIGestureRecognizerStateBegan: { lastScale = gesture.scale; }break; case UIGestureRecognizerStateChanged: { const CGFloat zoomSensitivity = 5; const CGFloat zoomMin = 1; const CGFloat zoomMax = 16; CGFloat objectScale = gesture.view.contentScaleFactor; CGFloat zoomDiff = lastScale - gesture.scale; CGFloat zoomDirty = objectScale - zoomDiff * zoomSensivity; CGFloat zoomTo = fmaxf(zoomMin, fminf(zoomDirty, zoomMax)); // step round if needed (neutralize elusive changes) zoomTo = (NSInteger)(zoomTo * 10) * 0.1; if ( objectScale != zoomTo ) gesture.view.contentScaleFactor = zoomTo; lastScale = gesture.scale; }break; default: break; } } 

Paul Solt在Swift 3 的回答

  if(gesture.state == .began) { // Reset the last scale, necessary if there are multiple objects with different scales lastScale = gesture.scale } if (gesture.state == .began || gesture.state == .changed) { let currentScale = gesture.view!.layer.value(forKeyPath:"transform.scale")! as! CGFloat // Constants to adjust the max/min values of zoom let kMaxScale:CGFloat = 2.0 let kMinScale:CGFloat = 1.0 var newScale = 1 - (lastScale - gesture.scale) newScale = min(newScale, kMaxScale / currentScale) newScale = max(newScale, kMinScale / currentScale) let transform = (gesture.view?.transform)!.scaledBy(x: newScale, y: newScale); gesture.view?.transform = transform lastScale = gesture.scale // Store the previous scale factor for the next pinch gesture call } 

你可以使用滚动视图吗? 然后你可以使用scrollView.minimumZoomScale和scrollView.maximumZoomScale