检测 CGAffineTransformed 视图是不是超出屏幕/UIView 的范围

Posted

技术标签:

【中文标题】检测 CGAffineTransformed 视图是不是超出屏幕/UIView 的范围【英文标题】:Detecting if a CGAffineTransformed view is out of bounds of a screen/UIView检测 CGAffineTransformed 视图是否超出屏幕/UIView 的范围 【发布时间】:2011-04-04 13:03:33 【问题描述】:

我有几个可以拖动、旋转、缩放的视图。我想让它们不会被吸毒、旋转或缩小到屏幕之外。

拖动似乎不是问题,因为我没有使用变换来生成新位置并查看该新位置是否会使视图脱离屏幕。

当我旋转或缩放时,我使用 CGAffineTransform(CGAffineTransformedRotate 或 CGAffineTransformScale),如果不将其实际应用到我的视图中,我似乎无法获得新的框架。

CGRect  newElementBounds = CGRectApplyAffineTransform(element.bounds, CGAffineTransformScale([[gestureRecognizer view] transform], [gestureRecognizer scale], [gestureRecognizer scale]));


CGRect elementBoundsInSuperView = [element convertRect:newElementBounds toView:[element superview]];

elementBoundsInSuperView 不是我所期望的 Rect,它离我很远。

我也试过先在 SuperView 中获取边界,然后对其应用变换,这也不对。

CGRect elementBoundsInSuperView = [element convertRect:element.bounds toView:[element superview]];

CGRect  newElementBounds = CGRectApplyAffineTransform(newElementBounds, CGAffineTransformScale([[gestureRecognizer view] transform], [gestureRecognizer scale], [gestureRecognizer scale]));

[gestureRecognizer view]应该和element一样。

【问题讨论】:

【参考方案1】:

我想出了一些可以工作的手势处理程序,因此您正在操作的视图不会超出您指定的区域。我的视图托盘由 kscreenEditorSpace 定义,2048。

Pan 手势只是调用 calcCenterFromXposition:yPosition:fromBoundsInSuperView: 方法来设置它的中心,如果中心超出边界它只是调整并保持元素在边界内

//--------------------------------------------------------------------------------------------------------
//    handlePanGesture
//    Description: Called when scrollView got a DoubleFinger DoubleTap Gesture
//                 We want to Zoom out one ZOOM_STEP.               
//
//--------------------------------------------------------------------------------------------------------
- (void)handlePanGesture:(UIPanGestureRecognizer *)gestureRecognizer 

    UIView *element = [gestureRecognizer view];


    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ) 
            [[self superview] bringSubviewToFront:self]; 
    

    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) 
            //Front and Center Mr Element! 

            // Find out where we are going
        CGPoint translation = [gestureRecognizer translationInView:[element superview]];
        CGRect elementBoundsInSuperView = [element convertRect:element.bounds toView:[element superview]];
        CGFloat xPosition = CGRectGetMidX(elementBoundsInSuperView) + translation.x;
        CGFloat yPosition = CGRectGetMidY(elementBoundsInSuperView) + translation.y;

        CGPoint newCenter = [self calcCenterFromXposition:xPosition yPosition:yPosition fromBoundsInSuperView:elementBoundsInSuperView];

            //Re position ourselves
        [element setCenter:newCenter];

            //set the translation back to 0 point
        [gestureRecognizer setTranslation:CGPointZero inView:[element superview]];
        [self setNeedsDisplay];
    

    if ([gestureRecognizer state] == UIGestureRecognizerStateEnded ) 
    


所以手柄捏和旋转非常相似。我们不是直接调用计算中心,而是调用另一个方法来帮助确定我们是否在边界内。

//--------------------------------------------------------------------------------------------------------
//    handlePinchGesture
//    Description: Called when scrollView got a DoubleFinger DoubleTap Gesture
//                 We want to Zoom out one ZOOM_STEP.               
//
//--------------------------------------------------------------------------------------------------------

- (void)handlePinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer 
    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ) 

        [[self superview] bringSubviewToFront:self]; 
    

    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) 



        BOOL aSelectedElementOffscreen = FALSE;
            if ([[gestureRecognizer view] pinchOffScreen:[gestureRecognizer scale]]) 
                aSelectedElementOffscreen = TRUE;
            

        if (!aSelectedElementOffscreen) 

            [gestureRecognizer view].transform = CGAffineTransformScale([[gestureRecognizer view] transform], [gestureRecognizer scale], [gestureRecognizer scale]);

                //Update ourself
            [self contentSizeChanged];  
     
             [gestureRecognizer setScale:1];
    

    if ([gestureRecognizer state] == UIGestureRecognizerStateEnded) 

        if (![self becomeFirstResponder]) 
            NSLog(@" %@ - %@ - couldn't become first responder", INTERFACENAME, NSStringFromSelector(_cmd) );
            return;
        
    


捏离屏幕方法

 //--------------------------------------------------------------------------------------------------------
//    pinchOffScreen
//    Description: Called to see if the Pinch Gesture will cause element to go off screen Gesture
//
//--------------------------------------------------------------------------------------------------------

- (BOOL)pinchOffScreen:(CGFloat)scale 

    // Save Our Current Transform incase we go off Screen
CGAffineTransform elementOrigTransform = [self transform];
    //Apply our Transform
self.transform = CGAffineTransformScale([self transform],  scale,  scale);
    // Get our new Bounds in the SuperView
CGRect newElementBoundsInSuperView = [self convertRect:self.bounds toView:[self superview]];

    //Find out where we are in the SuperView 
