简体   繁体   中英

UIGraphicsGetImageFromCurrentContext and preserve aspect ratio

I'm trying to implement a UIImageView subclass that will allow a user to draw in the image view using their fingers. The contentMode of my UIImageView is set to Aspect Fill . I noticed that when my draw code executes and the resulting image is extracted from the graphics context it is being scaled in a Scale to Fill type format which is not something I want. I was wondering if anyone knows how to achieve extracting this image and maintaining the aspect ratio of the image.

class DrawImageView : UIImageView {

    var lastTouchPoint = CGPoint.zero
    var isSwiping = false

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        isSwiping = false 
        if let touchPoint = touches.first {
            lastTouchPoint = touchPoint.location(in: self)
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        isSwiping = true

        if let touchPoint = touches.first {
            let currentPoint = touchPoint.location(in: self)
            UIGraphicsBeginImageContext(frame.size)

            image?.draw(in: CGRect(x:0, y:0, width:frame.size.width, height:frame.size.height))

            if let context = UIGraphicsGetCurrentContext() {
                context.move(to: lastTouchPoint)
                context.addLine(to: currentPoint)
                context.setLineCap(CGLineCap.round)
                context.setLineWidth(9.0)
                context.setStrokeColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 1.0)
                context.strokePath()
                image = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                lastTouchPoint = currentPoint
            }
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if(!isSwiping) {

            UIGraphicsBeginImageContext(frame.size)
            image?.draw(in: CGRect(x:0, y:0, width:frame.size.width, height:frame.size.height))


            if let context = UIGraphicsGetCurrentContext() {
                context.setLineCap(CGLineCap.round)
                context.setLineWidth(9.0)
                context.setStrokeColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 1.0)
                context.move(to: lastTouchPoint)
                context.addLine(to: lastTouchPoint)
                context.strokePath()
                image = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
            }
        }
    }
}

With this code:

UIGraphicsBeginImageContext(frame.size)
image?.draw(in: CGRect(x:0, y:0, width:frame.size.width, height:frame.size.height))

you are saying "draw this full image - regardless of size or aspect ratio - into a rectangle of width x height dimensions." So, if image has a "native" size of, say, 300 x 200, and your frame is 200 x 200, that's where the image is going to be drawn.

To avoid that, you need to calculate the proper rect to draw into. Using those sizes, you'd want to do (for aspect-fill):

image?.draw(in: CGRect(x:-50, y:0, width:frame.size.width + 50, height:frame.size.height))

Of course, instead of hard-coded values, you'd run quick calculations to determine the actual numbers.

The functions I ended up discovering and using to provide both an aspect fill rect and a aspect fit rect is as follows:

 func getAspectFillFrame(sizeImageView : CGSize, sizeImage: CGSize) -> CGRect {

        let aspect = sizeImage.width / sizeImage.height
        let rect: CGRect

        if sizeImageView.width / aspect > sizeImageView.height {

            let height = sizeImageView.width / aspect

            rect = CGRect(x: 0, y: (sizeImageView.height - height) / 2,
                          width: sizeImageView.width, height: height)

        } else {
            let width = sizeImageView.height * aspect
            rect = CGRect(x: (sizeImageView.width - width) / 2, y: 0,
                          width: width, height: sizeImageView.height)
        }

        return rect

    }

    func getAspectFitFrame(sizeImgView:CGSize, sizeImage:CGSize) -> CGRect {

        let imageSize:CGSize  = sizeImage
        let viewSize:CGSize = sizeImgView

        let hfactor : CGFloat = imageSize.width/viewSize.width
        let vfactor : CGFloat = imageSize.height/viewSize.height

        let factor : CGFloat = max(hfactor, vfactor)

        // Divide the size by the greater of the vertical or horizontal shrinkage factor
        let newWidth : CGFloat = imageSize.width / factor
        let newHeight : CGFloat = imageSize.height / factor

        var x:CGFloat = 0.0
        var y:CGFloat = 0.0
        if newWidth > newHeight{
            y = (sizeImgView.height - newHeight)/2
        }
        if newHeight > newWidth{
            x = (sizeImgView.width - newWidth)/2
        }
        let newRect:CGRect = CGRect(x: x, y: y, width: newWidth, height: newHeight)

        return newRect

    }

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