将 UIImage 切割成圆形

Cut a UIImage into a circle

我想把一个 UIImage 剪成一个圆圈,这样我就可以将它用作注释。我在这个网站上找到的每个答案都描述了创建一个 UIImageView,然后修改它并显示它,但是你不能将注释的图像设置为 UIImageView,只能设置 UIImage . 我该怎么办?

确保在需要时导入 QuarzCore。

 func maskRoundedImage(image: UIImage, radius: CGFloat) -> UIImage {
        let imageView: UIImageView = UIImageView(image: image)
        let layer = imageView.layer
        layer.masksToBounds = true
        layer.cornerRadius = radius
        UIGraphicsBeginImageContext(imageView.bounds.size)
        layer.render(in: UIGraphicsGetCurrentContext()!)
        let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return roundedImage!
    }

我通过找到 BezierPath 的用途设法回答了我自己的问题!

if let xyz = UIImage(contentsOfFile: readPath) {
     var Rect: CGRect = CGRectMake(0, 0, xyz.size.width, xyz.size.height)
     var x = UIBezierPath(roundedRect: Rect, cornerRadius: 200).addClip()

     UIGraphicsBeginImageContextWithOptions(xyz.size, false, xyz.scale)
     xyz.drawInRect(Rect)
     var ImageNew = UIGraphicsGetImageFromCurrentImageContext()
     UIGraphicsEndImageContext()
     annotation.image = ImageNew
}

Xcode 11 • Swift 5.1 或更高版本

edit/update:对于iOS10+我们可以使用UIGraphicsImageRenderer。对于较旧的 Swift 语法检查编辑历史。

extension UIImage {
    var isPortrait:  Bool    { size.height > size.width }
    var isLandscape: Bool    { size.width > size.height }
    var breadth:     CGFloat { min(size.width, size.height) }
    var breadthSize: CGSize  { .init(width: breadth, height: breadth) }
    var breadthRect: CGRect  { .init(origin: .zero, size: breadthSize) }
    var circleMasked: UIImage? {
        guard let cgImage = cgImage?
            .cropping(to: .init(origin: .init(x: isLandscape ? ((size.width-size.height)/2).rounded(.down) : 0,
                                              y: isPortrait  ? ((size.height-size.width)/2).rounded(.down) : 0),
                                size: breadthSize)) else { return nil }
        let format = imageRendererFormat
        format.opaque = false
        return UIGraphicsImageRenderer(size: breadthSize, format: format).image { _ in
            UIBezierPath(ovalIn: breadthRect).addClip()
            UIImage(cgImage: cgImage, scale: format.scale, orientation: imageOrientation)
            .draw(in: .init(origin: .zero, size: breadthSize))
        }
    }
}

游乐场测试

let profilePicture = UIImage(data: try! Data(contentsOf: URL(string:"http://i.stack.imgur.com/Xs4RX.jpg")!))!
profilePicture.circleMasked

UIImage 扩展名:

extension UIImage {

    func circularImage(size size: CGSize?) -> UIImage {
        let newSize = size ?? self.size

        let minEdge = min(newSize.height, newSize.width)
        let size = CGSize(width: minEdge, height: minEdge)

        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
        let context = UIGraphicsGetCurrentContext()

        self.drawInRect(CGRect(origin: CGPoint.zero, size: size), blendMode: .Copy, alpha: 1.0)

        CGContextSetBlendMode(context, .Copy)
        CGContextSetFillColorWithColor(context, UIColor.clearColor().CGColor)

        let rectPath = UIBezierPath(rect: CGRect(origin: CGPoint.zero, size: size))
        let circlePath = UIBezierPath(ovalInRect: CGRect(origin: CGPoint.zero, size: size))
        rectPath.appendPath(circlePath)
        rectPath.usesEvenOddFillRule = true
        rectPath.fill()

        let result = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return result
    }

}

用法:

UIImageView:

@IBDesignable class CircularImageView: UIImageView {

    override var image: UIImage? {
        didSet {
            super.image = image?.circularImage(size: nil)
        }
    }

}

UIButton:

@IBDesignable class CircularImageButton: UIButton {

    override func setImage(image: UIImage?, forState state: UIControlState) {
        let circularImage = image?.circularImage(size: nil)
        super.setImage(circularImage, forState: state)
    }

}

Xcode8.1,Swift3.0.1

我的代码将如下所示:

let image = yourImage.resize(CGSize(width: 20, height: 20))?.circled(forRadius: 20)

添加 UIImage 扩展,然后:

func resize(_ size: CGSize) -> UIImage? {
    let rect = CGRect(origin: .zero, size: size)
    return redraw(in: rect)
}

func redraw(in rect: CGRect) -> UIImage? {
    UIGraphicsBeginImageContextWithOptions(rect.size, false, UIScreen.main.scale)

    guard let context = UIGraphicsGetCurrentContext(), let cgImage = cgImage else { return nil }

    let rect = CGRect(origin: .zero, size: size)
    let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: rect.size.height)

    context.concatenate(flipVertical)
    context.draw(cgImage, in: rect)

    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}

func circled(forRadius radius: CGFloat) -> UIImage? {
    let rediusSize = CGSize(width: radius, height: radius)
    let rect = CGRect(origin: .zero, size: size)

    UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)

    guard let context = UIGraphicsGetCurrentContext(), let cgImage = cgImage else { return nil }

    let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: rect.size.height)
    context.concatenate(flipVertical)

    let bezierPath = UIBezierPath(roundedRect: rect, byRoundingCorners: [.allCorners], cornerRadii: rediusSize)
    context.addPath(bezierPath.cgPath)
    context.clip()

    context.drawPath(using: .fillStroke)
    context.draw(cgImage, in: rect)

    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return image
}

