简体   繁体   中英

Saving UIImage with rounded corners and border with Swift

I am using imagePickerController to pick a picture form the users library. I need to save the picture within the app with rounded corners and a border. When I save the image, it saves the unaltered image. I'm assuming that I am only altering the view not the image itself. Is there a way to save the picture as what can be seen in the view?

@IBOutlet var imageViewer: UIImageView!

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
    var imagePicked = info[UIImagePickerControllerOriginalImage] as UIImage
    imageViewer.layer.cornerRadius = 10.0
    imageViewer.clipsToBounds = true
    imageViewer.layer.frame = CGRectInset(imageViewer.layer.frame, 20, 20)
    imageViewer.layer.borderColor = UIColor.purpleColor().CGColor
    imageViewer.layer.borderWidth = 2.0
    imageViewer.image = imagePicked

Thank you for your help!

The basic operations you need to perform are:

  • Clip the drawing area to draw your image into without the corners
  • Draw the image
  • Configure the stroke colour etc
  • Then stroke the path used for clipping

    func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) { let borderWidth: CGFloat = 2.0 let imagePicked = info[UIImagePickerControllerOriginalImage] as UIImage UIGraphicsBeginImageContextWithOptions(imageViewer.frame.size, false, 0) let path = UIBezierPath(roundedRect: CGRectInset(imageViewer.bounds, borderWidth / 2, borderWidth / 2), cornerRadius: 10.0) let context = UIGraphicsGetCurrentContext() CGContextSaveGState(context) // Clip the drawing area to the path path.addClip() // Draw the image into the context imagePicked.drawInRect(imageViewer.bounds) CGContextRestoreGState(context) // Configure the stroke UIColor.purpleColor().setStroke() path.lineWidth = borderWidth // Stroke the border path.stroke() roundedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); view.addSubview(UIImageView(image: roundedImage)) picker.dismissViewControllerAnimated(true, completion: nil) }

In the code above I've inset the path by half of the stroke width because the stroke is drawn along the center of the path, which means that one pixel will end up outside the path.

Paul.s's answer is perfect (⬆️), but since it only captures the size of the UIImage relative to the UIImageView , it can reduce image quality. Assuming you want your image to maintains its aspect ratio within the frame and you want the border to lie directly on its edge, you can do this to maintain the size of the full image for saving:

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {

    let imagePicked = info[UIImagePickerControllerOriginalImage] as UIImage

    let borderWidth: CGFloat = 2.0
    let cornerRadius:CGFloat = 10.0

    // Create a multiplier to scale up the corner radius and border
    // width you decided on relative to the imageViewer frame such
    // that the corner radius and border width can be converted to
    // the UIImage's scale.
    let multiplier:CGFloat = imagePicked.size.height/imageViewer.frame.size.height > imagePicked.size.width/imageViewer.frame.size.width ?
       imagePicked.size.height/imageViewer.frame.size.height :
       imagePicked.size.width/imageViewer.frame.size.width

    let borderWidthMultiplied:CGFloat = borderWidth * multiplier
    let cornerRadiusMultiplied:CGFloat = cornerRadius * multiplier

    UIGraphicsBeginImageContextWithOptions(imagePicked.size, false, 0)

    let path = UIBezierPath(roundedRect: CGRectInset(CGRectMake(0, 0, imagePicked.size.width, imagePicked.size.height),
       borderWidthMultiplied / 2, borderWidthMultiplied / 2), cornerRadius: cornerRadiusMultiplied)

    let context = UIGraphicsGetCurrentContext()

    CGContextSaveGState(context)
    // Clip the drawing area to the path
    path.addClip()

    // Draw the image into the context
    imagePicked.drawInRect(CGRectMake(0, 0, imagePicked.size.width, imagePicked.size.height))
    CGContextRestoreGState(context)

    // Configure the stroke
    UIColor.blackColor().setStroke()
    path.lineWidth = borderWidthMultiplied

    // Stroke the border
    path.stroke()

    imageViewer.image = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    picker.dismissViewControllerAnimated(true, completion: nil)


}

Rounded UIImage with border:

extension UIImage {
    
    func roundedWithStroke(width: CGFloat = 3) -> UIImage {
        let imageLayer = CALayer()
        let targetSize = CGSize(width: 29, height: 29)

        imageLayer.frame = CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height)
        imageLayer.contents = cgImage
        imageLayer.masksToBounds = true
        imageLayer.cornerRadius = targetSize.width / 2
        imageLayer.borderWidth = width
        imageLayer.borderColor = UIColor.mainAccent.cgColor
        
        UIGraphicsBeginImageContextWithOptions(targetSize, false, scale)
        imageLayer.render(in: UIGraphicsGetCurrentContext()!)
        let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return roundedImage ?? UIImage()
    }
    
}

You can modify extension method to pass parameters that you need.

What you are currently doing is changing the presentation of the image, without changing the image itself. You'll need to create a bitmap context (see UIGraphicsBeginImageContextWithOptions ); set up a round rect clipping region ( CGContextEOClip ); draw your image into the context; and, finally, get a UIImage from the context ( UIGraphicsGetImageFromCurrentImageContext )

Swift 3:

     func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
            let borderWidth: CGFloat = 2.0
               UIGraphicsBeginImageContextWithOptions(myImageButton.frame.size, false, 0)
                let path = UIBezierPath(roundedRect: myImageButton.bounds.insetBy(dx: borderWidth / 2, dy: borderWidth / 2), cornerRadius: 40.0)
                let context = UIGraphicsGetCurrentContext()
                context!.saveGState()
                path.addClip()
                image.draw(in: myImageButton.bounds)
                UIColor.gray.setStroke()
                path.lineWidth = borderWidth
                path.stroke()
                let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()

                myImageButton.setImage(roundedImage, for: .normal)
}
}

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