Разрежьте UIImage в круг
Я хочу сократить UIImage
в круг, чтобы я мог использовать его в качестве аннотации. Каждый ответ на этом сайте описывает создание UIImageView
, затем изменив и отобразив его, но вы не можете установить изображение аннотации в UIImageView
только UIImage
, Как я должен идти об этом?
13 ответов
Обязательно импортируйте 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!
}
Xcode 8.2 • Swift 3.0.2
extension UIImage {
var isPortrait: Bool { return size.height > size.width }
var isLandscape: Bool { return size.width > size.height }
var breadth: CGFloat { return min(size.width, size.height) }
var breadthSize: CGSize { return CGSize(width: breadth, height: breadth) }
var breadthRect: CGRect { return CGRect(origin: .zero, size: breadthSize) }
var circleMasked: UIImage? {
UIGraphicsBeginImageContextWithOptions(breadthSize, false, scale)
defer { UIGraphicsEndImageContext() }
guard let cgImage = cgImage?.cropping(to: CGRect(origin: CGPoint(x: isLandscape ? floor((size.width - size.height) / 2) : 0, y: isPortrait ? floor((size.height - size.width) / 2) : 0), size: breadthSize)) else { return nil }
UIBezierPath(ovalIn: breadthRect).addClip()
UIImage(cgImage: cgImage, scale: 1, orientation: imageOrientation).draw(in: breadthRect)
return UIGraphicsGetImageFromCurrentImageContext()
}
}
Тестирование детской площадки
let profilePicture = UIImage(data: try! Data(contentsOf: URL(string:"https://stackru.com/images/ef9dfaef8c3bec7e67a69584eb37fe5a3e115132.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)
}
}
На основании ответа Никоса:
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
Swift 5.3, Xcode 12.2, обрабатывает все изображения
На основании ответа Лео Дабус
Спасибо, отлично работает! НО только для изображений с imageOrientation .up или .down. Для изображений с ориентацией вправо или влево возникают искажения. А с камеры iPhone/iPad для исходных фотографий изначально получаем правильную ориентацию.
В приведенном ниже коде учитывается свойство 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))
}
}
}
Мне удалось ответить на мой собственный вопрос, найдя применение 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 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
}
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
колл класс в ИБ на раскадровке
установите угол, как вам угодно (1/2 ширины, если хотите круг)
Готово!
Кажется, цель состоит в том, чтобы перейти от любого изображения к круговым изображениям профиля на экране. Я начал с отличного ответа @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")
Я использую класс RoundedImageView, проблема заключается в том, что при просмотре изображения из галереи изображение не отображается по кругу или кругу.. Я просто меняю свойства UIImageView/RoundedImageView -> view -> Content Mode -> Aspect Fillсм. Снимок экрана
Принятый ответ @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)
- Меньше строк кода
- Возможность добавления отступов
- Необязательный результат