简体   繁体   中英

UIImageView gets awfully stretched when changing device orientation after rotating image

I have subclassed UIImageView to allow me to spin the image, either with the user's finger dragging the image around, or the use of a slider to set the image spinning.

This all works fine when initially used, however if the user spins the image and then re-orientates the device then the image size gets massively screwed up - it appears that if the user spins the image clockwise then the image goes massive, counterclockwise the image goes tiny.

If I set the UIImageView's autoresizingMask to none then the problem goes away, however if I do that then I will need to manually move/re-size the view on device rotation, which I would prefer not to do.

The following code does most of the work:

- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event{
    self.spinInProgress = NO;
    if (!self.spinnable || [[event allTouches] count]>1) {
        [super touchesBegan:touches withEvent:event];
        return;
    }
    UITouch *touch = (UITouch*)[touches anyObject];

    //check that touch is within radius of the circle
    float radius = MIN(self.frame.size.height, self.frame.size.width);
    radius = radius / 2;
    CGPoint centre = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);

    CGPoint posInView = [touch locationInView:self];

    float x = posInView.x;
    float y = posInView.y;
    float centre_x = centre.x;
    float centre_y = centre.y;


    if ((powf(x - centre_x, 2) + powf(y - centre_y, 2)) > powf(radius, 2))
        return; //not within the circle..

    //so now doing the work
    self.spinInProgress = YES;

    //stop the spinning if it is doing so
    if (spinTimer!=nil && [spinTimer isValid]){
        [spinTimer invalidate];
        spinTimer = nil;
    }

    //store the current touch as the previous touch position for now for future calculation when touch moves
    CGPoint previousPosition = [touch locationInView:self.superview];

    CGPoint lowerLeft = self.frame.origin;
    CGSize size = self.frame.size;

    //find the centre of the image (really should cache this!)

    centreX = (size.width/2) + lowerLeft.x;
    centreY = (size.height/2) + lowerLeft.y;

    startX = previousPosition.x;
    startY = previousPosition.y;

    //work out the angle of the current touch
    float startdx = previousPosition.x - centreX;
    float startdy = previousPosition.y - centreY;
    startAngle = atan2(startdx, startdy) *180/M_PI;

    startAngle += self.currentAngle;

    [super touchesBegan:touches withEvent:event];


}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

    if (!self.spinInProgress) {
        [super touchesMoved:touches withEvent:event];
        return;
    }

    UITouch *touch = (UITouch*)[touches anyObject];
    //work out the current position of the touch
    CGPoint currentPosition = [touch locationInView:self.superview];

    //calculate the angle
    float dx = currentPosition.x - centreX;
    float dy = currentPosition.y - centreY;

    float newangle = atan2(dx,dy) * 180/ M_PI;

    float rotateangle = startAngle - newangle;

    //float movedAngle = rotateangle - currentAngle;

    if (rotateangle > self.currentAngle){
        clockSpin = YES;
    } else {
        clockSpin = NO;
    }

    if (rotateangle>360)
        rotateangle = rotateangle-360;

    if (rotateangle<0)
        rotateangle = fabs(rotateangle);

    self.currentAngle = rotateangle;

    lastMove = [event timestamp]; 

    [super touchesMoved:touches withEvent:event];
}

-(void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
    [self stop:self];
    self.spinInProgress = NO;
    [super touchesEnded:touches withEvent:event];

}

-(void)setCurrentAngle:(float)currentAngle
{

    [self willChangeValueForKey:@"currentAngle"];
    __currentAngle = currentAngle;
    [self didChangeValueForKey:@"currentAngle"];

    CGAffineTransform trans = CGAffineTransformMakeRotation(self.currentAngle*DEGREES_TO_RADIANS);

    self.transform = trans;

}

I think the most important bit is setCurrentAngle as that does the work, but I thought I should post the rest to give some context.

Any help you might be able to give would be much appreciated!!

Thanks

Richard

OK, so finally worked it out - it turns out that autolayout and tranforms don't play nicely together (see How do I adjust the anchor point of a CALayer, when Auto Layout is being used? for a good explanation)

Due to the rotation the solution was a little complicated, and involved two steps..

1/ hold the offending UIImageView inside an additional view (I used 'editor> embed in>view' in interface builder) 2/ set the required autoresizing to the enclosing view (not the imageView itself) 3/ set any of the autorisizing mask flags for the UIImageView (these will be ignored but need to be there so that layoutSubviews is called when a rotation is detected)

Override the layoutSubviews method for the sub-classed UIImageView as follows:

- (void)layoutSubviews
{
    [super layoutSubviews];
    self.bounds = self.superview.bounds;

}

The above code simply sets the UIImageView's bounds to that of the enclosing view - it's a bit of a bodge but it appears to work!!

Hope that's helpful to someone!

Richard

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM