在ios上使用AspectFill拍摄UIImageView的屏幕截图

在ios上使用AspectFill拍摄UIImageView的屏幕截图,ios,swift,uiimageview,uiimage,avfoundation,Ios,Swift,Uiimageview,Uiimage,Avfoundation,我只是想用contentMode设置为aspectFill来捕获UIImageView的UIImage,但有时它不起作用。我需要它的尺寸始终为375 x 667,可能问题与此相关,但我通过测试无法修复它:/ 以下是使用的代码: 要获取图像,请执行以下操作: extension UIView { func asImage() -> UIImage { let renderer = UIGraphicsImageRenderer(bounds: CGRect(x: 0

我只是想用
contentMode
设置为
aspectFill
来捕获UIImageView的UIImage,但有时它不起作用。我需要它的尺寸始终为375 x 667,可能问题与此相关,但我通过测试无法修复它:/

以下是使用的代码:

要获取图像,请执行以下操作:

extension UIView {

    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: CGRect(x: 0, y: 0, width: 375, height: 667))
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }

}
用法:

//ImageView setup stuff
imgViewForVideo.image = thumbnailImage
imgViewForVideo.contentMode = .scaleAspectFill
imgViewForVideo.isHidden = false
                
let newImage = imgViewForVideo.asImage() //usage
    
UIImageWriteToSavedPhotosAlbum(newImage, self, #selector(media(_:didFinishSavingWithError:contextInfo:)), nil) //saving it to phone for testing
这里有两个例子来说明我的意思:(图像应该是纵横比填充,并填充整个375 x 667屏幕,无论原始UIImage大小…)

正确填写方面和屏幕截图:

这是一个混乱的例子:(注意:左侧的黑色边框不是问题的一部分,这是从我的计算机截屏时出现的错误。但是,它有助于显示屏幕的白色部分…这是我遇到的问题之一…除了图像有时被放大太大以外…)


使用当前的
扩展名
,您的意思是:

“在375 x 667图像中以当前大小渲染视图”

因此,如果您的
imgViewForVideo
80 x 142
(例如以大约相同的纵横比显示“缩略图”),则您可以执行以下操作:

您要做的是:

获取视图当前大小的
UIImage
,并将其缩放为375 x 667

您可以通过将
imgViewForVideo
的帧设置为375 x 667来执行此操作,或者,要按原样使用图像视图,请使用此扩展:

extension UIView {
    
    // this method will work, but uses multiple image scaling operations
    // resulting in loss of image quality
    
    func resizedImage(_ size: CGSize, useScreenScale: Bool? = true) -> UIImage {
        let format = UIGraphicsImageRendererFormat()
        if useScreenScale == false {
            format.scale = 1
        }
        // use bounds of self
        var renderer = UIGraphicsImageRenderer(bounds: bounds, format: format)
        let img = renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
        // use target size
        renderer = UIGraphicsImageRenderer(size: size, format: format)
        return renderer.image { (context) in
            img.draw(in: CGRect(origin: .zero, size: size))
        }
    }
    
}
extension UIImage {
    
    // scales and clips original image
    // optionally preserving aspect ratio
    
    func scaleTo(size targetSize: CGSize, mode: UIView.ContentMode? = .scaleToFill, useScreenScale: Bool? = true) -> UIImage {
        // make sure a valid scale mode was requested
        //  if not, set it to scaleToFill
        var sMode: UIView.ContentMode = mode ?? .scaleToFill
        let validModes: [UIView.ContentMode] = [.scaleToFill, .scaleAspectFit, .scaleAspectFill]
        if !validModes.contains(sMode) {
            print("Invalid contentMode requested - using scaleToFill")
            sMode = .scaleToFill
        }
        
        var scaledImageSize = targetSize
        
        // if scaleToFill, don't maintain aspect ratio
        if mode != .scaleToFill {
            // Determine the scale factor that preserves aspect ratio
            let widthRatio = targetSize.width / size.width
            let heightRatio = targetSize.height / size.height
            
            // scaleAspectFit
            var scaleFactor = min(widthRatio, heightRatio)
            if mode == .scaleAspectFill {
                // scaleAspectFill
                scaleFactor = max(widthRatio, heightRatio)
            }
            
            // Compute the new image size that preserves aspect ratio
            scaledImageSize = CGSize(
                width: size.width * scaleFactor,
                height: size.height * scaleFactor
            )
        }
        
        // UIGraphicsImageRenderer uses screen scale, so...
        //  if targetSize is 100x100
        //      on an iPhone 8, for example, screen scale is 2
        //          renderer will produce a 750 x 1334 image
        //      on an iPhone 11 Pro, for example, screen scale is 3
        //          renderer will produce a 1125 x 2001 image
        //
        // if we want a pixel-exact image, set format.scale = 1
        let format = UIGraphicsImageRendererFormat()
        if useScreenScale == false {
            format.scale = 1
        }
        
        let renderer = UIGraphicsImageRenderer(
            size: targetSize,
            format: format
        )
        var origin = CGPoint.zero
        if mode != .scaleToFill {
            origin.x = (targetSize.width - scaledImageSize.width) * 0.5
            origin.y = (targetSize.height - scaledImageSize.height) * 0.5
        }
        let scaledImage = renderer.image { _ in
            self.draw(in: CGRect(
                origin: origin,
                size: scaledImageSize
            ))
        }
        
        return scaledImage
    }
    
}
并称之为:

    let targetSZ = CGSize(width: 375, height: 667)
    let newImage = imgViewForVideo.resizedImage(targetSZ, useScreenScale: false)
请注意,该方法最终会多次缩放图像,导致质量下降

更好的方法是使用原始的图像并将其缩放到目标大小

看看这个扩展:

extension UIView {
    
    // this method will work, but uses multiple image scaling operations
    // resulting in loss of image quality
    
    func resizedImage(_ size: CGSize, useScreenScale: Bool? = true) -> UIImage {
        let format = UIGraphicsImageRendererFormat()
        if useScreenScale == false {
            format.scale = 1
        }
        // use bounds of self
        var renderer = UIGraphicsImageRenderer(bounds: bounds, format: format)
        let img = renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
        // use target size
        renderer = UIGraphicsImageRenderer(size: size, format: format)
        return renderer.image { (context) in
            img.draw(in: CGRect(origin: .zero, size: size))
        }
    }
    
}
extension UIImage {
    
    // scales and clips original image
    // optionally preserving aspect ratio
    
    func scaleTo(size targetSize: CGSize, mode: UIView.ContentMode? = .scaleToFill, useScreenScale: Bool? = true) -> UIImage {
        // make sure a valid scale mode was requested
        //  if not, set it to scaleToFill
        var sMode: UIView.ContentMode = mode ?? .scaleToFill
        let validModes: [UIView.ContentMode] = [.scaleToFill, .scaleAspectFit, .scaleAspectFill]
        if !validModes.contains(sMode) {
            print("Invalid contentMode requested - using scaleToFill")
            sMode = .scaleToFill
        }
        
        var scaledImageSize = targetSize
        
        // if scaleToFill, don't maintain aspect ratio
        if mode != .scaleToFill {
            // Determine the scale factor that preserves aspect ratio
            let widthRatio = targetSize.width / size.width
            let heightRatio = targetSize.height / size.height
            
            // scaleAspectFit
            var scaleFactor = min(widthRatio, heightRatio)
            if mode == .scaleAspectFill {
                // scaleAspectFill
                scaleFactor = max(widthRatio, heightRatio)
            }
            
            // Compute the new image size that preserves aspect ratio
            scaledImageSize = CGSize(
                width: size.width * scaleFactor,
                height: size.height * scaleFactor
            )
        }
        
        // UIGraphicsImageRenderer uses screen scale, so...
        //  if targetSize is 100x100
        //      on an iPhone 8, for example, screen scale is 2
        //          renderer will produce a 750 x 1334 image
        //      on an iPhone 11 Pro, for example, screen scale is 3
        //          renderer will produce a 1125 x 2001 image
        //
        // if we want a pixel-exact image, set format.scale = 1
        let format = UIGraphicsImageRendererFormat()
        if useScreenScale == false {
            format.scale = 1
        }
        
        let renderer = UIGraphicsImageRenderer(
            size: targetSize,
            format: format
        )
        var origin = CGPoint.zero
        if mode != .scaleToFill {
            origin.x = (targetSize.width - scaledImageSize.width) * 0.5
            origin.y = (targetSize.height - scaledImageSize.height) * 0.5
        }
        let scaledImage = renderer.image { _ in
            self.draw(in: CGRect(
                origin: origin,
                size: scaledImageSize
            ))
        }
        
        return scaledImage
    }
    
}
不要在图像视图上调用“转换为图像”函数,而是直接在图像本身上调用
scaleTo(…)

    // make sure the image view has a valid image to begin with
    guard let img = imgViewForVideo.image else {
        print("imgViewForVideo has no image !!!")
        return
    }
    
    let targetSZ = CGSize(width: 375, height: 667)
    let newImage = img.scaleTo(size: targetSZ, mode: .scaleAspectFill, useScreenScale: false)
下面是一个2400 x 1500图像的示例,在应用程序中以80 x 142
的.scaleAspectFill
图像视图显示,使用
UIView
扩展名保存到375 x 667:

extension UIView {
    
    // this method will work, but uses multiple image scaling operations
    // resulting in loss of image quality
    
    func resizedImage(_ size: CGSize, useScreenScale: Bool? = true) -> UIImage {
        let format = UIGraphicsImageRendererFormat()
        if useScreenScale == false {
            format.scale = 1
        }
        // use bounds of self
        var renderer = UIGraphicsImageRenderer(bounds: bounds, format: format)
        let img = renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
        // use target size
        renderer = UIGraphicsImageRenderer(size: size, format: format)
        return renderer.image { (context) in
            img.draw(in: CGRect(origin: .zero, size: size))
        }
    }
    
}
extension UIImage {
    
    // scales and clips original image
    // optionally preserving aspect ratio
    
    func scaleTo(size targetSize: CGSize, mode: UIView.ContentMode? = .scaleToFill, useScreenScale: Bool? = true) -> UIImage {
        // make sure a valid scale mode was requested
        //  if not, set it to scaleToFill
        var sMode: UIView.ContentMode = mode ?? .scaleToFill
        let validModes: [UIView.ContentMode] = [.scaleToFill, .scaleAspectFit, .scaleAspectFill]
        if !validModes.contains(sMode) {
            print("Invalid contentMode requested - using scaleToFill")
            sMode = .scaleToFill
        }
        
        var scaledImageSize = targetSize
        
        // if scaleToFill, don't maintain aspect ratio
        if mode != .scaleToFill {
            // Determine the scale factor that preserves aspect ratio
            let widthRatio = targetSize.width / size.width
            let heightRatio = targetSize.height / size.height
            
            // scaleAspectFit
            var scaleFactor = min(widthRatio, heightRatio)
            if mode == .scaleAspectFill {
                // scaleAspectFill
                scaleFactor = max(widthRatio, heightRatio)
            }
            
            // Compute the new image size that preserves aspect ratio
            scaledImageSize = CGSize(
                width: size.width * scaleFactor,
                height: size.height * scaleFactor
            )
        }
        
        // UIGraphicsImageRenderer uses screen scale, so...
        //  if targetSize is 100x100
        //      on an iPhone 8, for example, screen scale is 2
        //          renderer will produce a 750 x 1334 image
        //      on an iPhone 11 Pro, for example, screen scale is 3
        //          renderer will produce a 1125 x 2001 image
        //
        // if we want a pixel-exact image, set format.scale = 1
        let format = UIGraphicsImageRendererFormat()
        if useScreenScale == false {
            format.scale = 1
        }
        
        let renderer = UIGraphicsImageRenderer(
            size: targetSize,
            format: format
        )
        var origin = CGPoint.zero
        if mode != .scaleToFill {
            origin.x = (targetSize.width - scaledImageSize.width) * 0.5
            origin.y = (targetSize.height - scaledImageSize.height) * 0.5
        }
        let scaledImage = renderer.image { _ in
            self.draw(in: CGRect(
                origin: origin,
                size: scaledImageSize
            ))
        }
        
        return scaledImage
    }
    
}

这是相同的示例2400 x 1500图像,在应用程序中以80 x 142
.scaleAspectFill
图像视图显示,使用
UIImage
扩展名保存到375 x 667:

extension UIView {
    
    // this method will work, but uses multiple image scaling operations
    // resulting in loss of image quality
    
    func resizedImage(_ size: CGSize, useScreenScale: Bool? = true) -> UIImage {
        let format = UIGraphicsImageRendererFormat()
        if useScreenScale == false {
            format.scale = 1
        }
        // use bounds of self
        var renderer = UIGraphicsImageRenderer(bounds: bounds, format: format)
        let img = renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
        // use target size
        renderer = UIGraphicsImageRenderer(size: size, format: format)
        return renderer.image { (context) in
            img.draw(in: CGRect(origin: .zero, size: size))
        }
    }
    
}
extension UIImage {
    
    // scales and clips original image
    // optionally preserving aspect ratio
    
    func scaleTo(size targetSize: CGSize, mode: UIView.ContentMode? = .scaleToFill, useScreenScale: Bool? = true) -> UIImage {
        // make sure a valid scale mode was requested
        //  if not, set it to scaleToFill
        var sMode: UIView.ContentMode = mode ?? .scaleToFill
        let validModes: [UIView.ContentMode] = [.scaleToFill, .scaleAspectFit, .scaleAspectFill]
        if !validModes.contains(sMode) {
            print("Invalid contentMode requested - using scaleToFill")
            sMode = .scaleToFill
        }
        
        var scaledImageSize = targetSize
        
        // if scaleToFill, don't maintain aspect ratio
        if mode != .scaleToFill {
            // Determine the scale factor that preserves aspect ratio
            let widthRatio = targetSize.width / size.width
            let heightRatio = targetSize.height / size.height
            
            // scaleAspectFit
            var scaleFactor = min(widthRatio, heightRatio)
            if mode == .scaleAspectFill {
                // scaleAspectFill
                scaleFactor = max(widthRatio, heightRatio)
            }
            
            // Compute the new image size that preserves aspect ratio
            scaledImageSize = CGSize(
                width: size.width * scaleFactor,
                height: size.height * scaleFactor
            )
        }
        
        // UIGraphicsImageRenderer uses screen scale, so...
        //  if targetSize is 100x100
        //      on an iPhone 8, for example, screen scale is 2
        //          renderer will produce a 750 x 1334 image
        //      on an iPhone 11 Pro, for example, screen scale is 3
        //          renderer will produce a 1125 x 2001 image
        //
        // if we want a pixel-exact image, set format.scale = 1
        let format = UIGraphicsImageRendererFormat()
        if useScreenScale == false {
            format.scale = 1
        }
        
        let renderer = UIGraphicsImageRenderer(
            size: targetSize,
            format: format
        )
        var origin = CGPoint.zero
        if mode != .scaleToFill {
            origin.x = (targetSize.width - scaledImageSize.width) * 0.5
            origin.y = (targetSize.height - scaledImageSize.height) * 0.5
        }
        let scaledImage = renderer.image { _ in
            self.draw(in: CGRect(
                origin: origin,
                size: scaledImageSize
            ))
        }
        
        return scaledImage
    }
    
}

他们使用了2400 x 1500的原始图像:


我在这里放了一个示例应用程序(我用来生成这些图像):

抱歉,我看到这个太晚了;但是效果很好!!:)