您可以使用此代码圈出图片

extension UIImage {
func circleImage(_ cornerRadius: CGFloat, size: CGSize) -> UIImage? {
    let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
    UIGraphicsBeginImageContextWithOptions(size, false, 0)
    if let context = UIGraphicsGetCurrentContext() {
        var path: UIBezierPath
        if size.height == size.width {
            if cornerRadius == size.width/2 {
                path = UIBezierPath(arcCenter: CGPoint(x: size.width/2, y: size.height/2), radius: cornerRadius, startAngle: 0, endAngle: 2.0*CGFloat(Double.pi), clockwise: true)
            }else {
                path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
            }
        }else {
            path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
        }
        context.addPath(path.cgPath)
        context.clip()
        self.draw(in: rect)
        // 从上下文上获取剪裁后的照片
        guard let uncompressedImage = UIGraphicsGetImageFromCurrentImageContext() else {
            UIGraphicsEndImageContext()
            return nil
        }
        // 关闭上下文
        UIGraphicsEndImageContext()
        return uncompressedImage
    }else {
        return nil
    }
}}

swift 3 符合MVC模式 创建外部文件

@IBDesignable
class RoundImage: UIImageView{


@IBInspectable var cornerRadius: CGFloat = 0 {
    didSet{
        self.layer.cornerRadius = cornerRadius
    }
}

// set border width
@IBInspectable var borderWidth: CGFloat = 0 {
    didSet{
        self.layer.borderWidth = borderWidth
    }
}

// set border color

@IBInspectable var borderColor: UIColor = UIColor.clear {
    didSet{
        self.layer.borderColor = borderColor.cgColor
    }
}

override func awakeFromNib() {
    self.clipsToBounds = true
}


}// class

在情节提要的 IB 中调用 class

根据需要设置角半径(如果需要圆,则为宽度的 1/2)

完成!

对于一个直接的解决方案来说,所有这些答案都非常复杂。我刚刚复制了我的 Objective-C 代码并针对 Swift.

进行了调整
self.myImageView?.layer.cornerRadius = (self.myImageView?.frame.size.width)! / 2;
self.myImageView?.clipsToBounds = true

基于 Nikos 的回答:

public extension UIImage {

func roundedImage() -> UIImage {
    let imageView: UIImageView = UIImageView(image: self)
    let layer = imageView.layer
    layer.masksToBounds = true
    layer.cornerRadius = imageView.frame.width / 2
    UIGraphicsBeginImageContext(imageView.bounds.size)
    layer.render(in: UIGraphicsGetCurrentContext()!)
    let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return roundedImage!
    }

}

//Usage

let roundedImage = image.roundedImage()

我正在使用 RoundedImageView class,面临的问题是当从图库中浏览图片时,图片没有以圆形或圆形显示。我只是更改 UIImageView/RoundedImageView -> 视图的属性 - > 内容模式 -> 纵横比填充see screenshot

Swift 5.3,Xcode 12.2,处理所有图像方向

基于回答

谢谢,完美!但仅适用于 imageOrientation .up.down 的图像。对于 .right.left 方向的图像,结果会出现失真。从 iPhone/iPad 原始照片的相机最初我们得到 .right 方向。

下面的代码考虑了 imageOrientation 属性:

extension UIImage {

    func cropToCircle() -> UIImage? {
    
        let isLandscape = size.width > size.height
        let isUpOrDownImageOrientation = [0,1,4,5].contains(imageOrientation.rawValue)
    
        let breadth: CGFloat = min(size.width, size.height)
        let breadthSize = CGSize(width: breadth, height: breadth)
        let breadthRect = CGRect(origin: .zero, size: breadthSize)
    
        let xOriginPoint = CGFloat(isLandscape ?
                                (isUpOrDownImageOrientation ? ((size.width-size.height)/2).rounded(.down) : 0) :
                                (isUpOrDownImageOrientation ? 0 : ((size.height-size.width)/2).rounded(.down)))
        let yOriginPoint = CGFloat(isLandscape ?
                                (isUpOrDownImageOrientation ? 0 : ((size.width-size.height)/2).rounded(.down)) :
                                (isUpOrDownImageOrientation ? ((size.height-size.width)/2).rounded(.down) : 0))
    
        guard let cgImage = cgImage?.cropping(to: CGRect(origin: CGPoint(x: xOriginPoint, y: yOriginPoint),
                                                     size: breadthSize)) else { return nil }
        let format = imageRendererFormat
        format.opaque = false
    
        return UIGraphicsImageRenderer(size: breadthSize, format: format).image {_ in
            UIBezierPath(ovalIn: breadthRect).addClip()
            UIImage(cgImage: cgImage, scale: format.scale, orientation: imageOrientation).draw(in: CGRect(origin: .zero, size: breadthSize))
        }
    }
}

@Leo Dabus 接受的答案很好,但这里有一个更好的方法 ✅

import UIKit

public extension UIImage {
    /// Returns a circle image with diameter, color and optional padding
    class func circle(_ color: UIColor, diameter: CGFloat, padding: CGFloat = .zero) -> UIImage {
        let rectangle = CGSize(width: diameter + padding * 2, height: diameter + padding * 2)
        return UIGraphicsImageRenderer(size: rectangle).image { context in
            let rect = CGRect(x: padding, y: padding, width: diameter + padding, height: diameter + padding)
            color.setFill()
            UIBezierPath(ovalIn: rect).fill()
        }
    }
}

如何使用

let image = UIImage.circle(.black, diameter: 8.0)
  • 更少的代码行
  • 能够添加填充
  • Non-optional 结果