0

Supposed that I have a UIImage's object on the UIViewController, and I want to set the image from the Controller. Basically what I want to do is, merging two images together, that the first image is the 5 star with blue color :

enter image description here

and the second image is the 5 star with grey color :

enter image description here

It's intended for rating image. Since the maximum rating is 5, then I have to multiply it by 20 to get 100 point to make the calculation easier. Please see code for more details logic.

So I have this (BM_RatingHelper.swift) :

static func getRatingImageBasedOnRating(rating: CGFloat, width: CGFloat, height: CGFloat) -> UIImage {
    // available maximum rating is 5.0, so we have to multiply it by 20 to achieve 100.0 point
    let ratingImageWidth = ( width / 100.0 ) * ( rating * 20.0 )

    // get active rating image
    let activeRatingImage = BM_ImageHelper.resize(UIImage(named: "StarRatingFullActive")!, targetSize: CGSize(width: width, height: height))
    let activeRatingImageView = UIImageView(frame: CGRectMake(0, 0, ratingImageWidth, height));
    activeRatingImageView.image = BM_ImageHelper.crop(activeRatingImage, x: 0, y: 0, width: ratingImageWidth, height: height);

    // get inactive rating image
    let inactiveRatingImage = BM_ImageHelper.resize(UIImage(named: "StarRatingFullInactive")!, targetSize: CGSize(width: width, height: height))
    let inactiveRatingImageView = UIImageView(frame: CGRectMake(ratingImageWidth, 0, ( 100.0 - ratingImageWidth ), height));
    inactiveRatingImageView.image = BM_ImageHelper.crop(inactiveRatingImage, x: ratingImageWidth, y: 0, width: ( 100.0 - ratingImageWidth ), height: height);

    // combine the images
    let ratingView = UIView.init(frame: CGRect(x: 0, y: 0, width: width, height: height))
    ratingView.backgroundColor = BM_Color.colorForType(BM_ColorType.ColorWhiteTransparent)
    ratingView.addSubview(activeRatingImageView)
    ratingView.addSubview(inactiveRatingImageView)

    return ratingView.capture()
}

The BM_ImageHelper.swift :

import UIKit

class BM_ImageHelper: NSObject {

    // http://stackoverflow.com/questions/158914/cropping-an-uiimage
    static func crop(image: UIImage, x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) -> UIImage {
        let rect = CGRect(x: x, y: y, width: width, height: height)
        let imageRef = CGImageCreateWithImageInRect(image.CGImage, rect)!
        let croppedImage = UIImage(CGImage: imageRef)
        return croppedImage
    }

    // http://iosdevcenters.blogspot.com/2015/12/how-to-resize-image-in-swift-in-ios.html
    static func resize(image: UIImage, targetSize: CGSize) -> UIImage {
        let size = image.size
        let widthRatio  = targetSize.width  / image.size.width
        let heightRatio = targetSize.height / image.size.height

        // Figure out what our orientation is, and use that to form the rectangle
        var newSize: CGSize

        if(widthRatio > heightRatio) {
            newSize = CGSizeMake(size.width * heightRatio, size.height * heightRatio)
        } else {
            newSize = CGSizeMake(size.width * widthRatio,  size.height * widthRatio)
        }

        // This is the rect that we've calculated out and this is what is actually used below
        let rect = CGRectMake(0, 0, newSize.width, newSize.height)

        // Actually do the resizing to the rect using the ImageContext stuff
        UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
        image.drawInRect(rect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage
    }
}

extension UIView {

    // http://stackoverflow.com/a/34895760/897733
    func capture() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.frame.size, self.opaque, UIScreen.mainScreen().scale)
        self.layer.renderInContext(UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image
    }

}

I call that function like (supposed that the image need to filled is ratingImage) :

self.ratingImage.image =
    BM_RatingHelper.getRatingImageBasedOnRating(3.7, width: 100.0, height: 20.0)

The code works perfectly, but the merged image is so low in quality although I have use the high quality image. This is the image for 3.7 rating :

enter image description here

What should I do to merge the images without lose the original quality? Thanks.

mrjimoy_05
  • 3,452
  • 9
  • 58
  • 95

1 Answers1

2

In your BM_ImageHelper.resize method its giving the scale 1.0. It should be the devices's screens scale.

Change it to

UIGraphicsBeginImageContextWithOptions(newSize, false,  UIScreen.mainScreen().scale)

UPDATE

Also change your crop method to address the scale, like

static func crop(image: UIImage, x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) -> UIImage {
    let transform = CGAffineTransformMakeScale(image.scale, image.scale)
    let rect = CGRect(x: x, y: y, width: width, height: height)
    let transformedCropRect = CGRectApplyAffineTransform(rect, transform);
    let imageRef = CGImageCreateWithImageInRect(image.CGImage, transformedCropRect)!
    let croppedImage = UIImage(CGImage: imageRef, scale: image.scale, orientation: image.imageOrientation)

    return croppedImage
}
Johnykutty
  • 12,091
  • 13
  • 59
  • 100