Search code examples
swiftcocoaswift3transformcgaffinetransform

Flip NSButton's image upside down


I have a very simply macOS app (written in Swift using Xcode 8.2.1). In my main UI there is an NSButton with a custom image (it represents a playing card - like in Poker). When I click that button I'd like its image to be drawn rotated by 180 degrees (flipped upside down).

I'm new to affine transforms but I thought this might work (it doesn't).

@IBAction func buttonClicked(_ sender: NSButton) {
  var transform = sender.layer?.affineTransform()
  transform = transform?.rotated(by:  180.0 * (CGFloat.pi / 180))
  sender.layer?.setAffineTransform(transform!)
}

The card is rotated properly but it is drawn in a new location.

What is the right way to rotate a button's image by 180 degrees while keeping its position in its parent static?


Solution

  • To rotate NSImage or NSButton image I wrote an extension for NSImage using Swift 3.

    You can pass to the function:

    • Rotation degree let's say 180 degrees.
    • The desired image to rotate.

    Here is the way to call it:

    @IBAction func button(_ sender: NSButton) {
    
         sender.image = NSImage().imageRotatedByDegrees(rotationDegree: 180,
    forImage: sender.image!)
    
    }
    

    And your extension:

    extension NSImage {
    
         func imageRotatedByDegrees(rotationDegree degrees:CGFloat,forImage
    image:NSImage) -> NSImage {
    
             // calculate the bounds for the rotated image
             var imageBounds = NSRect(origin: NSZeroPoint, size: image.size)
    
             let boundsPath : NSBezierPath = NSBezierPath(rect: imageBounds)
    
             var transform : NSAffineTransform = NSAffineTransform()
    
             transform.rotate(byDegrees: degrees)
             boundsPath.transform(using: transform as AffineTransform)
    
             let rotatedBounds : NSRect = NSRect(origin: NSZeroPoint, size:
    boundsPath.bounds.size)
    
             let rotatedImage = NSImage(size: rotatedBounds.size)
    
             // center the image within the rotated bounds
    
             imageBounds.origin.x = NSMidX(rotatedBounds) - (NSWidth
    (imageBounds) / 2); imageBounds.origin.y = NSMidY(rotatedBounds) -
    (NSHeight (imageBounds) / 2)
    
             // set up the rotation transform
             transform = NSAffineTransform()
    
             transform.translateX(by: +(NSWidth(rotatedBounds) / 2), yBy:
    +(NSHeight(rotatedBounds) / 2))
    
             transform.rotate(byDegrees: degrees)
    
             transform.translateX(by: -(NSWidth(rotatedBounds) / 2), yBy:
    -(NSHeight(rotatedBounds) / 2))
    
             // draw the original image, rotated, into the new image
             rotatedImage.lockFocus()
             transform.concat()
    
             image.draw(in: imageBounds, from: NSZeroRect, operation:
    NSCompositeCopy, fraction: 1.0)
    
             rotatedImage.unlockFocus()
    
             return rotatedImage
    
         }
    
    }