CGFloat xPosition = CGRectGetMidX( newElementBoundsInSuperView);
CGFloat yPosition = CGRectGetMidY( newElementBoundsInSuperView);

    //See if we are off the Screen
BOOL offScreen = [self calcOffEditorFromXposition:xPosition yPosition:yPosition fromBoundsInSuperView: newElementBoundsInSuperView];

    // We just wanted to Check. Revert to where we were
self.transform = elementOrigTransform;
return offScreen;


Handle Rotation 类似于 Pinch,我们有一个辅助方法来查看我们是否旋转到屏幕外。

//--------------------------------------------------------------------------------------------------------
//    handleRotationGesture
//    Description: Called when we get a rotation gesture
//                 toggle the scroll/zoom lock
//
//--------------------------------------------------------------------------------------------------------

- (void) handleRotationGesture:(UIRotationGestureRecognizer *)gestureRecognizer
    UIView *element = [gestureRecognizer view];

    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ) 
                [[self superview] bringSubviewToFront:self]; 
    

    if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged) 


        BOOL aSelectedElementOffscreen = FALSE;
            if ([element rotateOffScreen:[gestureRecognizer rotation]]) 
                aSelectedElementOffscreen = TRUE;
            

        if (!aSelectedElementOffscreen) 

            [gestureRecognizer view].transform = CGAffineTransformRotate([element transform], [gestureRecognizer rotation]);


                //Update ourself
            [self contentSizeChanged];  

        
        [gestureRecognizer setRotation:0];
    
    if ([gestureRecognizer state] == UIGestureRecognizerStateEnded) 
    


离屏方法

//--------------------------------------------------------------------------------------------------------
//    rotateOffScreen
//    Description: Called to see if the Rotation Gesture will cause element to go off screen Gesture
//
//--------------------------------------------------------------------------------------------------------

- (BOOL)rotateOffScreen:(CGFloat)rotation 

    // Save Our Current Transform incase we go off Screen
CGAffineTransform elementOrigTransform = [self transform];
    //Apply our Transform
self.transform = CGAffineTransformRotate([self transform], rotation);
    // Get our new Bounds in the SuperView
CGRect newElementBoundsInSuperView = [self convertRect:self.bounds toView:[self superview]];

    //Find out where we are in the SuperVire 
CGFloat xPosition = CGRectGetMidX( newElementBoundsInSuperView);
CGFloat yPosition = CGRectGetMidY( newElementBoundsInSuperView);

    //See if we are off the Screen
BOOL offScreen = [self calcOffEditorFromXposition:xPosition yPosition:yPosition fromBoundsInSuperView: newElementBoundsInSuperView];

    // We just wanted to Check. Revert to where we were
self.transform = elementOrigTransform;

return offScreen;


Calc 屏幕定位辅助方法

#pragma mark -
#pragma mark === Calc Screen Positioning  ===
#pragma mark
//--------------------------------------------------------------------------------------------------------
//    calcCenterFromXposition: yPosition: fromBoundsInSuperView:
//    Description: calculate the center point in the element's super view from x, y
//
//--------------------------------------------------------------------------------------------------------
-(CGPoint) calcCenterFromXposition: (CGFloat) xPosition yPosition:(CGFloat) yPosition fromBoundsInSuperView:(CGRect) elementBoundsInSuperView


    // Ge the Height/width based on SuperView Bounds
CGFloat elementWidth = CGRectGetWidth(elementBoundsInSuperView); 
CGFloat elementHeight = CGRectGetHeight(elementBoundsInSuperView);

    //Determine our center.x from the new x
if (xPosition < elementWidth/2) 
    xPosition = elementWidth/2;
 else if (xPosition + elementWidth/2  > kscreenEditorSpace) 
    xPosition = kscreenEditorSpace - elementWidth/2;
   
    //Determine our center.y from the new y
if (yPosition < elementHeight/2) 
    yPosition = elementHeight/2;
 else if (yPosition + elementHeight/2  > kscreenEditorSpace) 
    yPosition = kscreenEditorSpace - elementHeight/2;
 
return (CGPointMake(xPosition, yPosition));


//--------------------------------------------------------------------------------------------------------
//    calcOffEditorFromXposition: yPosition: fromBoundsInSuperView:
//    Description: Determine if moving the element to x, y will it be off the editor screen
//
//--------------------------------------------------------------------------------------------------------
-(BOOL) calcOffEditorFromXposition: (CGFloat) xPosition yPosition:(CGFloat) yPosition fromBoundsInSuperView:(CGRect) elementBoundsInSuperView

BOOL offScreen = NO;

    // Ge the Height/width based on SuperView Bounds
CGFloat elementWidth = CGRectGetWidth(elementBoundsInSuperView); 
CGFloat elementHeight = CGRectGetHeight(elementBoundsInSuperView);

    // Off Screen on Left
if (xPosition < elementWidth/2) 
    offScreen = YES;
 
    //Off Screen Right
if (xPosition + elementWidth/2  > kscreenEditorSpace) 
    offScreen = YES;;


    // Off Screen Top
if (yPosition < elementHeight/2) 
    offScreen = YES;
 

    //Off Screen Bottom
if (yPosition + elementHeight/2  > kscreenEditorSpace) 
    offScreen = YES;


return (offScreen);

【讨论】:

以上是关于检测 CGAffineTransformed 视图是不是超出屏幕/UIView 的范围的主要内容,如果未能解决你的问题,请参考以下文章

在另一个子视图之上检测一个子视图

检测 UIButton 触摸子视图

在配置单元视图上检测到递归视图

子视图中的触摸检测

检测视图是不是重叠

SwiftUI:如何检测两个视图是不是相互交叉?