将 UIImage 剪切成圆形

问题描述 投票:0回答:13

我想将

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

ios swift uiimage
13个回答
108
投票

Xcode 11 • Swift 5.1 或更高版本

编辑/更新:对于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:"https://i.stack.imgur.com/Xs4RX.jpg")!))!
profilePicture.circleMasked


39
投票

如果需要,请确保导入 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!
    }

10
投票

UI图像扩展:

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)
        }
    }

}

UI按钮:

@IBDesignable class CircularImageButton: UIButton {

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

}

3
投票

基于 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()

2
投票

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

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
    }
}}

2
投票

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

self.myImageView?.layer.cornerRadius = (self.myImageView?.frame.size.width)! / 2;
self.myImageView?.clipsToBounds = true

1
投票

Xcode 8.1、Swift 3.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
}

1
投票

我设法通过找到 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
}

1
投票

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

基于答案Leo Dabus

谢谢,完美运行!但仅适用于 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))
        }
    }
}

0
投票

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 中的课程

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

完成!


0
投票

我正在使用 RoundedImageView 类,面临的问题是,当从图库中浏览图像时,图像不会以圆形或圆形显示。我只需更改 UIImageView/RoundedImageView 的属性 -> 视图 -> 内容模式 -> 宽高比填充see screenshot


0
投票

@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)
  • 更少的代码行
  • 能够添加填充
  • 非可选结果

0
投票

目标似乎能够从任何图片变成屏幕上的圆形个人资料图片。我从 @Leo Dabus 出色的答案开始,但它没有处理方向或缩放,并且在剪切似乎没有必要之前进行 CGImage 和裁剪。所以我用 UIImage.draw() 重写了。它可以按百分比或固定大小进行缩放。

请注意,它将生成的图像比例设置为 3.0,以便在屏幕上的给定尺寸下尽可能保持图像质量,而不管原始图像的比例如何。

import UIKit

@objc
extension UIImage {
    
    var isPortrait:  Bool {return size.height > size.width }
    var isLandscape: Bool {return size.width  > size.height}
    
    func circleMask (breadth: CGFloat) -> UIImage {
        guard breadth > 0, size.width > 0, size.height > 0 else {return UIImage()}
        let scaleUp       = breadth / min(size.width, size.height)
        let breadthSize   = CGSize(width: breadth, height: breadth) 
        let breadthRect   = CGRect(origin: .zero, size: breadthSize)
        let excessPoints  = (scaleUp * abs(size.width-size.height)/2).rounded(.down)
        
        let scaledSize = CGSize(width: size.width*scaleUp, height: size.height * scaleUp)
        let cropRect = CGRect(
            origin: CGPoint(
                x: isLandscape ? -excessPoints : 0,
                y: isPortrait  ? 0             : -excessPoints),
            size: scaledSize)
        
        let format = imageRendererFormat
        format.opaque = false
        format.scale = 3.0
        return UIGraphicsImageRenderer(size: breadthSize, format: format).image { _ in
            UIBezierPath(ovalIn: breadthRect).addClip()
            self.draw(in: cropRect)
        }
    }
    
    func circleMask(scaleUp: CGFloat = 1.0 ) -> UIImage {
        return circleMask(breadth: scaleUp * min(size.width,size.height))
    }
}

调用示例:


func profilePict(at loc: String) -> UIImage? {
    guard let url = URL(string:loc) else { print ("Bad URL: \(loc)"); return nil }
    do {
        let data = try Data(contentsOf: url) 
        let profilePicture = UIImage(data: data)
        if let profilePicture {
            return profilePicture.circleMask(breadth:80)
//          return profilePicture.circleMask(scaleUp:0.5)
        } else {
            print("Invalid data for image at \(loc)")
            return nil
        }
    }
    catch { print("No data at \(url) error: \(error)") }
    return nil
}   
    
    
profilePict(at: "https://github.com/recurser/exif-orientation-examples/blob/219294e144531b0c01247913cb58b6f5531b5081/Landscape_7.jpg?raw=true")
© www.soinside.com 2019 - 2024. All rights reserved.