如何从 UIView 创建特定大小的图像
How to create an image of specific size from UIView
我正在开发 iOS 应用程序,该应用程序应该允许用户创建 Instagram 快拍照片并将它们导出到 Instagram。基本上像 Unfold、Stellar、Chroma Stories 这样的应用程序...我已经准备好 UI,用户可以在其中 select 从准备好的模板中添加自己的带有滤镜、标签等的照片。
我的问题是 - 导出创建的 UIView 到更大的图像的最佳方式是什么?
我的意思是如何获得最佳质量、清晰的标签像素等?
因为带有子视图(添加的照片、标签...)的模板视图占据了设备屏幕的 +- 一半。但我需要更大尺寸的导出图像。
目前我使用:
func makeImageFromView() -> UIImage {
let format = UIGraphicsImageRendererFormat()
let size = CGSize(width: 1080 / format.scale, height: 1920 / format.scale)
let renderer = UIGraphicsImageRenderer(size: size, format: format)
let image = renderer.image { (ctx) in
templateView.drawHierarchy(in: CGRect(origin: .zero, size: size), afterScreenUpdates: true)
}
return image
}
生成的图像大小为 1080 x 1920,但标签不清晰。
在将其制作成图像之前,我是否需要以某种方式缩放照片和字体大小?
谢谢!
所以实际上是的,在捕获图像之前我需要缩放整个视图及其子视图。这是我的发现(可能是显而易见的事情,但我花了一段时间才意识到这一点——我会很高兴有任何改进)
渲染相同尺寸的图像
当你想将UIView截取为图像时,你可以简单地使用这个函数。结果图像将与视图具有相同的大小(根据实际设备缩放 2x / 3x)
func makeImageFrom(_ desiredView: MyView) -> UIImage {
let size = CGSize(width: desiredView.bounds.width, height: desiredView.bounds.height)
let renderer = UIGraphicsImageRenderer(size: size)
let image = renderer.image { (ctx) in
desiredView.drawHierarchy(in: CGRect(origin: .zero, size: size), afterScreenUpdates: true)
}
return image
}
不同尺寸的渲染图像
但是当您想要导出的图像的特定尺寸时该怎么办?
因此,根据我的用例,我想渲染最终尺寸 (1080 x 1920) 的图像,但我想捕获的视图尺寸较小(在我的例子中为 275 x 487)。如果什么都不做就做这样的渲染,画质肯定是有损失的。
如果你想避免这种情况并保留清晰的标签和其他子视图,你需要尝试理想地将视图缩放到所需的大小。在我的例子中,从275 x 487 到 1080 x 1920。
func makeImageFrom(_ desiredView: MyView) -> UIImage {
let format = UIGraphicsImageRendererFormat()
// We need to divide desired size with renderer scale, otherwise you get output size larger @2x or @3x
let size = CGSize(width: 1080 / format.scale, height: 1920 / format.scale)
let renderer = UIGraphicsImageRenderer(size: size, format: format)
let image = renderer.image { (ctx) in
// remake constraints or change size of desiredView to 1080 x 1920
// handle it's subviews (update font size etc.)
// ...
desiredView.drawHierarchy(in: CGRect(origin: .zero, size: size), afterScreenUpdates: true)
// undo the size changes
// ...
}
return image
}
我的做法
但是因为我不想弄乱显示给用户的视图大小,所以我采取了不同的方式并使用了不显示给用户的第二个视图。这意味着就在我想要捕获图像之前,我准备了 "duplicated" 具有相同内容但更大尺寸的视图。我没有将它添加到视图控制器的视图层次结构中,所以它不可见。
重要提示!
您确实需要处理子视图。这意味着,你必须增加字体大小,更新移动子视图的位置(例如它们的中心)等!
这里只是几行来说明这一点:
// 1. Create bigger view
let hdView = MyView()
hdView.frame = CGRect(x: 0, y: 0, width: 1080, height: 1920)
// 2. Load content according to the original view (desiredView)
// set text, images...
// 3. Scale subviews
// Find out what scale we need
let scaleMultiplier: CGFloat = 1080 / desiredView.bounds.width // 1080 / 275 = 3.927 ...
// Scale everything, for examples label's font size
[label1, label2].forEach { [=12=].font = UIFont.systemFont(ofSize: [=12=].font.pointSize * scaleMultiplier, weight: .bold) }
// or subview's center
subview.center = subview.center.applying(.init(scaleX: scaleMultiplier, y: scaleMultiplier))
// 4. Render image from hdView
let hdImage = makeImageFrom(hdView)
与实际使用的质量差异——放大到标签:
我正在开发 iOS 应用程序,该应用程序应该允许用户创建 Instagram 快拍照片并将它们导出到 Instagram。基本上像 Unfold、Stellar、Chroma Stories 这样的应用程序...我已经准备好 UI,用户可以在其中 select 从准备好的模板中添加自己的带有滤镜、标签等的照片。
我的问题是 - 导出创建的 UIView 到更大的图像的最佳方式是什么? 我的意思是如何获得最佳质量、清晰的标签像素等? 因为带有子视图(添加的照片、标签...)的模板视图占据了设备屏幕的 +- 一半。但我需要更大尺寸的导出图像。 目前我使用:
func makeImageFromView() -> UIImage {
let format = UIGraphicsImageRendererFormat()
let size = CGSize(width: 1080 / format.scale, height: 1920 / format.scale)
let renderer = UIGraphicsImageRenderer(size: size, format: format)
let image = renderer.image { (ctx) in
templateView.drawHierarchy(in: CGRect(origin: .zero, size: size), afterScreenUpdates: true)
}
return image
}
生成的图像大小为 1080 x 1920,但标签不清晰。 在将其制作成图像之前,我是否需要以某种方式缩放照片和字体大小? 谢谢!
所以实际上是的,在捕获图像之前我需要缩放整个视图及其子视图。这是我的发现(可能是显而易见的事情,但我花了一段时间才意识到这一点——我会很高兴有任何改进)
渲染相同尺寸的图像
当你想将UIView截取为图像时,你可以简单地使用这个函数。结果图像将与视图具有相同的大小(根据实际设备缩放 2x / 3x)
func makeImageFrom(_ desiredView: MyView) -> UIImage {
let size = CGSize(width: desiredView.bounds.width, height: desiredView.bounds.height)
let renderer = UIGraphicsImageRenderer(size: size)
let image = renderer.image { (ctx) in
desiredView.drawHierarchy(in: CGRect(origin: .zero, size: size), afterScreenUpdates: true)
}
return image
}
不同尺寸的渲染图像
但是当您想要导出的图像的特定尺寸时该怎么办? 因此,根据我的用例,我想渲染最终尺寸 (1080 x 1920) 的图像,但我想捕获的视图尺寸较小(在我的例子中为 275 x 487)。如果什么都不做就做这样的渲染,画质肯定是有损失的。
如果你想避免这种情况并保留清晰的标签和其他子视图,你需要尝试理想地将视图缩放到所需的大小。在我的例子中,从275 x 487 到 1080 x 1920。
func makeImageFrom(_ desiredView: MyView) -> UIImage {
let format = UIGraphicsImageRendererFormat()
// We need to divide desired size with renderer scale, otherwise you get output size larger @2x or @3x
let size = CGSize(width: 1080 / format.scale, height: 1920 / format.scale)
let renderer = UIGraphicsImageRenderer(size: size, format: format)
let image = renderer.image { (ctx) in
// remake constraints or change size of desiredView to 1080 x 1920
// handle it's subviews (update font size etc.)
// ...
desiredView.drawHierarchy(in: CGRect(origin: .zero, size: size), afterScreenUpdates: true)
// undo the size changes
// ...
}
return image
}
我的做法
但是因为我不想弄乱显示给用户的视图大小,所以我采取了不同的方式并使用了不显示给用户的第二个视图。这意味着就在我想要捕获图像之前,我准备了 "duplicated" 具有相同内容但更大尺寸的视图。我没有将它添加到视图控制器的视图层次结构中,所以它不可见。
重要提示!
您确实需要处理子视图。这意味着,你必须增加字体大小,更新移动子视图的位置(例如它们的中心)等! 这里只是几行来说明这一点:
// 1. Create bigger view
let hdView = MyView()
hdView.frame = CGRect(x: 0, y: 0, width: 1080, height: 1920)
// 2. Load content according to the original view (desiredView)
// set text, images...
// 3. Scale subviews
// Find out what scale we need
let scaleMultiplier: CGFloat = 1080 / desiredView.bounds.width // 1080 / 275 = 3.927 ...
// Scale everything, for examples label's font size
[label1, label2].forEach { [=12=].font = UIFont.systemFont(ofSize: [=12=].font.pointSize * scaleMultiplier, weight: .bold) }
// or subview's center
subview.center = subview.center.applying(.init(scaleX: scaleMultiplier, y: scaleMultiplier))
// 4. Render image from hdView
let hdImage = makeImageFrom(hdView)