在 ios 上使用 AspectFill 截取 UIImageView 的屏幕截图
Take screenshot of UIImageView with AspectFill on ios
我只是想捕获 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
下面是我的意思的两个示例:(无论原始 UIImage 大小如何,图像都应该是纵横比填充并填充整个 375 x 667 屏幕...)
正确的纵横比填充和截图:
这是一个混乱的例子:(注意:左边的黑色边框不是我电脑截图错误的问题的一部分。但是它有助于显示屏幕的白色部分...这是我遇到的问题之一...除了有时图像放大得太大..)
以你目前的 extension
,你说:
“在 375 x 667 图像中以 当前大小 渲染视图”
因此,如果您的 imgViewForVideo
是 80 x 142
(例如以大致相同的宽高比显示“缩略图”),您就是这样做的:
您想做的是:
获取 UIImage
视图的 当前大小 并 将其缩放 至 375 x 667
您可以通过将 imgViewForVideo
的框架设置为 375 x 667 来做到这一点,或者,要使用图像视图 as-is,请使用此扩展名:
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))
}
}
}
并调用它:
let targetSZ = CGSize(width: 375, height: 667)
let newImage = imgViewForVideo.resizedImage(targetSZ, useScreenScale: false)
请注意,该方法最终会多次缩放图像,从而导致质量下降。
更好的方法是使用原始 图像 并将其缩放和裁剪到您的目标大小。
看看这个扩展:
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
图像视图中显示 in-app,使用 UIView
扩展保存为 375 x 667:
这是同一个 2400 x 1500 图像示例,在 80 x 142 .scaleAspectFill
图像视图中显示 in-app,使用 UIImage
扩展名保存为 375 x 667:
这些使用了原始的 2400 x 1500 图片:
我在这里放了一个示例应用程序(我用来生成这些图像):https://github.com/DonMag/ImageSaveExample
我只是想捕获 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
下面是我的意思的两个示例:(无论原始 UIImage 大小如何,图像都应该是纵横比填充并填充整个 375 x 667 屏幕...)
正确的纵横比填充和截图:
这是一个混乱的例子:(注意:左边的黑色边框不是我电脑截图错误的问题的一部分。但是它有助于显示屏幕的白色部分...这是我遇到的问题之一...除了有时图像放大得太大..)
以你目前的 extension
,你说:
“在 375 x 667 图像中以 当前大小 渲染视图”
因此,如果您的 imgViewForVideo
是 80 x 142
(例如以大致相同的宽高比显示“缩略图”),您就是这样做的:
您想做的是:
获取 UIImage
视图的 当前大小 并 将其缩放 至 375 x 667
您可以通过将 imgViewForVideo
的框架设置为 375 x 667 来做到这一点,或者,要使用图像视图 as-is,请使用此扩展名:
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))
}
}
}
并调用它:
let targetSZ = CGSize(width: 375, height: 667)
let newImage = imgViewForVideo.resizedImage(targetSZ, useScreenScale: false)
请注意,该方法最终会多次缩放图像,从而导致质量下降。
更好的方法是使用原始 图像 并将其缩放和裁剪到您的目标大小。
看看这个扩展:
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
图像视图中显示 in-app,使用 UIView
扩展保存为 375 x 667:
这是同一个 2400 x 1500 图像示例,在 80 x 142 .scaleAspectFill
图像视图中显示 in-app,使用 UIImage
扩展名保存为 375 x 667:
这些使用了原始的 2400 x 1500 图片:
我在这里放了一个示例应用程序(我用来生成这些图像):https://github.com/DonMag/ImageSaveExample