简体   繁体   中英

UIImage with the corner round itself

Due to the auto layout feature, I tried to use UIImage corner radius instead of UIImageView. The corner radius was too thin when the photo was too large, such as 4k x 4k, but when the photo was small, such as 500 x 500, the corner radius was too large. No matter what size the photo is, I want the corner radius to be 25. Do you have any suggestions?

I tried the following code from this, but it does not solve my problem.: https://newbedev.com/how-to-set-corner-radius-to-uiimage-not-uiimageview-in-ios-swift

My goal is to have the corner radius equal to the size of any photo. I tested the image name "demo" which is 4,000 x 4,000, the corner radius is like 5, and "demo2" which is 500 x 500, the corner radius is like 50.

Here my full code:

class TheCountdownDetails: UIViewController {
    let photoPreview = UIImageView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        photoPreview.translatesAutoresizingMaskIntoConstraints = false
        photoPreview.contentMode = .scaleAspectFit
        view.addSubview(photoPreview)
        photoPreview.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
        photoPreview.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
        photoPreview.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20).isActive = true
        photoPreview.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
    }
    
    override func viewDidLayoutSubviews() {
        photoPreview.image = UIImage(named: "demo")?.withRoundedCorners(radius: 25)
    }
    
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
    }
}


extension UIImage {
    public func withRoundedCorners(radius: CGFloat? = nil) -> UIImage? {
        let maxRadius = min(size.width, size.height) / 2
        let cornerRadius: CGFloat
        if let radius = radius, radius > 0 && radius <= maxRadius {
            cornerRadius = radius
        } else {
            cornerRadius = maxRadius
        }
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        let rect = CGRect(origin: .zero, size: size)
        UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).addClip()
        draw(in: rect)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}

If you use debug to inspect the UIImage returned from your withRoundedCorners(...) func, you'll see that both images do, in fact, have the same rounded corners.

The problem is that you are using a radius of 25 on a 4k x 4k image, and a radius of 25 on a 500 x 500 image, but then scaling them to fit your imageView .

If you change your imageView's content mode to:

photoPreview.contentMode = .topLeft

the images won't scale, and you'll see that you're getting the same radius rounded corners.

So, you need to scale the image at the same time you're clipping the rounded corners.

Here's a modification of your extension:

extension UIImage {

    func withRoundedCorners(radius: CGFloat? = nil, targetSize: CGSize) -> UIImage {
        // First, determine the scale factor that preserves aspect ratio
        let widthRatio = targetSize.width / size.width
        let heightRatio = targetSize.height / size.height
        
        let scaleFactor = min(widthRatio, heightRatio)
        
        // Compute the new image size that preserves aspect ratio
        let scaledImageSize = CGSize(
            width: size.width * scaleFactor,
            height: size.height * scaleFactor
        )
        
        let maxRadius = min(scaledImageSize.width, scaledImageSize.height) / 2
        let cornerRadius: CGFloat
        if let radius = radius, radius > 0 && radius <= maxRadius {
            cornerRadius = radius
        } else {
            cornerRadius = maxRadius
        }

        let newRect: CGRect = CGRect(origin: .zero, size: scaledImageSize)
        
        let renderer = UIGraphicsImageRenderer(size: newRect.size)
        
        let scaledImage = renderer.image { _ in
            UIBezierPath(roundedRect: newRect, cornerRadius: cornerRadius).addClip()
            self.draw(in: newRect)
        }
        
        return scaledImage
    }

}

and an example controller, putting two imageViews in a stack view, so we can see two different size images at the same time:

class TheCountdownDetails: UIViewController {
    
    let photoPreview1 = UIImageView()
    let photoPreview2 = UIImageView()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let stack = UIStackView()
        stack.axis = .vertical
        stack.distribution = .fillEqually
        stack.spacing = 20
        stack.translatesAutoresizingMaskIntoConstraints = false
        
        stack.addArrangedSubview(photoPreview1)
        stack.addArrangedSubview(photoPreview2)
        view.addSubview(stack)
        
        photoPreview1.contentMode = .center
        photoPreview2.contentMode = .center
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            stack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            stack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            stack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            stack.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
        ])

    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        // image views are in a stack view,
        //  so we need to force their layouts
        //  before asking for their frames
        photoPreview1.setNeedsLayout()
        photoPreview1.layoutIfNeeded()
        photoPreview2.setNeedsLayout()
        photoPreview2.layoutIfNeeded()
        
        guard let img1 = UIImage(named: "image4kx4k") else { return }
        guard let img2 = UIImage(named: "image500x500") else { return }
        
        let img1r = img1.withRoundedCorners(radius: 25, targetSize: photoPreview1.frame.size)
        let img2r = img2.withRoundedCorners(radius: 25, targetSize: photoPreview2.frame.size)

        photoPreview1.image = img1r
        photoPreview2.image = img2r

    }
    
}

Using this 4kx4k image (original source: https://images.wallpaperscraft.com/image/single/night_city_aerial_view_city_lights_130879_4000x4000.jpg ):

在此处输入图像描述

and this 500x500 image (original source: https://www.digitalphotopix.com/wp-content/uploads/2011/02/blue-lake.jpg )

在此处输入图像描述

We get this output:

在此处输入图像描述

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