docs:programming:cocoa_and_objective-c:nsview_rotation

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];
    		}
    		
    	}
  • docs/programming/cocoa_and_objective-c/nsview_rotation.txt
  • Last modified: 2008/08/03 00:25
  • by 127.0.0.1