UIImage с самим закруглением угла

#ios #swift #uiimageview #uiimage #cornerradius

#iOS #swift #uiimageview #uiimage #cornerradius

Вопрос:

Из-за функции автоматической компоновки я попытался использовать UIImage corner radius вместо UIImageView. Радиус угла был слишком маленьким, когда фотография была слишком большой, например, 4k x 4k, но когда фотография была маленькой, например 500 x 500, радиус угла был слишком большим. Независимо от того, какого размера фотография, я хочу, чтобы радиус угла был 25. У вас есть какие-либо предложения?

Я попробовал следующий код из этого, но он не решает мою проблему.: https://newbedev.com/how-to-set-corner-radius-to-uiimage-not-uiimageview-in-ios-swift

Моя цель — сделать радиус угла равным размеру любой фотографии. Я протестировал название изображения «demo» размером 4000 x 4000, радиус угла равен 5, и «demo2» размером 500 x 500, радиус угла равен 50.

Вот мой полный код:

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

Ответ №1:

Если вы используете debug для проверки UIImage возвращаемых данных из вашей withRoundedCorners(...) функции, вы увидите, что оба изображения на самом деле имеют одинаковые закругленные углы.

Проблема в том, что вы используете радиус 25 4k x 4k изображения и радиус 25 500 x 500 изображения, но затем масштабируете их в соответствии с вашим представлением изображения.

Если вы измените режим содержимого вашего ImageView на:

 photoPreview.contentMode = .topLeft
 

изображения не будут масштабироваться, и вы увидите, что получаете закругленные углы того же радиуса.

Итак, вам нужно масштабировать изображение одновременно с обрезкой закругленных углов.

Вот модификация вашего расширения:

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

}
 

и пример контроллера, помещающий два представления изображений в представление стека, чтобы мы могли видеть два изображения разного размера одновременно:

 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

    }
    
}
 

Используя это 4kx4k изображение (исходный источник: https://images .wallpaperscraft.com/image/single/night_city_aerial_view_city_lights_130879_4000x4000.jpg ):

введите описание изображения здесь

и это 500x500 изображение (исходный источник: https://www.digitalphotopix.com/wp-content/uploads/2011/02/blue-lake.jpg )

введите описание изображения здесь

Мы получаем этот вывод:

введите описание изображения здесь