NSView Rotation
Previously, attempts were made to modify bounds rotation and other things to compensate for rotating an image 90 degrees right or left. It has been found that this approach is much harder than necessary. The correct approach is simple, fast, and more efficient. The correct thing to do is not to rotate the frame or bounds of any object, but to rotate the drawing coordinate system from inside the drawRect method:
- when in the drawRect method, the rect value is considered the “drawing rect”
- find the size of your desired image representation (pixel size, pdf desired size, etc…)
- set the image representation to this size, and set the drawing rect to this size
- note that the drawing rect does not need to compensate for rotation, since it will be affected by the Affine Transform methods below
- set the NSClipView frame to compensate for rotation
- 0 and 180 degree rotations use width and height the same as the image representation
- 90 and 270 degree rotations use swapped width and height from the image representation
- use NSAffineTransform to move and then rotate the drawing coordinate system (see code below)
- get the current graphics state with [NSAffineTransform transform]
- make a copy of the current graphics state so you can restore it later
- make your coordinate and rotation transformations
- apply your transformations with the “concat” method
- use the drawInRect method from the NSImageRep or NSPDFImageRep classes to properly draw a rotated image
- restore the original graphics state
- code example:
if(pdf != nil){ [pdf setCurrentPage:repOffset]; // update the page number field [pageNumbers setStringValue:[NSString stringWithFormat:@"%d/%d", repOffset+1, [pdf pageCount]]]; NSRect pageRect = [pdf bounds]; NSSize actualSize; actualSize.width = pageRect.size.width * scaleFactor; actualSize.height = pageRect.size.height * scaleFactor; // this is the drawing rect, and does not need compensation for rotation // because the coordinate system will be transformed when this is used rect.size = actualSize; // size the frame according to the rotation if(rotation == 90 || rotation == 270){ NSSize rotSize = NSMakeSize(actualSize.height,actualSize.width); [self setFrameSize:rotSize]; }else{ [self setFrameSize:actualSize]; } // initially we were trying to rotate the view, but it was found to work // much better to adjust the drawing coordinate system NSAffineTransform* xform = nil; NSAffineTransform* xformOriginal = nil; // to save/restore the current state if(rotation != 0){ xform = [NSAffineTransform transform]; // get the current state xformOriginal = (NSAffineTransform *)[xform copy]; // copy the current state // first move the origin of drawing to the correct location switch (rotation) { case(90): [xform translateXBy:actualSize.height yBy:0.]; break; case(180): [xform translateXBy:actualSize.width yBy:actualSize.height]; break; case(270): [xform translateXBy:0. yBy:actualSize.width]; break; default: break; } // next rotate the coordinate system by the proper rotation [xform rotateByDegrees:rotation]; // apply the transformation before we draw [xform concat]; } // fix scrolling origin... // the superview of this class is the contentView of the scrollView, which // is of the NSClipView class NSClipView *cv = (NSClipView *)[self superview]; NSRect contentFrame = [cv frame]; rect.origin = contentFrame.origin; // draw a white page (background) [[NSColor whiteColor] set]; [NSBezierPath fillRect:rect]; // draw the pdf representation [pdf drawInRect:rect]; // restore the state of the original coordinate system if(xformOriginal != nil){ [xformOriginal concat]; } }