我想将
UIImage
切成一个圆圈,以便我可以将其用作注释。我在这个网站上找到的每个答案都描述了创建一个 UIImageView
,然后修改它并显示它,但你不能将注释的图像设置为 UIImageView
,只能设置为 UIImage
。
我该怎么办?
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
如果需要,请确保导入 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!
}
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)
}
}
基于 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()
您可以使用此代码圈出图像
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
}
}}
对于直接的解决方案来说,所有这些答案都非常复杂。我刚刚复制了 Objective-C 代码并针对 Swift 进行了调整。
self.myImageView?.layer.cornerRadius = (self.myImageView?.frame.size.width)! / 2;
self.myImageView?.clipsToBounds = true
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
}
我设法通过找到 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
}
基于答案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))
}
}
}
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)
完成!
@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)
目标似乎能够从任何图片变成屏幕上的圆形个人资料图片。我从 @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")